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