7bda9f68d6e8306263f11b1f1d3670ecf715a428
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / application_service.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_service.h"
6
7 #include <set>
8 #include <string>
9
10 #include "base/files/file_enumerator.h"
11 #include "base/file_util.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/version.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_observer.h"
18 #include "xwalk/application/browser/application_event_manager.h"
19 #include "xwalk/application/browser/application.h"
20 #include "xwalk/application/browser/application_storage.h"
21 #include "xwalk/application/browser/application_system.h"
22 #include "xwalk/application/browser/installer/package.h"
23 #include "xwalk/application/common/application_file_util.h"
24 #include "xwalk/application/common/application_manifest_constants.h"
25 #include "xwalk/application/common/event_names.h"
26 #include "xwalk/application/common/id_util.h"
27 #include "xwalk/application/common/permission_policy_manager.h"
28 #include "xwalk/runtime/browser/runtime_context.h"
29 #include "xwalk/runtime/browser/runtime.h"
30 #include "xwalk/runtime/browser/xwalk_runner.h"
31
32 #if defined(OS_TIZEN)
33 #include "xwalk/application/browser/installer/tizen/package_installer.h"
34 #include "xwalk/application/browser/installer/tizen/service_package_installer.h"
35 #endif
36
37 using xwalk::RuntimeContext;
38
39 namespace {
40
41 void CloseMessageLoop() {
42   // FIXME: Quit message loop here at present. This should go away once
43   // we have Application in place.
44   base::MessageLoop::current()->QuitWhenIdle();
45 }
46
47 void WaitForEventAndClose(
48     const std::string& app_id, const std::string& event_name,
49     xwalk::application::ApplicationEventManager* event_manager) {
50   class CloseOnEventArrived : public xwalk::application::EventObserver {
51    public:
52     static CloseOnEventArrived* Create(const std::string& event_name,
53         xwalk::application::ApplicationEventManager* event_manager) {
54       return new CloseOnEventArrived(event_name, event_manager);
55     }
56
57     virtual void Observe(
58         const std::string& app_id,
59         scoped_refptr<xwalk::application::Event> event) OVERRIDE {
60       DCHECK(xwalk::application::kOnJavaScriptEventAck == event->name());
61       std::string ack_event_name;
62       event->args()->GetString(0, &ack_event_name);
63       if (ack_event_name != event_name_)
64         return;
65       CloseMessageLoop();
66       delete this;
67     }
68
69    private:
70     CloseOnEventArrived(
71         const std::string& event_name,
72         xwalk::application::ApplicationEventManager* event_manager)
73         : xwalk::application::EventObserver(event_manager),
74           event_name_(event_name) {}
75
76     std::string event_name_;
77   };
78
79   DCHECK(event_manager);
80   CloseOnEventArrived* observer =
81       CloseOnEventArrived::Create(event_name, event_manager);
82   event_manager->AttachObserver(app_id,
83       xwalk::application::kOnJavaScriptEventAck, observer);
84 }
85
86 void WaitForFinishLoad(
87     scoped_refptr<xwalk::application::ApplicationData> application,
88     xwalk::application::ApplicationEventManager* event_manager,
89     content::WebContents* contents) {
90   class CloseAfterLoadObserver : public content::WebContentsObserver {
91    public:
92     CloseAfterLoadObserver(
93         scoped_refptr<xwalk::application::ApplicationData> application,
94         xwalk::application::ApplicationEventManager* event_manager,
95         content::WebContents* contents)
96         : content::WebContentsObserver(contents),
97           application_(application),
98           event_manager_(event_manager) {
99       DCHECK(application_);
100       DCHECK(event_manager_);
101     }
102
103     virtual void DidFinishLoad(
104         int64 frame_id,
105         const GURL& validate_url,
106         bool is_main_frame,
107         content::RenderViewHost* render_view_host) OVERRIDE {
108       if (!IsEventHandlerRegistered(xwalk::application::kOnInstalled)) {
109         CloseMessageLoop();
110       } else {
111         scoped_ptr<base::ListValue> event_args(new base::ListValue);
112         scoped_refptr<xwalk::application::Event> event =
113             xwalk::application::Event::CreateEvent(
114                 xwalk::application::kOnInstalled, event_args.Pass());
115         event_manager_->SendEvent(application_->ID(), event);
116
117         WaitForEventAndClose(
118             application_->ID(), event->name(), event_manager_);
119       }
120       delete this;
121     }
122
123    private:
124     bool IsEventHandlerRegistered(const std::string& event_name) const {
125       const std::set<std::string>& events = application_->GetEvents();
126       return events.find(event_name) != events.end();
127     }
128
129     scoped_refptr<xwalk::application::ApplicationData> application_;
130     xwalk::application::ApplicationEventManager* event_manager_;
131   };
132
133   // This object is self-destroyed when an event occurs.
134   new CloseAfterLoadObserver(application, event_manager, contents);
135 }
136
137 void SaveSystemEventsInfo(
138     xwalk::application::ApplicationService* application_service,
139     scoped_refptr<xwalk::application::ApplicationData> application_data,
140     xwalk::application::ApplicationEventManager* event_manager) {
141   // We need to run main document after installation in order to
142   // register system events.
143   if (application_data->HasMainDocument()) {
144     if (xwalk::application::Application* application =
145         application_service->Launch(application_data->ID())) {
146       WaitForFinishLoad(application->data(), event_manager,
147                         application->GetMainDocumentRuntime()->web_contents());
148     }
149   }
150 }
151
152 #if defined(OS_TIZEN)
153 bool InstallPackageOnTizen(xwalk::application::ApplicationService* service,
154                            xwalk::application::ApplicationStorage* storage,
155                            xwalk::application::ApplicationData* application,
156                            const base::FilePath& data_dir) {
157   if (xwalk::XWalkRunner::GetInstance()->is_running_as_service()) {
158     return InstallApplicationForTizen(application, data_dir);
159   }
160
161   scoped_ptr<xwalk::application::PackageInstaller> installer =
162       xwalk::application::PackageInstaller::Create(service, storage,
163                                                    application->ID(), data_dir);
164   if (!installer || !installer->Install()) {
165     LOG(ERROR) << "An error occurred during installation on Tizen.";
166     return false;
167   }
168   return true;
169 }
170
171 bool UninstallPackageOnTizen(xwalk::application::ApplicationService* service,
172                              xwalk::application::ApplicationStorage* storage,
173                              xwalk::application::ApplicationData* application,
174                              const base::FilePath& data_dir) {
175   if (xwalk::XWalkRunner::GetInstance()->is_running_as_service()) {
176     return UninstallApplicationForTizen(application, data_dir);
177   }
178
179   scoped_ptr<xwalk::application::PackageInstaller> installer =
180       xwalk::application::PackageInstaller::Create(service, storage,
181                                                    application->ID(), data_dir);
182   if (!installer || !installer->Uninstall()) {
183     LOG(ERROR) << "An error occurred during uninstallation on Tizen.";
184     return false;
185   }
186   return true;
187 }
188 #endif  // OS_TIZEN
189
190 bool CopyDirectoryContents(const base::FilePath& from,
191     const base::FilePath& to) {
192   base::FileEnumerator iter(from, false,
193       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
194   for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
195     if (iter.GetInfo().IsDirectory()) {
196       if (!base::CopyDirectory(path, to, true))
197         return false;
198     } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
199         return false;
200     }
201   }
202
203   return true;
204 }
205
206 }  // namespace
207
208 namespace xwalk {
209 namespace application {
210
211 const base::FilePath::CharType kApplicationsDir[] =
212     FILE_PATH_LITERAL("applications");
213
214 ApplicationService::ApplicationService(RuntimeContext* runtime_context,
215                                        ApplicationStorage* app_storage,
216                                        ApplicationEventManager* event_manager)
217     : runtime_context_(runtime_context),
218       application_storage_(app_storage),
219       event_manager_(event_manager),
220       permission_policy_handler_(new PermissionPolicyManager()) {
221   AddObserver(event_manager);
222 }
223
224 ApplicationService::~ApplicationService() {
225 }
226
227 bool ApplicationService::Install(const base::FilePath& path, std::string* id) {
228   // FIXME(leandro): Installation is not robust enough -- should any step
229   // fail, it can't roll back to a consistent state.
230   if (!base::PathExists(path))
231     return false;
232
233   const base::FilePath data_dir =
234       runtime_context_->GetPath().Append(kApplicationsDir);
235
236   // Make sure the kApplicationsDir exists under data_path, otherwise,
237   // the installation will always fail because of moving application
238   // resources into an invalid directory.
239   if (!base::DirectoryExists(data_dir) &&
240       !base::CreateDirectory(data_dir))
241     return false;
242
243   std::string app_id;
244   base::FilePath unpacked_dir;
245   scoped_ptr<Package> package;
246   if (!base::DirectoryExists(path)) {
247     package = Package::Create(path);
248     package->Extract(&unpacked_dir);
249     app_id = package->Id();
250   } else {
251     unpacked_dir = path;
252   }
253
254   std::string error;
255   scoped_refptr<ApplicationData> application_data = LoadApplication(
256       unpacked_dir, app_id, Manifest::COMMAND_LINE, &error);
257   if (!application_data) {
258     LOG(ERROR) << "Error during application installation: " << error;
259     return false;
260   }
261   if (!permission_policy_handler_->
262       InitApplicationPermission(application_data)) {
263     LOG(ERROR) << "Application permission data is invalid";
264     return false;
265   }
266
267   if (application_storage_->Contains(application_data->ID())) {
268     *id = application_data->ID();
269     LOG(INFO) << "Already installed: " << *id;
270     return false;
271   }
272
273   base::FilePath app_dir = data_dir.AppendASCII(application_data->ID());
274   if (base::DirectoryExists(app_dir)) {
275     if (!base::DeleteFile(app_dir, true))
276       return false;
277   }
278   if (!package) {
279     if (!base::CreateDirectory(app_dir))
280       return false;
281     if (!CopyDirectoryContents(unpacked_dir, app_dir))
282       return false;
283   } else {
284     if (!base::Move(unpacked_dir, app_dir))
285       return false;
286   }
287
288   application_data->SetPath(app_dir);
289
290   if (!application_storage_->AddApplication(application_data)) {
291     LOG(ERROR) << "Application with id " << application_data->ID()
292                << " couldn't be installed.";
293     return false;
294   }
295
296 #if defined(OS_TIZEN)
297   if (!InstallPackageOnTizen(this, application_storage_,
298                              application_data.get(),
299                              runtime_context_->GetPath())) {
300     application_storage_->RemoveApplication(application_data->ID());
301     return false;
302   }
303 #endif
304
305   LOG(INFO) << "Application be installed in: " << app_dir.MaybeAsASCII();
306   LOG(INFO) << "Installed application with id: " << application_data->ID()
307             << " successfully.";
308   *id = application_data->ID();
309
310   SaveSystemEventsInfo(this, application_data, event_manager_);
311
312   FOR_EACH_OBSERVER(Observer, observers_,
313                     OnApplicationInstalled(application_data->ID()));
314
315   return true;
316 }
317
318 bool ApplicationService::Update(const std::string& id,
319                                 const base::FilePath& path) {
320   if (!base::PathExists(path)) {
321     LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
322     return false;
323   }
324
325   if (base::DirectoryExists(path)) {
326     LOG(WARNING) << "Can not update an unpacked XPK/WGT package.";
327     return false;
328   }
329
330   base::FilePath unpacked_dir;
331   base::FilePath origin_dir;
332   std::string app_id;
333   scoped_ptr<Package> package = Package::Create(path);
334   if (!package) {
335     LOG(ERROR) << "XPK/WGT file is invalid.";
336     return false;
337   }
338
339   app_id = package->Id();
340
341   if (app_id.empty()) {
342     LOG(ERROR) << "XPK/WGT file is invalid, and the application id is empty.";
343     return false;
344   }
345
346   if (id.empty() ||
347       id.compare(app_id) != 0) {
348     LOG(ERROR) << "The XPK/WGT file is not the same as expecting.";
349     return false;
350   }
351
352   if (!package->Extract(&unpacked_dir))
353     return false;
354
355   std::string error;
356   scoped_refptr<ApplicationData> new_application =
357       LoadApplication(unpacked_dir,
358                       app_id,
359                       Manifest::COMMAND_LINE,
360                       &error);
361   if (!new_application) {
362     LOG(ERROR) << "An error occurred during application updating: " << error;
363     return false;
364   }
365
366   scoped_refptr<ApplicationData> old_application =
367       application_storage_->GetApplicationData(app_id);
368   if (!old_application) {
369     LOG(INFO) << "Application haven't installed yet: " << app_id;
370     return false;
371   }
372
373   if (old_application->Version()->CompareTo(
374           *(new_application->Version())) >= 0) {
375     LOG(INFO) << "The version number of new XPK/WGT package "
376                  "should be higher than "
377               << old_application->VersionString();
378     return false;
379   }
380
381   const base::FilePath& app_dir = old_application->Path();
382   const base::FilePath tmp_dir(app_dir.value()
383                                + FILE_PATH_LITERAL(".tmp"));
384
385   // FIXME: Need to terminate the application here if it's running, after
386   // shared runtime process mode has been implemented.
387   if (!base::Move(app_dir, tmp_dir) ||
388       !base::Move(unpacked_dir, app_dir))
389     return false;
390
391   new_application = LoadApplication(app_dir,
392                                     app_id,
393                                     Manifest::COMMAND_LINE,
394                                     &error);
395   if (!new_application) {
396     LOG(ERROR) << "Error during loading new package: " << error;
397     base::DeleteFile(app_dir, true);
398     base::Move(tmp_dir, app_dir);
399     return false;
400   }
401
402 #if defined(OS_TIZEN)
403   if (!UninstallPackageOnTizen(this, application_storage_,
404                                old_application.get(),
405                                runtime_context_->GetPath())) {
406     base::DeleteFile(app_dir, true);
407     base::Move(tmp_dir, app_dir);
408     return false;
409   }
410 #endif
411
412   if (!application_storage_->UpdateApplication(new_application)) {
413     LOG(ERROR) << "An Error occurred when updating the application.";
414     base::DeleteFile(app_dir, true);
415     base::Move(tmp_dir, app_dir);
416 #if defined(OS_TIZEN)
417     InstallPackageOnTizen(this, application_storage_,
418                           old_application.get(),
419                           runtime_context_->GetPath());
420 #endif
421     return false;
422   }
423 #if defined(OS_TIZEN)
424   if (!InstallPackageOnTizen(this, application_storage_,
425                              new_application.get(),
426                              runtime_context_->GetPath()))
427     return false;
428 #endif
429   base::DeleteFile(tmp_dir, true);
430
431   SaveSystemEventsInfo(this, new_application, event_manager_);
432
433   FOR_EACH_OBSERVER(Observer, observers_,
434                     OnApplicationUpdated(app_id));
435
436   return true;
437 }
438
439 bool ApplicationService::Uninstall(const std::string& id) {
440   bool result = true;
441
442   scoped_refptr<ApplicationData> application =
443       application_storage_->GetApplicationData(id);
444   if (!application) {
445     LOG(ERROR) << "Cannot uninstall application with id " << id
446                << "; invalid application id";
447     return false;
448   }
449
450 #if defined(OS_TIZEN)
451   if (!UninstallPackageOnTizen(this, application_storage_, application.get(),
452                                runtime_context_->GetPath()))
453     result = false;
454 #endif
455
456   if (!application_storage_->RemoveApplication(id)) {
457     LOG(ERROR) << "Cannot uninstall application with id " << id
458                << "; application is not installed.";
459     result = false;
460   }
461
462   const base::FilePath resources =
463       runtime_context_->GetPath().Append(kApplicationsDir).AppendASCII(id);
464   if (base::DirectoryExists(resources) &&
465       !base::DeleteFile(resources, true)) {
466     LOG(ERROR) << "Error occurred while trying to remove application with id "
467                << id << "; Cannot remove all resources.";
468     result = false;
469   }
470
471   content::StoragePartition* partition =
472       content::BrowserContext::GetStoragePartitionForSite(
473           runtime_context_, application->GetBaseURLFromApplicationId(id));
474   partition->ClearDataForOrigin(
475       content::StoragePartition::REMOVE_DATA_MASK_ALL,
476       content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
477       application->URL(),
478       partition->GetURLRequestContext());
479
480   FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
481
482   return result;
483 }
484
485 Application* ApplicationService::Launch(
486     const std::string& id, const Application::LaunchParams& params) {
487   scoped_refptr<ApplicationData> application_data =
488     application_storage_->GetApplicationData(id);
489   if (!application_data) {
490     LOG(ERROR) << "Application with id " << id << " is not installed.";
491     return NULL;
492   }
493
494   return Launch(application_data, params);
495 }
496
497 Application* ApplicationService::Launch(const base::FilePath& path) {
498   if (!base::DirectoryExists(path))
499     return NULL;
500
501   std::string error;
502   scoped_refptr<ApplicationData> application_data =
503       LoadApplication(path, Manifest::COMMAND_LINE, &error);
504
505   if (!application_data) {
506     LOG(ERROR) << "Error occurred while trying to launch application: "
507                << error;
508     return NULL;
509   }
510
511   return Launch(application_data, Application::LaunchParams());
512 }
513
514 Application* ApplicationService::Launch(const GURL& url) {
515   namespace keys = xwalk::application_manifest_keys;
516
517   const std::string& url_spec = url.spec();
518   DCHECK(!url_spec.empty());
519   const std::string& app_id = GenerateId(url_spec);
520   // FIXME: we need to handle hash collisions.
521   DCHECK(!application_storage_->GetApplicationData(app_id));
522
523   base::DictionaryValue manifest;
524   // FIXME: define permissions!
525   manifest.SetString(keys::kURLKey, url_spec);
526   manifest.SetString(keys::kNameKey, "XWalk Dummy App");
527   manifest.SetString(keys::kVersionKey, "0");
528   manifest.SetInteger(keys::kManifestVersionKey, 1);
529   std::string error;
530   scoped_refptr<ApplicationData> application_data = ApplicationData::Create(
531             base::FilePath(), Manifest::COMMAND_LINE, manifest, app_id, &error);
532   if (!application_data) {
533     LOG(ERROR) << "Error occurred while trying to launch application: "
534                << error;
535     return NULL;
536   }
537
538   Application::LaunchParams launch_params;
539   launch_params.entry_points = Application::URLKey;
540   return Launch(application_data, launch_params);
541 }
542
543 namespace {
544
545 struct ApplicationRenderHostIDComparator {
546     explicit ApplicationRenderHostIDComparator(int id) : id(id) { }
547     bool operator() (Application* application) {
548       return id == application->GetRenderProcessHostID();
549     }
550     int id;
551 };
552
553 struct ApplicationIDComparator {
554     explicit ApplicationIDComparator(const std::string& app_id)
555       : app_id(app_id) { }
556     bool operator() (Application* application) {
557       return app_id == application->id();
558     }
559     std::string app_id;
560 };
561
562 }  // namespace
563
564 Application* ApplicationService::GetApplicationByRenderHostID(int id) const {
565   ApplicationRenderHostIDComparator comparator(id);
566   ScopedVector<Application>::const_iterator found = std::find_if(
567       applications_.begin(), applications_.end(), comparator);
568   if (found != applications_.end())
569     return *found;
570   return NULL;
571 }
572
573 Application* ApplicationService::GetApplicationByID(
574     const std::string& app_id) const {
575   ApplicationIDComparator comparator(app_id);
576   ScopedVector<Application>::const_iterator found = std::find_if(
577       applications_.begin(), applications_.end(), comparator);
578   if (found != applications_.end())
579     return *found;
580   return NULL;
581 }
582
583 void ApplicationService::AddObserver(Observer* observer) {
584   observers_.AddObserver(observer);
585 }
586
587 void ApplicationService::RemoveObserver(Observer* observer) {
588   observers_.RemoveObserver(observer);
589 }
590
591 void ApplicationService::OnApplicationTerminated(
592                                       Application* application) {
593   ScopedVector<Application>::iterator found = std::find(
594       applications_.begin(), applications_.end(), application);
595   CHECK(found != applications_.end());
596   FOR_EACH_OBSERVER(Observer, observers_,
597                     WillDestroyApplication(application));
598   applications_.erase(found);
599   if (applications_.empty()) {
600     base::MessageLoop::current()->PostTask(
601             FROM_HERE, base::MessageLoop::QuitClosure());
602   }
603 }
604
605 Application* ApplicationService::Launch(
606     scoped_refptr<ApplicationData> application_data,
607     const Application::LaunchParams& launch_params) {
608   if (GetApplicationByID(application_data->ID()) != NULL) {
609     LOG(INFO) << "Application with id: " << application_data->ID()
610               << " is already running.";
611     // FIXME: we need to notify application that it had been attempted
612     // to invoke and let the application to define the further behavior.
613     return NULL;
614   }
615
616   event_manager_->AddEventRouterForApp(application_data);
617   Application* application(new Application(application_data,
618                                            runtime_context_,
619                                            this));
620   ScopedVector<Application>::iterator app_iter =
621       applications_.insert(applications_.end(), application);
622
623   if (!application->Launch(launch_params)) {
624     event_manager_->RemoveEventRouterForApp(application_data);
625     applications_.erase(app_iter);
626     return NULL;
627   }
628
629   FOR_EACH_OBSERVER(Observer, observers_,
630                     DidLaunchApplication(application));
631
632   return application;
633 }
634
635 void ApplicationService::CheckAPIAccessControl(const std::string& app_id,
636     const std::string& extension_name,
637     const std::string& api_name, const PermissionCallback& callback) {
638   Application* app = GetApplicationByID(app_id);
639   if (!app) {
640     LOG(ERROR) << "No running application found with ID: "
641       << app_id;
642     callback.Run(UNDEFINED_RUNTIME_PERM);
643     return;
644   }
645   if (!app->UseExtension(extension_name)) {
646     LOG(ERROR) << "Can not find extension: "
647       << extension_name << " of Application with ID: "
648       << app_id;
649     callback.Run(UNDEFINED_RUNTIME_PERM);
650     return;
651   }
652   // Permission name should have been registered at extension initialization.
653   std::string permission_name =
654       app->GetRegisteredPermissionName(extension_name, api_name);
655   if (permission_name.empty()) {
656     LOG(ERROR) << "API: " << api_name << " of extension: "
657       << extension_name << " not registered!";
658     callback.Run(UNDEFINED_RUNTIME_PERM);
659     return;
660   }
661   // Okay, since we have the permission name, let's get down to the policies.
662   // First, find out whether the permission is stored for the current session.
663   StoredPermission perm = app->GetPermission(
664       SESSION_PERMISSION, permission_name);
665   if (perm != UNDEFINED_STORED_PERM) {
666     // "PROMPT" should not be in the session storage.
667     DCHECK(perm != PROMPT);
668     if (perm == ALLOW) {
669       callback.Run(ALLOW_SESSION);
670       return;
671     }
672     if (perm == DENY) {
673       callback.Run(DENY_SESSION);
674       return;
675     }
676     NOTREACHED();
677   }
678   // Then, query the persistent policy storage.
679   perm = app->GetPermission(PERSISTENT_PERMISSION, permission_name);
680   // Permission not found in persistent permission table, normally this should
681   // not happen because all the permission needed by the application should be
682   // contained in its manifest, so it also means that the application is asking
683   // for something wasn't allowed.
684   if (perm == UNDEFINED_STORED_PERM) {
685     callback.Run(UNDEFINED_RUNTIME_PERM);
686     return;
687   }
688   if (perm == PROMPT) {
689     // TODO(Bai): We needed to pop-up a dialog asking user to chose one from
690     // either allow/deny for session/one shot/forever. Then, we need to update
691     // the session and persistent policy accordingly.
692     callback.Run(UNDEFINED_RUNTIME_PERM);
693     return;
694   }
695   if (perm == ALLOW) {
696     callback.Run(ALLOW_ALWAYS);
697     return;
698   }
699   if (perm == DENY) {
700     callback.Run(DENY_ALWAYS);
701     return;
702   }
703   NOTREACHED();
704 }
705
706 bool ApplicationService::RegisterPermissions(const std::string& app_id,
707     const std::string& extension_name,
708     const std::string& perm_table) {
709   Application* app = GetApplicationByID(app_id);
710   if (!app) {
711     LOG(ERROR) << "No running application found with ID: " << app_id;
712     return false;
713   }
714   if (!app->UseExtension(extension_name)) {
715     LOG(ERROR) << "Can not find extension: "
716                << extension_name << " of Application with ID: "
717                << app_id;
718     return false;
719   }
720   return app->RegisterPermissions(extension_name, perm_table);
721 }
722
723 }  // namespace application
724 }  // namespace xwalk