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