115041d0082d09cb30bf944a814f149abe900a17
[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   static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(this);
495   Browser::Get()->AddObserver(this);
496   content::GpuDataManager::GetInstance()->AddObserver(this);
497   Init(isolate);
498 }
499
500 App::~App() {
501   static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(
502       nullptr);
503   Browser::Get()->RemoveObserver(this);
504   content::GpuDataManager::GetInstance()->RemoveObserver(this);
505 }
506
507 void App::OnBeforeQuit(bool* prevent_default) {
508   *prevent_default = Emit("before-quit");
509 }
510
511 void App::OnWillQuit(bool* prevent_default) {
512   *prevent_default = Emit("will-quit");
513 }
514
515 void App::OnWindowAllClosed() {
516   Emit("window-all-closed");
517 }
518
519 void App::OnQuit() {
520   int exitCode = AtomBrowserMainParts::Get()->GetExitCode();
521   Emit("quit", exitCode);
522
523   if (process_singleton_) {
524     process_singleton_->Cleanup();
525     process_singleton_.reset();
526   }
527 }
528
529 void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
530   *prevent_default = Emit("open-file", file_path);
531 }
532
533 void App::OnOpenURL(const std::string& url) {
534   Emit("open-url", url);
535 }
536
537 void App::OnActivate(bool has_visible_windows) {
538   Emit("activate", has_visible_windows);
539 }
540
541 void App::OnWillFinishLaunching() {
542   Emit("will-finish-launching");
543 }
544
545 void App::OnFinishLaunching(const base::DictionaryValue& launch_info) {
546 #if defined(OS_LINUX)
547   // Set the application name for audio streams shown in external
548   // applications. Only affects pulseaudio currently.
549   media::AudioManager::SetGlobalAppName(Browser::Get()->GetName());
550 #endif
551   Emit("ready", launch_info);
552 }
553
554 void App::OnAccessibilitySupportChanged() {
555   Emit("accessibility-support-changed", IsAccessibilitySupportEnabled());
556 }
557
558 #if defined(OS_MACOSX)
559 void App::OnContinueUserActivity(
560     bool* prevent_default,
561     const std::string& type,
562     const base::DictionaryValue& user_info) {
563   *prevent_default = Emit("continue-activity", type, user_info);
564 }
565 #endif
566
567 void App::OnLogin(LoginHandler* login_handler,
568                   const base::DictionaryValue& request_details) {
569   v8::Locker locker(isolate());
570   v8::HandleScope handle_scope(isolate());
571   bool prevent_default = Emit(
572       "login",
573       WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
574       request_details,
575       login_handler->auth_info(),
576       base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
577
578   // Default behavior is to always cancel the auth.
579   if (!prevent_default)
580     login_handler->CancelAuth();
581 }
582
583 void App::OnCreateWindow(
584     const GURL& target_url,
585     const std::string& frame_name,
586     WindowOpenDisposition disposition,
587     const std::vector<std::string>& features,
588     const scoped_refptr<content::ResourceRequestBodyImpl>& body,
589     int render_process_id,
590     int render_frame_id) {
591   v8::Locker locker(isolate());
592   v8::HandleScope handle_scope(isolate());
593   content::RenderFrameHost* rfh =
594       content::RenderFrameHost::FromID(render_process_id, render_frame_id);
595   content::WebContents* web_contents =
596       content::WebContents::FromRenderFrameHost(rfh);
597   if (web_contents) {
598     auto api_web_contents = WebContents::CreateFrom(isolate(), web_contents);
599     api_web_contents->OnCreateWindow(target_url,
600                                      frame_name,
601                                      disposition,
602                                      features,
603                                      body);
604   }
605 }
606
607 void App::AllowCertificateError(
608     content::WebContents* web_contents,
609     int cert_error,
610     const net::SSLInfo& ssl_info,
611     const GURL& request_url,
612     content::ResourceType resource_type,
613     bool overridable,
614     bool strict_enforcement,
615     bool expired_previous_decision,
616     const base::Callback<void(content::CertificateRequestResultType)>&
617         callback) {
618   v8::Locker locker(isolate());
619   v8::HandleScope handle_scope(isolate());
620   bool prevent_default = Emit("certificate-error",
621                               WebContents::CreateFrom(isolate(), web_contents),
622                               request_url,
623                               net::ErrorToString(cert_error),
624                               ssl_info.cert,
625                               callback);
626
627   // Deny the certificate by default.
628   if (!prevent_default)
629     callback.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
630 }
631
632 void App::SelectClientCertificate(
633     content::WebContents* web_contents,
634     net::SSLCertRequestInfo* cert_request_info,
635     std::unique_ptr<content::ClientCertificateDelegate> delegate) {
636   std::shared_ptr<content::ClientCertificateDelegate>
637       shared_delegate(delegate.release());
638   bool prevent_default =
639       Emit("select-client-certificate",
640            WebContents::CreateFrom(isolate(), web_contents),
641            cert_request_info->host_and_port.ToString(),
642            cert_request_info->client_certs,
643            base::Bind(&OnClientCertificateSelected,
644                       isolate(),
645                       shared_delegate));
646
647   // Default to first certificate from the platform store.
648   if (!prevent_default)
649     shared_delegate->ContinueWithCertificate(
650         cert_request_info->client_certs[0].get());
651 }
652
653 void App::OnGpuProcessCrashed(base::TerminationStatus status) {
654   Emit("gpu-process-crashed",
655     status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
656 }
657
658 base::FilePath App::GetAppPath() const {
659   return app_path_;
660 }
661
662 void App::SetAppPath(const base::FilePath& app_path) {
663   app_path_ = app_path;
664 }
665
666 base::FilePath App::GetPath(mate::Arguments* args, const std::string& name) {
667   bool succeed = false;
668   base::FilePath path;
669   int key = GetPathConstant(name);
670   if (key >= 0)
671     succeed = PathService::Get(key, &path);
672   if (!succeed)
673     args->ThrowError("Failed to get '" + name + "' path");
674   return path;
675 }
676
677 void App::SetPath(mate::Arguments* args,
678                   const std::string& name,
679                   const base::FilePath& path) {
680   if (!path.IsAbsolute()) {
681     args->ThrowError("Path must be absolute");
682     return;
683   }
684
685   bool succeed = false;
686   int key = GetPathConstant(name);
687   if (key >= 0)
688     succeed = PathService::OverrideAndCreateIfNeeded(key, path, true, false);
689   if (!succeed)
690     args->ThrowError("Failed to set path");
691 }
692
693 void App::SetDesktopName(const std::string& desktop_name) {
694 #if defined(OS_LINUX)
695   std::unique_ptr<base::Environment> env(base::Environment::Create());
696   env->SetVar("CHROME_DESKTOP", desktop_name);
697 #endif
698 }
699
700 std::string App::GetLocale() {
701   return l10n_util::GetApplicationLocale("");
702 }
703
704 bool App::MakeSingleInstance(
705     const ProcessSingleton::NotificationCallback& callback) {
706   if (process_singleton_)
707     return false;
708
709   base::FilePath user_dir;
710   PathService::Get(brightray::DIR_USER_DATA, &user_dir);
711   process_singleton_.reset(new ProcessSingleton(
712       user_dir, base::Bind(NotificationCallbackWrapper, callback)));
713
714   switch (process_singleton_->NotifyOtherProcessOrCreate()) {
715     case ProcessSingleton::NotifyResult::LOCK_ERROR:
716     case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
717     case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
718       process_singleton_.reset();
719       return true;
720     case ProcessSingleton::NotifyResult::PROCESS_NONE:
721     default:  // Shouldn't be needed, but VS warns if it is not there.
722       return false;
723   }
724 }
725
726 void App::ReleaseSingleInstance() {
727   if (process_singleton_) {
728     process_singleton_->Cleanup();
729     process_singleton_.reset();
730   }
731 }
732
733 bool App::Relaunch(mate::Arguments* js_args) {
734   // Parse parameters.
735   bool override_argv = false;
736   base::FilePath exec_path;
737   relauncher::StringVector args;
738
739   mate::Dictionary options;
740   if (js_args->GetNext(&options)) {
741     if (options.Get("execPath", &exec_path) | options.Get("args", &args))
742       override_argv = true;
743   }
744
745   if (!override_argv) {
746 #if defined(OS_WIN)
747     const relauncher::StringVector& argv = atom::AtomCommandLine::wargv();
748 #else
749     const relauncher::StringVector& argv = atom::AtomCommandLine::argv();
750 #endif
751     return relauncher::RelaunchApp(argv);
752   }
753
754   relauncher::StringVector argv;
755   argv.reserve(1 + args.size());
756
757   if (exec_path.empty()) {
758     base::FilePath current_exe_path;
759     PathService::Get(base::FILE_EXE, &current_exe_path);
760     argv.push_back(current_exe_path.value());
761   } else {
762     argv.push_back(exec_path.value());
763   }
764
765   argv.insert(argv.end(), args.begin(), args.end());
766
767   return relauncher::RelaunchApp(argv);
768 }
769
770 void App::DisableHardwareAcceleration(mate::Arguments* args) {
771   if (Browser::Get()->is_ready()) {
772     args->ThrowError("app.disableHardwareAcceleration() can only be called "
773                      "before app is ready");
774     return;
775   }
776   content::GpuDataManager::GetInstance()->DisableHardwareAcceleration();
777 }
778
779 bool App::IsAccessibilitySupportEnabled() {
780   auto ax_state = content::BrowserAccessibilityState::GetInstance();
781   return ax_state->IsAccessibleBrowser();
782 }
783
784 Browser::LoginItemSettings App::GetLoginItemSettings(mate::Arguments* args) {
785   Browser::LoginItemSettings options;
786   args->GetNext(&options);
787   return Browser::Get()->GetLoginItemSettings(options);
788 }
789
790 #if defined(USE_NSS_CERTS)
791 void App::ImportCertificate(
792     const base::DictionaryValue& options,
793     const net::CompletionCallback& callback) {
794   auto browser_context = AtomBrowserContext::From("", false);
795   if (!certificate_manager_model_) {
796     std::unique_ptr<base::DictionaryValue> copy = options.CreateDeepCopy();
797     CertificateManagerModel::Create(
798         browser_context.get(),
799         base::Bind(&App::OnCertificateManagerModelCreated,
800                    base::Unretained(this),
801                    base::Passed(&copy),
802                    callback));
803     return;
804   }
805
806   int rv = ImportIntoCertStore(certificate_manager_model_.get(), options);
807   callback.Run(rv);
808 }
809
810 void App::OnCertificateManagerModelCreated(
811     std::unique_ptr<base::DictionaryValue> options,
812     const net::CompletionCallback& callback,
813     std::unique_ptr<CertificateManagerModel> model) {
814   certificate_manager_model_ = std::move(model);
815   int rv = ImportIntoCertStore(certificate_manager_model_.get(),
816                                *(options.get()));
817   callback.Run(rv);
818 }
819 #endif
820
821 #if defined(OS_WIN)
822 v8::Local<v8::Value> App::GetJumpListSettings() {
823   JumpList jump_list(Browser::Get()->GetAppUserModelID());
824
825   int min_items = 10;
826   std::vector<JumpListItem> removed_items;
827   if (jump_list.Begin(&min_items, &removed_items)) {
828     // We don't actually want to change anything, so abort the transaction.
829     jump_list.Abort();
830   } else {
831     LOG(ERROR) << "Failed to begin Jump List transaction.";
832   }
833
834   auto dict = mate::Dictionary::CreateEmpty(isolate());
835   dict.Set("minItems", min_items);
836   dict.Set("removedItems", mate::ConvertToV8(isolate(), removed_items));
837   return dict.GetHandle();
838 }
839
840 JumpListResult App::SetJumpList(v8::Local<v8::Value> val,
841                                 mate::Arguments* args) {
842   std::vector<JumpListCategory> categories;
843   bool delete_jump_list = val->IsNull();
844   if (!delete_jump_list &&
845     !mate::ConvertFromV8(args->isolate(), val, &categories)) {
846     args->ThrowError("Argument must be null or an array of categories");
847     return JumpListResult::ARGUMENT_ERROR;
848   }
849
850   JumpList jump_list(Browser::Get()->GetAppUserModelID());
851
852   if (delete_jump_list) {
853     return jump_list.Delete()
854       ? JumpListResult::SUCCESS
855       : JumpListResult::GENERIC_ERROR;
856   }
857
858   // Start a transaction that updates the JumpList of this application.
859   if (!jump_list.Begin())
860     return JumpListResult::GENERIC_ERROR;
861
862   JumpListResult result = jump_list.AppendCategories(categories);
863   // AppendCategories may have failed to add some categories, but it's better
864   // to have something than nothing so try to commit the changes anyway.
865   if (!jump_list.Commit()) {
866     LOG(ERROR) << "Failed to commit changes to custom Jump List.";
867     // It's more useful to return the earlier error code that might give
868     // some indication as to why the transaction actually failed, so don't
869     // overwrite it with a "generic error" code here.
870     if (result == JumpListResult::SUCCESS)
871       result = JumpListResult::GENERIC_ERROR;
872   }
873
874   return result;
875 }
876 #endif  // defined(OS_WIN)
877
878 void App::GetFileIcon(const base::FilePath& path,
879                       mate::Arguments* args) {
880   mate::Dictionary options;
881   IconLoader::IconSize icon_size;
882   FileIconCallback callback;
883
884   v8::Locker locker(isolate());
885   v8::HandleScope handle_scope(isolate());
886
887   base::FilePath normalized_path = path.NormalizePathSeparators();
888
889   if (!args->GetNext(&options)) {
890     icon_size = IconLoader::IconSize::NORMAL;
891   } else {
892     std::string icon_size_string;
893     options.Get("size", &icon_size_string);
894     icon_size = GetIconSizeByString(icon_size_string);
895   }
896
897   if (!args->GetNext(&callback)) {
898     args->ThrowError("Missing required callback function");
899     return;
900   }
901
902   auto icon_manager = g_browser_process->GetIconManager();
903   gfx::Image* icon =
904       icon_manager->LookupIconFromFilepath(normalized_path, icon_size);
905   if (icon) {
906     callback.Run(v8::Null(isolate()), *icon);
907   } else {
908     icon_manager->LoadIcon(
909         normalized_path, icon_size,
910         base::Bind(&OnIconDataAvailable, isolate(), callback),
911         &cancelable_task_tracker_);
912   }
913 }
914
915 // static
916 mate::Handle<App> App::Create(v8::Isolate* isolate) {
917   return mate::CreateHandle(isolate, new App(isolate));
918 }
919
920 // static
921 void App::BuildPrototype(
922     v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> prototype) {
923   prototype->SetClassName(mate::StringToV8(isolate, "App"));
924   auto browser = base::Unretained(Browser::Get());
925   mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
926       .SetMethod("quit", base::Bind(&Browser::Quit, browser))
927       .SetMethod("exit", base::Bind(&Browser::Exit, browser))
928       .SetMethod("focus", base::Bind(&Browser::Focus, browser))
929       .SetMethod("getVersion", base::Bind(&Browser::GetVersion, browser))
930       .SetMethod("setVersion", base::Bind(&Browser::SetVersion, browser))
931       .SetMethod("getName", base::Bind(&Browser::GetName, browser))
932       .SetMethod("setName", base::Bind(&Browser::SetName, browser))
933       .SetMethod("isReady", base::Bind(&Browser::is_ready, browser))
934       .SetMethod("addRecentDocument",
935                  base::Bind(&Browser::AddRecentDocument, browser))
936       .SetMethod("clearRecentDocuments",
937                  base::Bind(&Browser::ClearRecentDocuments, browser))
938       .SetMethod("setAppUserModelId",
939                  base::Bind(&Browser::SetAppUserModelID, browser))
940       .SetMethod("isDefaultProtocolClient",
941                  base::Bind(&Browser::IsDefaultProtocolClient, browser))
942       .SetMethod("setAsDefaultProtocolClient",
943                  base::Bind(&Browser::SetAsDefaultProtocolClient, browser))
944       .SetMethod("removeAsDefaultProtocolClient",
945                  base::Bind(&Browser::RemoveAsDefaultProtocolClient, browser))
946       .SetMethod("setBadgeCount", base::Bind(&Browser::SetBadgeCount, browser))
947       .SetMethod("getBadgeCount", base::Bind(&Browser::GetBadgeCount, browser))
948       .SetMethod("getLoginItemSettings", &App::GetLoginItemSettings)
949       .SetMethod("setLoginItemSettings",
950                  base::Bind(&Browser::SetLoginItemSettings, browser))
951 #if defined(OS_MACOSX)
952       .SetMethod("hide", base::Bind(&Browser::Hide, browser))
953       .SetMethod("show", base::Bind(&Browser::Show, browser))
954       .SetMethod("setUserActivity",
955                  base::Bind(&Browser::SetUserActivity, browser))
956       .SetMethod("getCurrentActivityType",
957                  base::Bind(&Browser::GetCurrentActivityType, browser))
958       .SetMethod("setAboutPanelOptions",
959                  base::Bind(&Browser::SetAboutPanelOptions, browser))
960 #endif
961 #if defined(OS_WIN)
962       .SetMethod("setUserTasks", base::Bind(&Browser::SetUserTasks, browser))
963       .SetMethod("getJumpListSettings", &App::GetJumpListSettings)
964       .SetMethod("setJumpList", &App::SetJumpList)
965 #endif
966 #if defined(OS_LINUX)
967       .SetMethod("isUnityRunning",
968                  base::Bind(&Browser::IsUnityRunning, browser))
969 #endif
970       .SetMethod("setAppPath", &App::SetAppPath)
971       .SetMethod("getAppPath", &App::GetAppPath)
972       .SetMethod("setPath", &App::SetPath)
973       .SetMethod("getPath", &App::GetPath)
974       .SetMethod("setDesktopName", &App::SetDesktopName)
975       .SetMethod("getLocale", &App::GetLocale)
976 #if defined(USE_NSS_CERTS)
977       .SetMethod("importCertificate", &App::ImportCertificate)
978 #endif
979       .SetMethod("makeSingleInstance", &App::MakeSingleInstance)
980       .SetMethod("releaseSingleInstance", &App::ReleaseSingleInstance)
981       .SetMethod("relaunch", &App::Relaunch)
982       .SetMethod("isAccessibilitySupportEnabled",
983                  &App::IsAccessibilitySupportEnabled)
984       .SetMethod("disableHardwareAcceleration",
985                  &App::DisableHardwareAcceleration)
986       .SetMethod("getFileIcon", &App::GetFileIcon);
987 }
988
989 }  // namespace api
990
991 }  // namespace atom
992
993
994 namespace {
995
996 void AppendSwitch(const std::string& switch_string, mate::Arguments* args) {
997   auto command_line = base::CommandLine::ForCurrentProcess();
998
999   if (base::EndsWith(switch_string, "-path",
1000                      base::CompareCase::INSENSITIVE_ASCII) ||
1001       switch_string == switches::kLogNetLog) {
1002     base::FilePath path;
1003     args->GetNext(&path);
1004     command_line->AppendSwitchPath(switch_string, path);
1005     return;
1006   }
1007
1008   std::string value;
1009   if (args->GetNext(&value))
1010     command_line->AppendSwitchASCII(switch_string, value);
1011   else
1012     command_line->AppendSwitch(switch_string);
1013 }
1014
1015 #if defined(OS_MACOSX)
1016 int DockBounce(const std::string& type) {
1017   int request_id = -1;
1018   if (type == "critical")
1019     request_id = Browser::Get()->DockBounce(Browser::BOUNCE_CRITICAL);
1020   else if (type == "informational")
1021     request_id = Browser::Get()->DockBounce(Browser::BOUNCE_INFORMATIONAL);
1022   return request_id;
1023 }
1024
1025 void DockSetMenu(atom::api::Menu* menu) {
1026   Browser::Get()->DockSetMenu(menu->model());
1027 }
1028 #endif
1029
1030 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
1031                 v8::Local<v8::Context> context, void* priv) {
1032   v8::Isolate* isolate = context->GetIsolate();
1033   auto command_line = base::CommandLine::ForCurrentProcess();
1034
1035   mate::Dictionary dict(isolate, exports);
1036   dict.Set("App", atom::api::App::GetConstructor(isolate)->GetFunction());
1037   dict.Set("app", atom::api::App::Create(isolate));
1038   dict.SetMethod("appendSwitch", &AppendSwitch);
1039   dict.SetMethod("appendArgument",
1040                  base::Bind(&base::CommandLine::AppendArg,
1041                             base::Unretained(command_line)));
1042 #if defined(OS_MACOSX)
1043   auto browser = base::Unretained(Browser::Get());
1044   dict.SetMethod("dockBounce", &DockBounce);
1045   dict.SetMethod("dockCancelBounce",
1046                  base::Bind(&Browser::DockCancelBounce, browser));
1047   dict.SetMethod("dockDownloadFinished",
1048                  base::Bind(&Browser::DockDownloadFinished, browser));
1049   dict.SetMethod("dockSetBadgeText",
1050                  base::Bind(&Browser::DockSetBadgeText, browser));
1051   dict.SetMethod("dockGetBadgeText",
1052                  base::Bind(&Browser::DockGetBadgeText, browser));
1053   dict.SetMethod("dockHide", base::Bind(&Browser::DockHide, browser));
1054   dict.SetMethod("dockShow", base::Bind(&Browser::DockShow, browser));
1055   dict.SetMethod("dockIsVisible", base::Bind(&Browser::DockIsVisible, browser));
1056   dict.SetMethod("dockSetMenu", &DockSetMenu);
1057   dict.SetMethod("dockSetIcon", base::Bind(&Browser::DockSetIcon, browser));
1058 #endif
1059 }
1060
1061 }  // namespace
1062
1063 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_app, Initialize)