81fa29e6e67214d7b06ca0f0598fe411632f3c55
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / application.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/application/browser/application.h"
6
7 #include <string>
8
9 #include "base/files/file_enumerator.h"
10 #include "base/json/json_reader.h"
11 #include "base/macros.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_split.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/values.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/site_instance.h"
20 #include "net/base/net_util.h"
21 #include "xwalk/application/common/application_manifest_constants.h"
22 #include "xwalk/application/common/constants.h"
23 #include "xwalk/application/common/manifest_handlers/warp_handler.h"
24 #include "xwalk/runtime/browser/runtime.h"
25 #include "xwalk/runtime/browser/runtime_context.h"
26 #include "xwalk/runtime/browser/xwalk_runner.h"
27
28 #if defined(OS_TIZEN)
29 #include "xwalk/application/browser/application_tizen.h"
30 #endif
31
32 using content::RenderProcessHost;
33
34 namespace xwalk {
35
36 namespace keys = application_manifest_keys;
37 namespace widget_keys = application_widget_keys;
38
39 namespace {
40 const char* kDefaultWidgetEntryPage[] = {
41 "index.html",
42 "index.htm",
43 "index.svg",
44 "index.xhtml",
45 "index.xht"};
46
47 GURL GetDefaultWidgetEntryPage(
48     scoped_refptr<xwalk::application::ApplicationData> data) {
49   base::ThreadRestrictions::SetIOAllowed(true);
50   base::FileEnumerator iter(
51       data->Path(), true,
52       base::FileEnumerator::FILES,
53       FILE_PATH_LITERAL("index.*"));
54   size_t priority = arraysize(kDefaultWidgetEntryPage);
55   std::string source;
56
57   for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next()) {
58     for (size_t i = 0; i < arraysize(kDefaultWidgetEntryPage); ++i) {
59       if (file.BaseName().MaybeAsASCII() == kDefaultWidgetEntryPage[i] &&
60           i < priority) {
61         source = kDefaultWidgetEntryPage[i];
62         priority = i;
63       }
64     }
65   }
66
67   return source.empty() ? GURL() : data->GetResourceURL(source);
68 }
69
70 }  // namespace
71
72 namespace application {
73
74 scoped_ptr<Application> Application::Create(
75     scoped_refptr<ApplicationData> data,
76     RuntimeContext* context) {
77 #if defined(OS_TIZEN)
78   return make_scoped_ptr<Application>(new ApplicationTizen(data, context));
79 #else
80   return make_scoped_ptr(new Application(data, context));
81 #endif
82 }
83
84 Application::Application(
85     scoped_refptr<ApplicationData> data,
86     RuntimeContext* runtime_context)
87     : data_(data),
88       render_process_host_(NULL),
89       web_contents_(NULL),
90       security_mode_enabled_(false),
91       runtime_context_(runtime_context),
92       observer_(NULL),
93       remote_debugging_enabled_(false),
94       weak_factory_(this) {
95   DCHECK(runtime_context_);
96   DCHECK(data_);
97 }
98
99 Application::~Application() {
100   Terminate();
101   if (render_process_host_)
102     render_process_host_->RemoveObserver(this);
103 }
104
105 template<>
106 GURL Application::GetStartURL<Package::WGT>() {
107   GURL url = GetAbsoluteURLFromKey(widget_keys::kLaunchLocalPathKey);
108   if (!url.is_valid()) {
109     LOG(WARNING) << "Failed to find start URL from the 'config.xml'"
110                  << "trying to find default entry page.";
111     url = GetDefaultWidgetEntryPage(data_);
112   }
113
114   if (url.is_valid()) {
115 #if defined(OS_TIZEN)
116     if (data_->IsHostedApp() && !url.SchemeIsHTTPOrHTTPS()) {
117       LOG(ERROR) << "Hosted apps are only supported with"
118                     "http:// or https:// scheme.";
119       return GURL();
120     }
121 #endif
122     return url;
123   }
124
125   LOG(WARNING) << "Failed to find a valid start URL in the manifest.";
126   return GURL();
127 }
128
129 template<>
130 GURL Application::GetStartURL<Package::XPK>() {
131   GURL url = GetAbsoluteURLFromKey(keys::kStartURLKey);
132   if (url.is_valid())
133     return url;
134
135   url = GetAbsoluteURLFromKey(keys::kLaunchLocalPathKey);
136   if (url.is_valid())
137     return url;
138
139   url = GetAbsoluteURLFromKey(keys::kDeprecatedURLKey);
140   if (url.is_valid()) {
141     LOG(WARNING) << "Deprecated key '" << keys::kDeprecatedURLKey
142         << "' found. Please migrate to using '" << keys::kStartURLKey
143         << "' instead.";
144     return url;
145   }
146
147   LOG(WARNING) << "Failed to find a valid start URL in the manifest.";
148   return GURL();
149 }
150
151
152 template<>
153 ui::WindowShowState Application::GetWindowShowState<Package::WGT>(
154     const LaunchParams& params) {
155   if (params.force_fullscreen)
156     return ui::SHOW_STATE_FULLSCREEN;
157
158   const Manifest* manifest = data_->GetManifest();
159   std::string view_modes_string;
160   if (manifest->GetString(widget_keys::kViewModesKey, &view_modes_string)) {
161     // FIXME: ATM only 'fullscreen' and 'windowed' values are supported.
162     // If the first user prefererence is 'fullscreen', set window show state
163     // FULLSCREEN, otherwise set the default window show state.
164     std::vector<std::string> modes;
165     base::SplitString(view_modes_string, ' ', &modes);
166     if (!modes.empty() && modes[0] == "fullscreen")
167       return ui::SHOW_STATE_FULLSCREEN;
168   }
169
170   return ui::SHOW_STATE_DEFAULT;
171 }
172
173 template<>
174 ui::WindowShowState Application::GetWindowShowState<Package::XPK>(
175     const LaunchParams& params) {
176   if (params.force_fullscreen)
177     return ui::SHOW_STATE_FULLSCREEN;
178
179   const Manifest* manifest = data_->GetManifest();
180   std::string display_string;
181   if (manifest->GetString(keys::kDisplay, &display_string)) {
182     // FIXME: ATM only 'fullscreen' and 'standalone' (which is fallback value)
183     // values are supported.
184     if (display_string.find("fullscreen") != std::string::npos)
185       return ui::SHOW_STATE_FULLSCREEN;
186   }
187
188   return ui::SHOW_STATE_DEFAULT;
189 }
190
191 bool Application::Launch(const LaunchParams& launch_params) {
192   if (!runtimes_.empty()) {
193     LOG(ERROR) << "Attempt to launch app with id " << id()
194                << ", but it is already running.";
195     return false;
196   }
197
198   CHECK(!render_process_host_);
199   bool is_wgt = data_->GetPackageType() == Package::WGT;
200
201   GURL url = is_wgt ? GetStartURL<Package::WGT>():
202                       GetStartURL<Package::XPK>();
203   if (!url.is_valid())
204     return false;
205
206   remote_debugging_enabled_ = launch_params.remote_debugging;
207
208   Runtime* runtime = Runtime::Create(
209       runtime_context_,
210       this, content::SiteInstance::CreateForURL(runtime_context_, url));
211   render_process_host_ = runtime->GetRenderProcessHost();
212   render_process_host_->AddObserver(this);
213   web_contents_ = runtime->web_contents();
214   InitSecurityPolicy();
215   runtime->LoadURL(url);
216
217   NativeAppWindow::CreateParams params;
218   params.net_wm_pid = launch_params.launcher_pid;
219   params.state = is_wgt ? GetWindowShowState<Package::WGT>(launch_params):
220                           GetWindowShowState<Package::XPK>(launch_params);
221
222   params.splash_screen_path = GetSplashScreenPath();
223
224   runtime->AttachWindow(params);
225
226   return true;
227 }
228
229 GURL Application::GetAbsoluteURLFromKey(const std::string& key) {
230   const Manifest* manifest = data_->GetManifest();
231   std::string source;
232
233   if (!manifest->GetString(key, &source) || source.empty())
234     return GURL();
235
236   std::size_t found = source.find("://");
237   if (found == std::string::npos)
238     return data_->GetResourceURL(source);
239   return GURL(source);
240 }
241
242 void Application::Terminate() {
243   std::set<Runtime*> to_be_closed(runtimes_);
244   std::for_each(to_be_closed.begin(), to_be_closed.end(),
245                 std::mem_fun(&Runtime::Close));
246 }
247
248 int Application::GetRenderProcessHostID() const {
249   DCHECK(render_process_host_);
250   return render_process_host_->GetID();
251 }
252
253 void Application::OnRuntimeAdded(Runtime* runtime) {
254   DCHECK(runtime);
255   runtime->set_remote_debugging_enabled(remote_debugging_enabled_);
256   runtimes_.insert(runtime);
257 }
258
259 void Application::OnRuntimeRemoved(Runtime* runtime) {
260   DCHECK(runtime);
261   runtimes_.erase(runtime);
262
263   if (runtimes_.empty()) {
264 #if defined(OS_TIZEN_MOBILE)
265     runtime->CloseRootWindow();
266 #endif
267     base::MessageLoop::current()->PostTask(FROM_HERE,
268         base::Bind(&Application::NotifyTermination,
269                    weak_factory_.GetWeakPtr()));
270   }
271 }
272
273 void Application::RenderProcessExited(RenderProcessHost* host,
274                                       base::ProcessHandle,
275                                       base::TerminationStatus,
276                                       int) {
277   DCHECK(render_process_host_ == host);
278   VLOG(1) << "RenderProcess id: " << host->GetID() << " is gone!";
279   XWalkRunner::GetInstance()->OnRenderProcessHostGone(host);
280 }
281
282 void Application::RenderProcessHostDestroyed(RenderProcessHost* host) {
283   DCHECK(render_process_host_ == host);
284   render_process_host_ = NULL;
285   web_contents_ = NULL;
286 }
287
288 void Application::NotifyTermination() {
289   CHECK(!render_process_host_);
290   if (observer_)
291     observer_->OnApplicationTerminated(this);
292 }
293
294 bool Application::UseExtension(const std::string& extension_name) const {
295   // TODO(Bai): Tells whether the application contains the specified extension
296   return true;
297 }
298
299 bool Application::RegisterPermissions(const std::string& extension_name,
300                                       const std::string& perm_table) {
301   // TODO(Bai): Parse the permission table and fill in the name_perm_map_
302   // The perm_table format is a simple JSON string, like
303   // [{"permission_name":"echo","apis":["add","remove","get"]}]
304   scoped_ptr<base::Value> root;
305   root.reset(base::JSONReader().ReadToValue(perm_table));
306   if (root.get() == NULL || !root->IsType(base::Value::TYPE_LIST))
307     return false;
308
309   base::ListValue* permission_list = static_cast<base::ListValue*>(root.get());
310   if (permission_list->GetSize() == 0)
311     return false;
312
313   for (base::ListValue::const_iterator iter = permission_list->begin();
314       iter != permission_list->end(); ++iter) {
315     if (!(*iter)->IsType(base::Value::TYPE_DICTIONARY))
316         return false;
317
318     base::DictionaryValue* dict_val =
319         static_cast<base::DictionaryValue*>(*iter);
320     std::string permission_name;
321     if (!dict_val->GetString("permission_name", &permission_name))
322       return false;
323
324     base::ListValue* api_list = NULL;
325     if (!dict_val->GetList("apis", &api_list))
326       return false;
327
328     for (base::ListValue::const_iterator api_iter = api_list->begin();
329         api_iter != api_list->end(); ++api_iter) {
330       std::string api;
331       if (!((*api_iter)->IsType(base::Value::TYPE_STRING)
332           && (*api_iter)->GetAsString(&api)))
333         return false;
334       // register the permission and api
335       name_perm_map_[api] = permission_name;
336       DLOG(INFO) << "Permission Registered [PERM] " << permission_name
337                  << " [API] " << api;
338     }
339   }
340
341   return true;
342 }
343
344 std::string Application::GetRegisteredPermissionName(
345     const std::string& extension_name,
346     const std::string& api_name) const {
347   std::map<std::string, std::string>::const_iterator iter =
348       name_perm_map_.find(api_name);
349   if (iter == name_perm_map_.end())
350     return std::string("");
351   return iter->second;
352 }
353
354 StoredPermission Application::GetPermission(PermissionType type,
355                                const std::string& permission_name) const {
356   if (type == SESSION_PERMISSION) {
357     StoredPermissionMap::const_iterator iter =
358         permission_map_.find(permission_name);
359     if (iter == permission_map_.end())
360       return UNDEFINED_STORED_PERM;
361     return iter->second;
362   }
363   if (type == PERSISTENT_PERMISSION) {
364     return data_->GetPermission(permission_name);
365   }
366   NOTREACHED();
367   return UNDEFINED_STORED_PERM;
368 }
369
370 bool Application::SetPermission(PermissionType type,
371                                 const std::string& permission_name,
372                                 StoredPermission perm) {
373   if (type == SESSION_PERMISSION) {
374     permission_map_[permission_name] = perm;
375     return true;
376   }
377   if (type == PERSISTENT_PERMISSION)
378     return data_->SetPermission(permission_name, perm);
379
380   NOTREACHED();
381   return false;
382 }
383
384 void Application::InitSecurityPolicy() {
385   // CSP policy takes precedence over WARP.
386   if (data_->HasCSPDefined())
387     security_policy_.reset(new SecurityPolicyCSP(this));
388   else if (data_->GetPackageType() == Package::WGT)
389     security_policy_.reset(new SecurityPolicyWARP(this));
390
391   if (security_policy_)
392     security_policy_->Enforce();
393 }
394
395 bool Application::CanRequestURL(const GURL& url) const {
396   if (security_policy_)
397     return security_policy_->IsAccessAllowed(url);
398   return true;
399 }
400
401 base::FilePath Application::GetSplashScreenPath() {
402   return base::FilePath();
403 }
404
405 }  // namespace application
406 }  // namespace xwalk