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