Upstream version 11.39.264.0
[platform/framework/web/crosswalk.git] / src / xwalk / extensions / browser / xwalk_extension_process_host.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/browser/xwalk_extension_process_host.h"
6
7 #include <string>
8
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/files/file_path.h"
12 #include "content/public/browser/browser_child_process_host.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/common/child_process_host.h"
16 #include "content/public/common/process_type.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/public/common/sandboxed_process_launcher_delegate.h"
19 #include "ipc/ipc_message.h"
20 #include "ipc/ipc_switches.h"
21 #include "ipc/message_filter.h"
22 #include "xwalk/extensions/common/xwalk_extension_messages.h"
23 #include "xwalk/extensions/common/xwalk_extension_switches.h"
24 #include "xwalk/runtime/browser/xwalk_runner.h"
25 #include "xwalk/runtime/common/xwalk_switches.h"
26
27 using content::BrowserThread;
28
29 namespace xwalk {
30 namespace extensions {
31
32 // This filter is used by ExtensionProcessHost to intercept when Render Process
33 // ask for the Extension Channel handle (that is created by extension process).
34 class XWalkExtensionProcessHost::RenderProcessMessageFilter
35     : public IPC::MessageFilter {
36  public:
37   explicit RenderProcessMessageFilter(XWalkExtensionProcessHost* eph)
38       : eph_(eph) {}
39
40   // This exists to fulfill the requirement for delayed reply handling, since it
41   // needs to send a message back if the parameters couldn't be correctly read
42   // from the original message received. See DispatchDealyReplyWithSendParams().
43   bool Send(IPC::Message* message) {
44     if (eph_)
45       return eph_->render_process_host_->Send(message);
46     delete message;
47     return false;
48   }
49
50   void Invalidate() {
51     eph_ = NULL;
52   }
53
54  private:
55   // IPC::ChannelProxy::MessageFilter implementation.
56   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
57     bool handled = true;
58     IPC_BEGIN_MESSAGE_MAP(RenderProcessMessageFilter, message)
59       IPC_MESSAGE_HANDLER_DELAY_REPLY(
60           XWalkExtensionProcessHostMsg_GetExtensionProcessChannel,
61           OnGetExtensionProcessChannel)
62       IPC_MESSAGE_UNHANDLED(handled = false)
63     IPC_END_MESSAGE_MAP()
64     return handled;
65   }
66
67   void OnGetExtensionProcessChannel(IPC::Message* reply) {
68     scoped_ptr<IPC::Message> scoped_reply(reply);
69     if (eph_)
70       eph_->OnGetExtensionProcessChannel(scoped_reply.Pass());
71   }
72
73   virtual ~RenderProcessMessageFilter() {}
74
75   XWalkExtensionProcessHost* eph_;
76 };
77
78 class ExtensionSandboxedProcessLauncherDelegate
79     : public content::SandboxedProcessLauncherDelegate {
80  public:
81   explicit ExtensionSandboxedProcessLauncherDelegate(
82       content::ChildProcessHost* host)
83 #if defined(OS_POSIX)
84       : ipc_fd_(host->TakeClientFileDescriptor())
85 #endif
86   {}
87   virtual ~ExtensionSandboxedProcessLauncherDelegate() {}
88
89 #if defined(OS_WIN)
90   virtual bool ShouldSandbox() OVERRIDE {
91     return false;
92   }
93 #elif defined(OS_POSIX)
94   virtual int GetIpcFd() OVERRIDE {
95     return ipc_fd_;
96   }
97 #endif
98
99  private:
100 #if defined(OS_POSIX)
101   int ipc_fd_;
102 #endif
103
104   DISALLOW_COPY_AND_ASSIGN(ExtensionSandboxedProcessLauncherDelegate);
105 };
106
107 bool XWalkExtensionProcessHost::Delegate::OnRegisterPermissions(
108     int render_process_id,
109     const std::string& extension_name,
110     const std::string& perm_table) {
111   return false;
112 }
113
114 XWalkExtensionProcessHost::XWalkExtensionProcessHost(
115     content::RenderProcessHost* render_process_host,
116     const base::FilePath& external_extensions_path,
117     XWalkExtensionProcessHost::Delegate* delegate,
118     scoped_ptr<base::ValueMap> runtime_variables)
119     : ep_rp_channel_handle_(""),
120       render_process_host_(render_process_host),
121       render_process_message_filter_(new RenderProcessMessageFilter(this)),
122       external_extensions_path_(external_extensions_path),
123       is_extension_process_channel_ready_(false),
124       delegate_(delegate),
125       runtime_variables_(runtime_variables.Pass()) {
126   render_process_host_->GetChannel()->AddFilter(
127       render_process_message_filter_.get());
128   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
129       base::Bind(&XWalkExtensionProcessHost::StartProcess,
130       base::Unretained(this)));
131 }
132
133 XWalkExtensionProcessHost::~XWalkExtensionProcessHost() {
134   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
135   render_process_message_filter_->Invalidate();
136   StopProcess();
137 }
138
139 namespace {
140
141 void ToListValue(base::ValueMap* vm, base::ListValue* lv) {
142   lv->Clear();
143
144   for (base::ValueMap::iterator it = vm->begin(); it != vm->end(); it++) {
145     base::DictionaryValue* dv = new base::DictionaryValue();
146     dv->Set(it->first, it->second);
147     lv->Append(dv);
148   }
149 }
150
151 }  // namespace
152
153 void XWalkExtensionProcessHost::StartProcess() {
154   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
155   CHECK(!process_ || !channel_);
156
157   if (XWalkRunner::GetInstance()->shared_process_mode_enabled()) {
158 #if defined(OS_LINUX)
159     std::string channel_id =
160         IPC::Channel::GenerateVerifiedChannelID(std::string());
161     channel_ = IPC::Channel::CreateServer(channel_id, this);
162     if (!channel_->Connect())
163       NOTREACHED();
164     IPC::ChannelHandle channel_handle(channel_id,
165         base::FileDescriptor(channel_->TakeClientFileDescriptor(), true));
166     BrowserThread::PostTask(
167         BrowserThread::UI, FROM_HERE,
168         base::Bind(
169             &XWalkExtensionProcessHost::Delegate::OnExtensionProcessCreated,
170             base::Unretained(delegate_), render_process_host_->GetID(),
171             channel_handle));
172 #else
173     NOTIMPLEMENTED();
174 #endif  // #if defined(OS_LINUX)
175   } else {
176     process_.reset(content::BrowserChildProcessHost::Create(
177         content::PROCESS_TYPE_CONTENT_END, this));
178
179     std::string channel_id = process_->GetHost()->CreateChannel();
180     CHECK(!channel_id.empty());
181
182     CommandLine::StringType extension_cmd_prefix;
183 #if defined(OS_POSIX)
184     const CommandLine &browser_command_line = *CommandLine::ForCurrentProcess();
185     extension_cmd_prefix = browser_command_line.GetSwitchValueNative(
186         switches::kXWalkExtensionCmdPrefix);
187 #endif
188
189 #if defined(OS_LINUX)
190     int flags = extension_cmd_prefix.empty() ?
191         content::ChildProcessHost::CHILD_ALLOW_SELF :
192         content::ChildProcessHost::CHILD_NORMAL;
193 #else
194     int flags = content::ChildProcessHost::CHILD_NORMAL;
195 #endif
196
197     base::FilePath exe_path = content::ChildProcessHost::GetChildPath(flags);
198     if (exe_path.empty())
199       return;
200
201     scoped_ptr<CommandLine> cmd_line(new CommandLine(exe_path));
202     cmd_line->AppendSwitchASCII(switches::kProcessType,
203                                 switches::kXWalkExtensionProcess);
204     cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
205     if (!extension_cmd_prefix.empty())
206       cmd_line->PrependWrapper(extension_cmd_prefix);
207
208     process_->Launch(
209         new ExtensionSandboxedProcessLauncherDelegate(process_->GetHost()),
210         cmd_line.release());
211   }
212
213   base::ListValue runtime_variables_lv;
214   ToListValue(&const_cast<base::ValueMap&>(*runtime_variables_),
215       &runtime_variables_lv);
216   Send(new XWalkExtensionProcessMsg_RegisterExtensions(
217         external_extensions_path_, runtime_variables_lv));
218 }
219
220 void XWalkExtensionProcessHost::StopProcess() {
221   if (process_)
222     process_.reset();
223   if (channel_)
224     channel_.reset();
225 }
226
227 void XWalkExtensionProcessHost::OnGetExtensionProcessChannel(
228     scoped_ptr<IPC::Message> reply) {
229   pending_reply_for_render_process_ = reply.Pass();
230   ReplyChannelHandleToRenderProcess();
231 }
232
233 bool XWalkExtensionProcessHost::OnMessageReceived(const IPC::Message& message) {
234   bool handled = true;
235   IPC_BEGIN_MESSAGE_MAP(XWalkExtensionProcessHost, message)
236     IPC_MESSAGE_HANDLER(
237         XWalkExtensionProcessHostMsg_RenderProcessChannelCreated,
238         OnRenderChannelCreated)
239     IPC_MESSAGE_HANDLER_DELAY_REPLY(
240         XWalkExtensionProcessHostMsg_CheckAPIAccessControl,
241         OnCheckAPIAccessControl)
242     IPC_MESSAGE_HANDLER(
243         XWalkExtensionProcessHostMsg_RegisterPermissions,
244         OnRegisterPermissions)
245     IPC_MESSAGE_UNHANDLED(handled = false)
246   IPC_END_MESSAGE_MAP()
247   return handled;
248 }
249
250 void XWalkExtensionProcessHost::OnChannelError() {
251   // This function is called just before
252   // BrowserChildProcessHostImpl::OnChildDisconnected gets called. Please refer
253   // to ChildProcessHostImpl::OnChannelError() from child_process_host_impl.cc.
254   // This means that content::BrowserChildProcessHost (process_, in our case)
255   // is about to delete its delegate, which is us!
256   // We should alert our XWalkExtensionProcessHost::Delegate, since it will
257   // most likely have a pointer to us that needs to be invalidated.
258
259   VLOG(1) << "\n\nExtensionProcess crashed";
260   if (delegate_)
261     delegate_->OnExtensionProcessDied(this, render_process_host_->GetID());
262 }
263
264 void XWalkExtensionProcessHost::OnProcessLaunched() {
265   VLOG(1) << "\n\nExtensionProcess was started!";
266 }
267
268 void XWalkExtensionProcessHost::OnRenderChannelCreated(
269     const IPC::ChannelHandle& handle) {
270   is_extension_process_channel_ready_ = true;
271   ep_rp_channel_handle_ = handle;
272   ReplyChannelHandleToRenderProcess();
273   if (delegate_)
274     delegate_->OnRenderChannelCreated(render_process_host_->GetID());
275 }
276
277 void XWalkExtensionProcessHost::ReplyChannelHandleToRenderProcess() {
278   // Replying the channel handle to RP depends on two events:
279   // - EP already notified EPH that new channel was created (for RP<->EP).
280   // - RP already asked for the channel handle.
281   //
282   // The order for this events is not determined, so we call this function from
283   // both, and the second execution will send the reply.
284   if (!is_extension_process_channel_ready_
285       || !pending_reply_for_render_process_)
286     return;
287
288   XWalkExtensionProcessHostMsg_GetExtensionProcessChannel::WriteReplyParams(
289       pending_reply_for_render_process_.get(), ep_rp_channel_handle_);
290
291   render_process_host_->Send(pending_reply_for_render_process_.release());
292 }
293
294 void XWalkExtensionProcessHost::ReplyAccessControlToExtension(
295     IPC::Message* reply_msg,
296     RuntimePermission perm) {
297   XWalkExtensionProcessHostMsg_CheckAPIAccessControl
298       ::WriteReplyParams(reply_msg, perm);
299   Send(reply_msg);
300 }
301
302 void XWalkExtensionProcessHost::OnCheckAPIAccessControl(
303     const std::string& extension_name,
304     const std::string& api_name, IPC::Message* reply_msg) {
305   CHECK(delegate_);
306   delegate_->OnCheckAPIAccessControl(render_process_host_->GetID(),
307                                      extension_name, api_name,
308       base::Bind(&XWalkExtensionProcessHost::ReplyAccessControlToExtension,
309                  base::Unretained(this),
310                  reply_msg));
311 }
312
313 void XWalkExtensionProcessHost::OnRegisterPermissions(
314     const std::string& extension_name,
315     const std::string& perm_table, bool* result) {
316   CHECK(delegate_);
317   *result = delegate_->OnRegisterPermissions(
318       render_process_host_->GetID(), extension_name, perm_table);
319 }
320
321 bool XWalkExtensionProcessHost::Send(IPC::Message* msg) {
322   if (process_)
323     return process_->GetHost()->Send(msg);
324   if (channel_)
325     return channel_->Send(msg);
326   return false;
327 }
328
329 }  // namespace extensions
330 }  // namespace xwalk
331