Initial Implementation of Node prelaunch
[platform/framework/web/crosswalk-tizen.git] / atom / browser / api / atom_api_app.cc
1 // Copyright (c) 2013 GitHub, Inc.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
4
5 #include "atom/browser/api/atom_api_app.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "atom/browser/api/atom_api_menu.h"
11 #include "atom/browser/api/atom_api_session.h"
12 #include "atom/browser/api/atom_api_web_contents.h"
13 #include "atom/browser/atom_browser_context.h"
14 #include "atom/browser/atom_browser_main_parts.h"
15 #include "atom/browser/login_handler.h"
16 #include "atom/browser/relauncher.h"
17 #include "atom/common/atom_command_line.h"
18 #include "atom/common/native_mate_converters/callback.h"
19 #include "atom/common/native_mate_converters/file_path_converter.h"
20 #include "atom/common/native_mate_converters/gurl_converter.h"
21 #include "atom/common/native_mate_converters/image_converter.h"
22 #include "atom/common/native_mate_converters/net_converter.h"
23 #include "atom/common/native_mate_converters/value_converter.h"
24 #include "atom/common/node_includes.h"
25 #include "atom/common/options_switches.h"
26 #include "base/command_line.h"
27 #include "base/environment.h"
28 #include "base/files/file_path.h"
29 #include "base/files/file_util.h"
30 #include "base/path_service.h"
31 #include "base/strings/string_util.h"
32 #include "brightray/browser/brightray_paths.h"
33 #include "chrome/browser/browser_process.h"
34 #include "chrome/browser/icon_manager.h"
35 #include "chrome/common/chrome_paths.h"
36 #include "content/public/browser/browser_accessibility_state.h"
37 #include "content/public/browser/client_certificate_delegate.h"
38 #include "content/public/browser/gpu_data_manager.h"
39 #include "content/public/browser/render_frame_host.h"
40 #include "content/public/common/content_switches.h"
41 #include "media/audio/audio_manager.h"
42 #include "native_mate/dictionary.h"
43 #include "native_mate/object_template_builder.h"
44 #include "net/ssl/ssl_cert_request_info.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/gfx/image/image.h"
47
48 #if defined(OS_WIN)
49 #include "atom/browser/ui/win/jump_list.h"
50 #include "base/strings/utf_string_conversions.h"
51 #endif
52
53 using atom::Browser;
54
55 namespace mate {
56
57 #if defined(OS_WIN)
58 template<>
59 struct Converter<Browser::UserTask> {
60   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
61                      Browser::UserTask* out) {
62     mate::Dictionary dict;
63     if (!ConvertFromV8(isolate, val, &dict))
64       return false;
65     if (!dict.Get("program", &(out->program)) ||
66         !dict.Get("title", &(out->title)))
67       return false;
68     if (dict.Get("iconPath", &(out->icon_path)) &&
69         !dict.Get("iconIndex", &(out->icon_index)))
70       return false;
71     dict.Get("arguments", &(out->arguments));
72     dict.Get("description", &(out->description));
73     return true;
74   }
75 };
76
77 using atom::JumpListItem;
78 using atom::JumpListCategory;
79 using atom::JumpListResult;
80
81 template<>
82 struct Converter<JumpListItem::Type> {
83   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
84                      JumpListItem::Type* out) {
85     std::string item_type;
86     if (!ConvertFromV8(isolate, val, &item_type))
87       return false;
88
89     if (item_type == "task")
90       *out = JumpListItem::Type::TASK;
91     else if (item_type == "separator")
92       *out = JumpListItem::Type::SEPARATOR;
93     else if (item_type == "file")
94       *out = JumpListItem::Type::FILE;
95     else
96       return false;
97
98     return true;
99   }
100
101   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
102                                    JumpListItem::Type val) {
103     std::string item_type;
104     switch (val) {
105       case JumpListItem::Type::TASK:
106         item_type = "task";
107         break;
108
109       case JumpListItem::Type::SEPARATOR:
110         item_type = "separator";
111         break;
112
113       case JumpListItem::Type::FILE:
114         item_type = "file";
115         break;
116     }
117     return mate::ConvertToV8(isolate, item_type);
118   }
119 };
120
121 template<>
122 struct Converter<JumpListItem> {
123   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
124                      JumpListItem* out) {
125     mate::Dictionary dict;
126     if (!ConvertFromV8(isolate, val, &dict))
127       return false;
128
129     if (!dict.Get("type", &(out->type)))
130       return false;
131
132     switch (out->type) {
133       case JumpListItem::Type::TASK:
134         if (!dict.Get("program", &(out->path)) ||
135             !dict.Get("title", &(out->title)))
136           return false;
137
138         if (dict.Get("iconPath", &(out->icon_path)) &&
139             !dict.Get("iconIndex", &(out->icon_index)))
140           return false;
141
142         dict.Get("args", &(out->arguments));
143         dict.Get("description", &(out->description));
144         return true;
145
146       case JumpListItem::Type::SEPARATOR:
147         return true;
148
149       case JumpListItem::Type::FILE:
150         return dict.Get("path", &(out->path));
151     }
152
153     assert(false);
154     return false;
155   }
156
157   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
158                                    const JumpListItem& val) {
159     mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
160     dict.Set("type", val.type);
161
162     switch (val.type) {
163       case JumpListItem::Type::TASK:
164         dict.Set("program", val.path);
165         dict.Set("args", val.arguments);
166         dict.Set("title", val.title);
167         dict.Set("iconPath", val.icon_path);
168         dict.Set("iconIndex", val.icon_index);
169         dict.Set("description", val.description);
170         break;
171
172       case JumpListItem::Type::SEPARATOR:
173         break;
174
175       case JumpListItem::Type::FILE:
176         dict.Set("path", val.path);
177         break;
178     }
179     return dict.GetHandle();
180   }
181 };
182
183 template<>
184 struct Converter<JumpListCategory::Type> {
185   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
186                      JumpListCategory::Type* out) {
187     std::string category_type;
188     if (!ConvertFromV8(isolate, val, &category_type))
189       return false;
190
191     if (category_type == "tasks")
192       *out = JumpListCategory::Type::TASKS;
193     else if (category_type == "frequent")
194       *out = JumpListCategory::Type::FREQUENT;
195     else if (category_type == "recent")
196       *out = JumpListCategory::Type::RECENT;
197     else if (category_type == "custom")
198       *out = JumpListCategory::Type::CUSTOM;
199     else
200       return false;
201
202     return true;
203   }
204
205   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
206                                    JumpListCategory::Type val) {
207     std::string category_type;
208     switch (val) {
209       case JumpListCategory::Type::TASKS:
210         category_type = "tasks";
211         break;
212
213       case JumpListCategory::Type::FREQUENT:
214         category_type = "frequent";
215         break;
216
217       case JumpListCategory::Type::RECENT:
218         category_type = "recent";
219         break;
220
221       case JumpListCategory::Type::CUSTOM:
222         category_type = "custom";
223         break;
224     }
225     return mate::ConvertToV8(isolate, category_type);
226   }
227 };
228
229 template<>
230 struct Converter<JumpListCategory> {
231   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
232                      JumpListCategory* out) {
233     mate::Dictionary dict;
234     if (!ConvertFromV8(isolate, val, &dict))
235       return false;
236
237     if (dict.Get("name", &(out->name)) && out->name.empty())
238       return false;
239
240     if (!dict.Get("type", &(out->type))) {
241       if (out->name.empty())
242         out->type = JumpListCategory::Type::TASKS;
243       else
244         out->type = JumpListCategory::Type::CUSTOM;
245     }
246
247     if ((out->type == JumpListCategory::Type::TASKS) ||
248         (out->type == JumpListCategory::Type::CUSTOM)) {
249       if (!dict.Get("items", &(out->items)))
250         return false;
251     }
252
253     return true;
254   }
255 };
256
257 // static
258 template<>
259 struct Converter<JumpListResult> {
260   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, JumpListResult val) {
261     std::string result_code;
262     switch (val) {
263       case JumpListResult::SUCCESS:
264         result_code = "ok";
265         break;
266
267       case JumpListResult::ARGUMENT_ERROR:
268         result_code = "argumentError";
269         break;
270
271       case JumpListResult::GENERIC_ERROR:
272         result_code = "error";
273         break;
274
275       case JumpListResult::CUSTOM_CATEGORY_SEPARATOR_ERROR:
276         result_code = "invalidSeparatorError";
277         break;
278
279       case JumpListResult::MISSING_FILE_TYPE_REGISTRATION_ERROR:
280         result_code = "fileTypeRegistrationError";
281         break;
282
283       case JumpListResult::CUSTOM_CATEGORY_ACCESS_DENIED_ERROR:
284         result_code = "customCategoryAccessDeniedError";
285         break;
286     }
287     return ConvertToV8(isolate, result_code);
288   }
289 };
290 #endif
291
292 template<>
293 struct Converter<Browser::LoginItemSettings> {
294   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
295                      Browser::LoginItemSettings* out) {
296     mate::Dictionary dict;
297     if (!ConvertFromV8(isolate, val, &dict))
298       return false;
299
300     dict.Get("openAtLogin", &(out->open_at_login));
301     dict.Get("openAsHidden", &(out->open_as_hidden));
302     dict.Get("path", &(out->path));
303     dict.Get("args", &(out->args));
304     return true;
305   }
306
307   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
308                                    Browser::LoginItemSettings val) {
309     mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
310     dict.Set("openAtLogin", val.open_at_login);
311     dict.Set("openAsHidden", val.open_as_hidden);
312     dict.Set("restoreState", val.restore_state);
313     dict.Set("wasOpenedAtLogin", val.opened_at_login);
314     dict.Set("wasOpenedAsHidden", val.opened_as_hidden);
315     return dict.GetHandle();
316   }
317 };
318
319 template<>
320 struct Converter<content::CertificateRequestResultType> {
321   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
322                      content::CertificateRequestResultType* out) {
323     bool b;
324     if (!ConvertFromV8(isolate, val, &b))
325       return false;
326     *out = b ? content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE :
327                content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL;
328     return true;
329   }
330 };
331
332 }  // namespace mate
333
334 namespace atom {
335
336 namespace api {
337
338 namespace {
339
340 IconLoader::IconSize GetIconSizeByString(const std::string& size) {
341   if (size == "small") {
342     return IconLoader::IconSize::SMALL;
343   } else if (size == "large") {
344     return IconLoader::IconSize::LARGE;
345   }
346   return IconLoader::IconSize::NORMAL;
347 }
348
349 // Return the path constant from string.
350 int GetPathConstant(const std::string& name) {
351   if (name == "appData")
352     return brightray::DIR_APP_DATA;
353   else if (name == "userData")
354     return brightray::DIR_USER_DATA;
355   else if (name == "cache")
356     return brightray::DIR_CACHE;
357   else if (name == "userCache")
358     return brightray::DIR_USER_CACHE;
359   else if (name == "home")
360     return base::DIR_HOME;
361   else if (name == "temp")
362     return base::DIR_TEMP;
363   else if (name == "userDesktop" || name == "desktop")
364     return base::DIR_USER_DESKTOP;
365   else if (name == "exe")
366     return base::FILE_EXE;
367   else if (name == "module")
368     return base::FILE_MODULE;
369   else if (name == "documents")
370     return chrome::DIR_USER_DOCUMENTS;
371   else if (name == "downloads")
372     return chrome::DIR_DEFAULT_DOWNLOADS;
373   else if (name == "music")
374     return chrome::DIR_USER_MUSIC;
375   else if (name == "pictures")
376     return chrome::DIR_USER_PICTURES;
377   else if (name == "videos")
378     return chrome::DIR_USER_VIDEOS;
379   else if (name == "pepperFlashSystemPlugin")
380     return chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN;
381   else
382     return -1;
383 }
384
385 bool NotificationCallbackWrapper(
386     const ProcessSingleton::NotificationCallback& callback,
387     const base::CommandLine::StringVector& cmd,
388     const base::FilePath& cwd) {
389   // Make sure the callback is called after app gets ready.
390   if (Browser::Get()->is_ready()) {
391     callback.Run(cmd, cwd);
392   } else {
393     scoped_refptr<base::SingleThreadTaskRunner> task_runner(
394         base::ThreadTaskRunnerHandle::Get());
395     task_runner->PostTask(
396         FROM_HERE, base::Bind(base::IgnoreResult(callback), cmd, cwd));
397   }
398   // ProcessSingleton needs to know whether current process is quiting.
399   return !Browser::Get()->is_shutting_down();
400 }
401
402 void OnClientCertificateSelected(
403     v8::Isolate* isolate,
404     std::shared_ptr<content::ClientCertificateDelegate> delegate,
405     mate::Arguments* args) {
406   if (args->Length() == 2) {
407     delegate->ContinueWithCertificate(nullptr);
408     return;
409   }
410
411   v8::Local<v8::Value> val;
412   args->GetNext(&val);
413   if (val->IsNull()) {
414     delegate->ContinueWithCertificate(nullptr);
415     return;
416   }
417
418   mate::Dictionary cert_data;
419   if (!mate::ConvertFromV8(isolate, val, &cert_data)) {
420     args->ThrowError("Must pass valid certificate object.");
421     return;
422   }
423
424   std::string data;
425   if (!cert_data.Get("data", &data))
426     return;
427
428   auto certs = net::X509Certificate::CreateCertificateListFromBytes(
429       data.c_str(), data.length(), net::X509Certificate::FORMAT_AUTO);
430   if (!certs.empty())
431     delegate->ContinueWithCertificate(certs[0].get());
432 }
433
434 void PassLoginInformation(scoped_refptr<LoginHandler> login_handler,
435                           mate::Arguments* args) {
436   base::string16 username, password;
437   if (args->GetNext(&username) && args->GetNext(&password))
438     login_handler->Login(username, password);
439   else
440     login_handler->CancelAuth();
441 }
442
443 #if defined(USE_NSS_CERTS)
444 int ImportIntoCertStore(
445     CertificateManagerModel* model,
446     const base::DictionaryValue& options) {
447   std::string file_data, cert_path;
448   base::string16 password;
449   net::CertificateList imported_certs;
450   int rv = -1;
451   options.GetString("certificate", &cert_path);
452   options.GetString("password", &password);
453
454   if (!cert_path.empty()) {
455     if (base::ReadFileToString(base::FilePath(cert_path), &file_data)) {
456       auto module = model->cert_db()->GetPublicModule();
457       rv = model->ImportFromPKCS12(module,
458                                    file_data,
459                                    password,
460                                    true,
461                                    &imported_certs);
462       if (imported_certs.size() > 1) {
463         auto it = imported_certs.begin();
464         ++it;  // skip first which would  be the client certificate.
465         for (; it != imported_certs.end(); ++it)
466           rv &= model->SetCertTrust(it->get(),
467                                     net::CA_CERT,
468                                     net::NSSCertDatabase::TRUSTED_SSL);
469       }
470     }
471   }
472   return rv;
473 }
474 #endif
475
476 void OnIconDataAvailable(v8::Isolate* isolate,
477                          const App::FileIconCallback& callback,
478                          gfx::Image* icon) {
479   v8::Locker locker(isolate);
480   v8::HandleScope handle_scope(isolate);
481
482   if (icon && !icon->IsEmpty()) {
483     callback.Run(v8::Null(isolate), *icon);
484   } else {
485     v8::Local<v8::String> error_message =
486       v8::String::NewFromUtf8(isolate, "Failed to get file icon.");
487     callback.Run(v8::Exception::Error(error_message), gfx::Image());
488   }
489 }
490
491 }  // namespace
492
493 App::App(v8::Isolate* isolate) {
494   if (AtomBrowserClient::Get())
495     static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(this);
496   Browser::Get()->AddObserver(this);
497   content::GpuDataManager::GetInstance()->AddObserver(this);
498   Init(isolate);
499 }
500
501 App::~App() {
502   static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(
503       nullptr);
504   Browser::Get()->RemoveObserver(this);
505   content::GpuDataManager::GetInstance()->RemoveObserver(this);
506 }
507
508 void App::OnBeforeQuit(bool* prevent_default) {
509   *prevent_default = Emit("before-quit");
510 }
511
512 void App::OnWillQuit(bool* prevent_default) {
513   *prevent_default = Emit("will-quit");
514 }
515
516 void App::OnWindowAllClosed() {
517   Emit("window-all-closed");
518 }
519
520 void App::OnQuit() {
521   int exitCode = AtomBrowserMainParts::Get()->GetExitCode();
522   Emit("quit", exitCode);
523
524   if (process_singleton_) {
525     process_singleton_->Cleanup();
526     process_singleton_.reset();
527   }
528 }
529
530 void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
531   *prevent_default = Emit("open-file", file_path);
532 }
533
534 void App::OnOpenURL(const std::string& url) {
535   Emit("open-url", url);
536 }
537
538 void App::OnActivate(bool has_visible_windows) {
539   Emit("activate", has_visible_windows);
540 }
541
542 void App::OnWillFinishLaunching() {
543   Emit("will-finish-launching");
544 }
545
546 void App::OnFinishLaunching(const base::DictionaryValue& launch_info) {
547 #if defined(OS_LINUX)
548   // Set the application name for audio streams shown in external
549   // applications. Only affects pulseaudio currently.
550   media::AudioManager::SetGlobalAppName(Browser::Get()->GetName());
551 #endif
552   Emit("ready", launch_info);
553 }
554
555 void App::OnAccessibilitySupportChanged() {
556   Emit("accessibility-support-changed", IsAccessibilitySupportEnabled());
557 }
558
559 #if defined(OS_MACOSX)
560 void App::OnContinueUserActivity(
561     bool* prevent_default,
562     const std::string& type,
563     const base::DictionaryValue& user_info) {
564   *prevent_default = Emit("continue-activity", type, user_info);
565 }
566 #endif
567
568 void App::OnLogin(LoginHandler* login_handler,
569                   const base::DictionaryValue& request_details) {
570   v8::Locker locker(isolate());
571   v8::HandleScope handle_scope(isolate());
572   bool prevent_default = Emit(
573       "login",
574       WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
575       request_details,
576       login_handler->auth_info(),
577       base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
578
579   // Default behavior is to always cancel the auth.
580   if (!prevent_default)
581     login_handler->CancelAuth();
582 }
583
584 void App::OnCreateWindow(
585     const GURL& target_url,
586     const std::string& frame_name,
587     WindowOpenDisposition disposition,
588     const std::vector<std::string>& features,
589     const scoped_refptr<content::ResourceRequestBodyImpl>& body,
590     int render_process_id,
591     int render_frame_id) {
592   v8::Locker locker(isolate());
593   v8::HandleScope handle_scope(isolate());
594   content::RenderFrameHost* rfh =
595       content::RenderFrameHost::FromID(render_process_id, render_frame_id);
596   content::WebContents* web_contents =
597       content::WebContents::FromRenderFrameHost(rfh);
598   if (web_contents) {
599     auto api_web_contents = WebContents::CreateFrom(isolate(), web_contents);
600     api_web_contents->OnCreateWindow(target_url,
601                                      frame_name,
602                                      disposition,
603                                      features,
604                                      body);
605   }
606 }
607
608 void App::AllowCertificateError(
609     content::WebContents* web_contents,
610     int cert_error,
611     const net::SSLInfo& ssl_info,
612     const GURL& request_url,
613     content::ResourceType resource_type,
614     bool overridable,
615     bool strict_enforcement,
616     bool expired_previous_decision,
617     const base::Callback<void(content::CertificateRequestResultType)>&
618         callback) {
619   v8::Locker locker(isolate());
620   v8::HandleScope handle_scope(isolate());
621   bool prevent_default = Emit("certificate-error",
622                               WebContents::CreateFrom(isolate(), web_contents),
623                               request_url,
624                               net::ErrorToString(cert_error),
625                               ssl_info.cert,
626                               callback);
627
628   // Deny the certificate by default.
629   if (!prevent_default)
630     callback.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
631 }
632
633 void App::SelectClientCertificate(
634     content::WebContents* web_contents,
635     net::SSLCertRequestInfo* cert_request_info,
636     std::unique_ptr<content::ClientCertificateDelegate> delegate) {
637   std::shared_ptr<content::ClientCertificateDelegate>
638       shared_delegate(delegate.release());
639   bool prevent_default =
640       Emit("select-client-certificate",
641            WebContents::CreateFrom(isolate(), web_contents),
642            cert_request_info->host_and_port.ToString(),
643            cert_request_info->client_certs,
644            base::Bind(&OnClientCertificateSelected,
645                       isolate(),
646                       shared_delegate));
647
648   // Default to first certificate from the platform store.
649   if (!prevent_default)
650     shared_delegate->ContinueWithCertificate(
651         cert_request_info->client_certs[0].get());
652 }
653
654 void App::OnGpuProcessCrashed(base::TerminationStatus status) {
655   Emit("gpu-process-crashed",
656     status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
657 }
658
659 base::FilePath App::GetAppPath() const {
660   return app_path_;
661 }
662
663 void App::SetAppPath(const base::FilePath& app_path) {
664   app_path_ = app_path;
665 }
666
667 base::FilePath App::GetPath(mate::Arguments* args, const std::string& name) {
668   bool succeed = false;
669   base::FilePath path;
670   int key = GetPathConstant(name);
671   if (key >= 0)
672     succeed = PathService::Get(key, &path);
673   if (!succeed)
674     args->ThrowError("Failed to get '" + name + "' path");
675   return path;
676 }
677
678 void App::SetPath(mate::Arguments* args,
679                   const std::string& name,
680                   const base::FilePath& path) {
681   if (!path.IsAbsolute()) {
682     args->ThrowError("Path must be absolute");
683     return;
684   }
685
686   bool succeed = false;
687   int key = GetPathConstant(name);
688   if (key >= 0)
689     succeed = PathService::OverrideAndCreateIfNeeded(key, path, true, false);
690   if (!succeed)
691     args->ThrowError("Failed to set path");
692 }
693
694 void App::SetDesktopName(const std::string& desktop_name) {
695 #if defined(OS_LINUX)
696   std::unique_ptr<base::Environment> env(base::Environment::Create());
697   env->SetVar("CHROME_DESKTOP", desktop_name);
698 #endif
699 }
700
701 std::string App::GetLocale() {
702   return l10n_util::GetApplicationLocale("");
703 }
704
705 bool App::MakeSingleInstance(
706     const ProcessSingleton::NotificationCallback& callback) {
707   if (process_singleton_)
708     return false;
709
710   base::FilePath user_dir;
711   PathService::Get(brightray::DIR_USER_DATA, &user_dir);
712   process_singleton_.reset(new ProcessSingleton(
713       user_dir, base::Bind(NotificationCallbackWrapper, callback)));
714
715   switch (process_singleton_->NotifyOtherProcessOrCreate()) {
716     case ProcessSingleton::NotifyResult::LOCK_ERROR:
717     case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
718     case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
719       process_singleton_.reset();
720       return true;
721     case ProcessSingleton::NotifyResult::PROCESS_NONE:
722     default:  // Shouldn't be needed, but VS warns if it is not there.
723       return false;
724   }
725 }
726
727 void App::ReleaseSingleInstance() {
728   if (process_singleton_) {
729     process_singleton_->Cleanup();
730     process_singleton_.reset();
731   }
732 }
733
734 bool App::Relaunch(mate::Arguments* js_args) {
735   // Parse parameters.
736   bool override_argv = false;
737   base::FilePath exec_path;
738   relauncher::StringVector args;
739
740   mate::Dictionary options;
741   if (js_args->GetNext(&options)) {
742     if (options.Get("execPath", &exec_path) | options.Get("args", &args))
743       override_argv = true;
744   }
745
746   if (!override_argv) {
747 #if defined(OS_WIN)
748     const relauncher::StringVector& argv = atom::AtomCommandLine::wargv();
749 #else
750     const relauncher::StringVector& argv = atom::AtomCommandLine::argv();
751 #endif
752     return relauncher::RelaunchApp(argv);
753   }
754
755   relauncher::StringVector argv;
756   argv.reserve(1 + args.size());
757
758   if (exec_path.empty()) {
759     base::FilePath current_exe_path;
760     PathService::Get(base::FILE_EXE, &current_exe_path);
761     argv.push_back(current_exe_path.value());
762   } else {
763     argv.push_back(exec_path.value());
764   }
765
766   argv.insert(argv.end(), args.begin(), args.end());
767
768   return relauncher::RelaunchApp(argv);
769 }
770
771 void App::DisableHardwareAcceleration(mate::Arguments* args) {
772   if (Browser::Get()->is_ready()) {
773     args->ThrowError("app.disableHardwareAcceleration() can only be called "
774                      "before app is ready");
775     return;
776   }
777   content::GpuDataManager::GetInstance()->DisableHardwareAcceleration();
778 }
779
780 bool App::IsAccessibilitySupportEnabled() {
781   auto ax_state = content::BrowserAccessibilityState::GetInstance();
782   return ax_state->IsAccessibleBrowser();
783 }
784
785 Browser::LoginItemSettings App::GetLoginItemSettings(mate::Arguments* args) {
786   Browser::LoginItemSettings options;
787   args->GetNext(&options);
788   return Browser::Get()->GetLoginItemSettings(options);
789 }
790
791 #if defined(USE_NSS_CERTS)
792 void App::ImportCertificate(
793     const base::DictionaryValue& options,
794     const net::CompletionCallback& callback) {
795   auto browser_context = AtomBrowserContext::From("", false);
796   if (!certificate_manager_model_) {
797     std::unique_ptr<base::DictionaryValue> copy = options.CreateDeepCopy();
798     CertificateManagerModel::Create(
799         browser_context.get(),
800         base::Bind(&App::OnCertificateManagerModelCreated,
801                    base::Unretained(this),
802                    base::Passed(&copy),
803                    callback));
804     return;
805   }
806
807   int rv = ImportIntoCertStore(certificate_manager_model_.get(), options);
808   callback.Run(rv);
809 }
810
811 void App::OnCertificateManagerModelCreated(
812     std::unique_ptr<base::DictionaryValue> options,
813     const net::CompletionCallback& callback,
814     std::unique_ptr<CertificateManagerModel> model) {
815   certificate_manager_model_ = std::move(model);
816   int rv = ImportIntoCertStore(certificate_manager_model_.get(),
817                                *(options.get()));
818   callback.Run(rv);
819 }
820 #endif
821
822 #if defined(OS_WIN)
823 v8::Local<v8::Value> App::GetJumpListSettings() {
824   JumpList jump_list(Browser::Get()->GetAppUserModelID());
825
826   int min_items = 10;
827   std::vector<JumpListItem> removed_items;
828   if (jump_list.Begin(&min_items, &removed_items)) {
829     // We don't actually want to change anything, so abort the transaction.
830     jump_list.Abort();
831   } else {
832     LOG(ERROR) << "Failed to begin Jump List transaction.";
833   }
834
835   auto dict = mate::Dictionary::CreateEmpty(isolate());
836   dict.Set("minItems", min_items);
837   dict.Set("removedItems", mate::ConvertToV8(isolate(), removed_items));
838   return dict.GetHandle();
839 }
840
841 JumpListResult App::SetJumpList(v8::Local<v8::Value> val,
842                                 mate::Arguments* args) {
843   std::vector<JumpListCategory> categories;
844   bool delete_jump_list = val->IsNull();
845   if (!delete_jump_list &&
846     !mate::ConvertFromV8(args->isolate(), val, &categories)) {
847     args->ThrowError("Argument must be null or an array of categories");
848     return JumpListResult::ARGUMENT_ERROR;
849   }
850
851   JumpList jump_list(Browser::Get()->GetAppUserModelID());
852
853   if (delete_jump_list) {
854     return jump_list.Delete()
855       ? JumpListResult::SUCCESS
856       : JumpListResult::GENERIC_ERROR;
857   }
858
859   // Start a transaction that updates the JumpList of this application.
860   if (!jump_list.Begin())
861     return JumpListResult::GENERIC_ERROR;
862
863   JumpListResult result = jump_list.AppendCategories(categories);
864   // AppendCategories may have failed to add some categories, but it's better
865   // to have something than nothing so try to commit the changes anyway.
866   if (!jump_list.Commit()) {
867     LOG(ERROR) << "Failed to commit changes to custom Jump List.";
868     // It's more useful to return the earlier error code that might give
869     // some indication as to why the transaction actually failed, so don't
870     // overwrite it with a "generic error" code here.
871     if (result == JumpListResult::SUCCESS)
872       result = JumpListResult::GENERIC_ERROR;
873   }
874
875   return result;
876 }
877 #endif  // defined(OS_WIN)
878
879 void App::GetFileIcon(const base::FilePath& path,
880                       mate::Arguments* args) {
881   mate::Dictionary options;
882   IconLoader::IconSize icon_size;
883   FileIconCallback callback;
884
885   v8::Locker locker(isolate());
886   v8::HandleScope handle_scope(isolate());
887
888   base::FilePath normalized_path = path.NormalizePathSeparators();
889
890   if (!args->GetNext(&options)) {
891     icon_size = IconLoader::IconSize::NORMAL;
892   } else {
893     std::string icon_size_string;
894     options.Get("size", &icon_size_string);
895     icon_size = GetIconSizeByString(icon_size_string);
896   }
897
898   if (!args->GetNext(&callback)) {
899     args->ThrowError("Missing required callback function");
900     return;
901   }
902
903   auto icon_manager = g_browser_process->GetIconManager();
904   gfx::Image* icon =
905       icon_manager->LookupIconFromFilepath(normalized_path, icon_size);
906   if (icon) {
907     callback.Run(v8::Null(isolate()), *icon);
908   } else {
909     icon_manager->LoadIcon(
910         normalized_path, icon_size,
911         base::Bind(&OnIconDataAvailable, isolate(), callback),
912         &cancelable_task_tracker_);
913   }
914 }
915
916 // static
917 mate::Handle<App> App::Create(v8::Isolate* isolate) {
918   return mate::CreateHandle(isolate, new App(isolate));
919 }
920
921 // static
922 void App::BuildPrototype(
923     v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> prototype) {
924   prototype->SetClassName(mate::StringToV8(isolate, "App"));
925   auto browser = base::Unretained(Browser::Get());
926   mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
927       .SetMethod("quit", base::Bind(&Browser::Quit, browser))
928       .SetMethod("exit", base::Bind(&Browser::Exit, browser))
929       .SetMethod("focus", base::Bind(&Browser::Focus, browser))
930       .SetMethod("getVersion", base::Bind(&Browser::GetVersion, browser))
931       .SetMethod("setVersion", base::Bind(&Browser::SetVersion, browser))
932       .SetMethod("getName", base::Bind(&Browser::GetName, browser))
933       .SetMethod("setName", base::Bind(&Browser::SetName, browser))
934       .SetMethod("isReady", base::Bind(&Browser::is_ready, browser))
935       .SetMethod("addRecentDocument",
936                  base::Bind(&Browser::AddRecentDocument, browser))
937       .SetMethod("clearRecentDocuments",
938                  base::Bind(&Browser::ClearRecentDocuments, browser))
939       .SetMethod("setAppUserModelId",
940                  base::Bind(&Browser::SetAppUserModelID, browser))
941       .SetMethod("isDefaultProtocolClient",
942                  base::Bind(&Browser::IsDefaultProtocolClient, browser))
943       .SetMethod("setAsDefaultProtocolClient",
944                  base::Bind(&Browser::SetAsDefaultProtocolClient, browser))
945       .SetMethod("removeAsDefaultProtocolClient",
946                  base::Bind(&Browser::RemoveAsDefaultProtocolClient, browser))
947       .SetMethod("setBadgeCount", base::Bind(&Browser::SetBadgeCount, browser))
948       .SetMethod("getBadgeCount", base::Bind(&Browser::GetBadgeCount, browser))
949       .SetMethod("getLoginItemSettings", &App::GetLoginItemSettings)
950       .SetMethod("setLoginItemSettings",
951                  base::Bind(&Browser::SetLoginItemSettings, browser))
952 #if defined(OS_MACOSX)
953       .SetMethod("hide", base::Bind(&Browser::Hide, browser))
954       .SetMethod("show", base::Bind(&Browser::Show, browser))
955       .SetMethod("setUserActivity",
956                  base::Bind(&Browser::SetUserActivity, browser))
957       .SetMethod("getCurrentActivityType",
958                  base::Bind(&Browser::GetCurrentActivityType, browser))
959       .SetMethod("setAboutPanelOptions",
960                  base::Bind(&Browser::SetAboutPanelOptions, browser))
961 #endif
962 #if defined(OS_WIN)
963       .SetMethod("setUserTasks", base::Bind(&Browser::SetUserTasks, browser))
964       .SetMethod("getJumpListSettings", &App::GetJumpListSettings)
965       .SetMethod("setJumpList", &App::SetJumpList)
966 #endif
967 #if defined(OS_LINUX)
968       .SetMethod("isUnityRunning",
969                  base::Bind(&Browser::IsUnityRunning, browser))
970 #endif
971       .SetMethod("setAppPath", &App::SetAppPath)
972       .SetMethod("getAppPath", &App::GetAppPath)
973       .SetMethod("setPath", &App::SetPath)
974       .SetMethod("getPath", &App::GetPath)
975       .SetMethod("setDesktopName", &App::SetDesktopName)
976       .SetMethod("getLocale", &App::GetLocale)
977 #if defined(USE_NSS_CERTS)
978       .SetMethod("importCertificate", &App::ImportCertificate)
979 #endif
980       .SetMethod("makeSingleInstance", &App::MakeSingleInstance)
981       .SetMethod("releaseSingleInstance", &App::ReleaseSingleInstance)
982       .SetMethod("relaunch", &App::Relaunch)
983       .SetMethod("isAccessibilitySupportEnabled",
984                  &App::IsAccessibilitySupportEnabled)
985       .SetMethod("disableHardwareAcceleration",
986                  &App::DisableHardwareAcceleration)
987       .SetMethod("getFileIcon", &App::GetFileIcon);
988 }
989
990 }  // namespace api
991
992 }  // namespace atom
993
994
995 namespace {
996
997 void AppendSwitch(const std::string& switch_string, mate::Arguments* args) {
998   auto command_line = base::CommandLine::ForCurrentProcess();
999
1000   if (base::EndsWith(switch_string, "-path",
1001                      base::CompareCase::INSENSITIVE_ASCII) ||
1002       switch_string == switches::kLogNetLog) {
1003     base::FilePath path;
1004     args->GetNext(&path);
1005     command_line->AppendSwitchPath(switch_string, path);
1006     return;
1007   }
1008
1009   std::string value;
1010   if (args->GetNext(&value))
1011     command_line->AppendSwitchASCII(switch_string, value);
1012   else
1013     command_line->AppendSwitch(switch_string);
1014 }
1015
1016 #if defined(OS_MACOSX)
1017 int DockBounce(const std::string& type) {
1018   int request_id = -1;
1019   if (type == "critical")
1020     request_id = Browser::Get()->DockBounce(Browser::BOUNCE_CRITICAL);
1021   else if (type == "informational")
1022     request_id = Browser::Get()->DockBounce(Browser::BOUNCE_INFORMATIONAL);
1023   return request_id;
1024 }
1025
1026 void DockSetMenu(atom::api::Menu* menu) {
1027   Browser::Get()->DockSetMenu(menu->model());
1028 }
1029 #endif
1030
1031 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
1032                 v8::Local<v8::Context> context, void* priv) {
1033   v8::Isolate* isolate = context->GetIsolate();
1034   auto command_line = base::CommandLine::ForCurrentProcess();
1035
1036   mate::Dictionary dict(isolate, exports);
1037   dict.Set("App", atom::api::App::GetConstructor(isolate)->GetFunction());
1038   dict.Set("app", atom::api::App::Create(isolate));
1039   dict.SetMethod("appendSwitch", &AppendSwitch);
1040   dict.SetMethod("appendArgument",
1041                  base::Bind(&base::CommandLine::AppendArg,
1042                             base::Unretained(command_line)));
1043 #if defined(OS_MACOSX)
1044   auto browser = base::Unretained(Browser::Get());
1045   dict.SetMethod("dockBounce", &DockBounce);
1046   dict.SetMethod("dockCancelBounce",
1047                  base::Bind(&Browser::DockCancelBounce, browser));
1048   dict.SetMethod("dockDownloadFinished",
1049                  base::Bind(&Browser::DockDownloadFinished, browser));
1050   dict.SetMethod("dockSetBadgeText",
1051                  base::Bind(&Browser::DockSetBadgeText, browser));
1052   dict.SetMethod("dockGetBadgeText",
1053                  base::Bind(&Browser::DockGetBadgeText, browser));
1054   dict.SetMethod("dockHide", base::Bind(&Browser::DockHide, browser));
1055   dict.SetMethod("dockShow", base::Bind(&Browser::DockShow, browser));
1056   dict.SetMethod("dockIsVisible", base::Bind(&Browser::DockIsVisible, browser));
1057   dict.SetMethod("dockSetMenu", &DockSetMenu);
1058   dict.SetMethod("dockSetIcon", base::Bind(&Browser::DockSetIcon, browser));
1059 #endif
1060 }
1061
1062 }  // namespace
1063
1064 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_app, Initialize)