diff --git a/README-ko.md b/README-ko.md index a3dd38124fae..706a5556df9f 100644 --- a/README-ko.md +++ b/README-ko.md @@ -56,6 +56,7 @@ npm install electron-prebuilt --save-dev - [스페인어](https://github.com/electron/electron/tree/master/docs-translations/es) - [중국어 간체](https://github.com/electron/electron/tree/master/docs-translations/zh-CN) - [중국어 번체](https://github.com/electron/electron/tree/master/docs-translations/zh-TW) +- [터키의](https://github.com/electron/electron/tree/master/docs-translations/tr-TR) - [우크라이나어](https://github.com/electron/electron/tree/master/docs-translations/uk-UA) - [러시아어](https://github.com/electron/electron/tree/master/docs-translations/ru-RU) - [프랑스어](https://github.com/electron/electron/tree/master/docs-translations/fr-FR) diff --git a/README.md b/README.md index 52e290f53efb..f8278f18586f 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ contains documents describing how to build and contribute to Electron. - [Spanish](https://github.com/electron/electron/tree/master/docs-translations/es) - [Simplified Chinese](https://github.com/electron/electron/tree/master/docs-translations/zh-CN) - [Traditional Chinese](https://github.com/electron/electron/tree/master/docs-translations/zh-TW) +- [Turkish](https://github.com/electron/electron/tree/master/docs-translations/tr-TR) - [Ukrainian](https://github.com/electron/electron/tree/master/docs-translations/uk-UA) - [Russian](https://github.com/electron/electron/tree/master/docs-translations/ru-RU) - [French](https://github.com/electron/electron/tree/master/docs-translations/fr-FR) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 52d6ec3d338c..304e53a55540 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -15,15 +15,17 @@ #include "atom/browser/browser.h" #include "atom/browser/login_handler.h" #include "atom/common/native_mate_converters/callback.h" -#include "atom/common/native_mate_converters/net_converter.h" #include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/image_converter.h" +#include "atom/common/native_mate_converters/net_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/node_includes.h" #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/environment.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/path_service.h" #include "brightray/browser/brightray_paths.h" #include "chrome/common/chrome_paths.h" @@ -39,7 +41,6 @@ #if defined(OS_WIN) #include "base/strings/utf_string_conversions.h" -#include "ui/base/win/shell.h" #endif using atom::Browser; @@ -157,12 +158,46 @@ void PassLoginInformation(scoped_refptr login_handler, login_handler->CancelAuth(); } +#if defined(USE_NSS_CERTS) +int ImportIntoCertStore( + CertificateManagerModel* model, + const base::DictionaryValue& options) { + std::string file_data, cert_path; + base::string16 password; + net::CertificateList imported_certs; + int rv = -1; + options.GetString("certificate", &cert_path); + options.GetString("password", &password); + + if (!cert_path.empty()) { + if (base::ReadFileToString(base::FilePath(cert_path), &file_data)) { + auto module = model->cert_db()->GetPublicModule(); + rv = model->ImportFromPKCS12(module, + file_data, + password, + true, + &imported_certs); + if (imported_certs.size() > 1) { + auto it = imported_certs.begin(); + ++it; // skip first which would be the client certificate. + for (; it != imported_certs.end(); ++it) + rv &= model->SetCertTrust(it->get(), + net::CA_CERT, + net::NSSCertDatabase::TRUSTED_SSL); + } + } + } + return rv; +} +#endif + } // namespace -App::App() { +App::App(v8::Isolate* isolate) { static_cast(AtomBrowserClient::Get())->set_delegate(this); Browser::Get()->AddObserver(this); content::GpuDataManager::GetInstance()->AddObserver(this); + Init(isolate); } App::~App() { @@ -296,12 +331,6 @@ void App::OnGpuProcessCrashed(base::TerminationStatus exit_code) { Emit("gpu-process-crashed"); } -#if defined(OS_MACOSX) -void App::OnPlatformThemeChanged() { - Emit("platform-theme-changed"); -} -#endif - base::FilePath App::GetPath(mate::Arguments* args, const std::string& name) { bool succeed = false; base::FilePath path; @@ -316,10 +345,15 @@ base::FilePath App::GetPath(mate::Arguments* args, const std::string& name) { void App::SetPath(mate::Arguments* args, const std::string& name, const base::FilePath& path) { + if (!path.IsAbsolute()) { + args->ThrowError("path must be absolute"); + return; + } + bool succeed = false; int key = GetPathConstant(name); if (key >= 0) - succeed = PathService::Override(key, path); + succeed = PathService::OverrideAndCreateIfNeeded(key, path, true, false); if (!succeed) args->ThrowError("Failed to set path"); } @@ -341,12 +375,6 @@ std::string App::GetLocale() { return l10n_util::GetApplicationLocale(""); } -#if defined(OS_WIN) -bool App::IsAeroGlassEnabled() { - return ui::win::IsAeroGlassEnabled(); -} -#endif - bool App::MakeSingleInstance( const ProcessSingleton::NotificationCallback& callback) { if (process_singleton_.get()) @@ -369,10 +397,46 @@ bool App::MakeSingleInstance( } } -mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( - v8::Isolate* isolate) { +#if defined(USE_NSS_CERTS) +void App::ImportCertificate( + const base::DictionaryValue& options, + const net::CompletionCallback& callback) { + auto browser_context = AtomBrowserMainParts::Get()->browser_context(); + if (!certificate_manager_model_) { + scoped_ptr copy = options.CreateDeepCopy(); + CertificateManagerModel::Create(browser_context, + base::Bind(&App::OnCertificateManagerModelCreated, + base::Unretained(this), + base::Passed(©), + callback)); + return; + } + + int rv = ImportIntoCertStore(certificate_manager_model_.get(), options); + callback.Run(rv); +} + +void App::OnCertificateManagerModelCreated( + scoped_ptr options, + const net::CompletionCallback& callback, + scoped_ptr model) { + certificate_manager_model_ = std::move(model); + int rv = ImportIntoCertStore(certificate_manager_model_.get(), + *(options.get())); + callback.Run(rv); +} +#endif + +// static +mate::Handle App::Create(v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new App(isolate)); +} + +// static +void App::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { auto browser = base::Unretained(Browser::Get()); - return mate::ObjectTemplateBuilder(isolate) + mate::ObjectTemplateBuilder(isolate, prototype) .SetMethod("quit", base::Bind(&Browser::Quit, browser)) .SetMethod("exit", base::Bind(&Browser::Exit, browser)) .SetMethod("focus", base::Bind(&Browser::Focus, browser)) @@ -387,6 +451,8 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( base::Bind(&Browser::ClearRecentDocuments, browser)) .SetMethod("setAppUserModelId", base::Bind(&Browser::SetAppUserModelID, browser)) + .SetMethod("isDefaultProtocolClient", + base::Bind(&Browser::IsDefaultProtocolClient, browser)) .SetMethod("setAsDefaultProtocolClient", base::Bind(&Browser::SetAsDefaultProtocolClient, browser)) .SetMethod("removeAsDefaultProtocolClient", @@ -394,13 +460,10 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( #if defined(OS_MACOSX) .SetMethod("hide", base::Bind(&Browser::Hide, browser)) .SetMethod("show", base::Bind(&Browser::Show, browser)) - .SetMethod("isDarkMode", - base::Bind(&Browser::IsDarkMode, browser)) #endif #if defined(OS_WIN) .SetMethod("setUserTasks", base::Bind(&Browser::SetUserTasks, browser)) - .SetMethod("isAeroGlassEnabled", &App::IsAeroGlassEnabled) #endif .SetMethod("setPath", &App::SetPath) .SetMethod("getPath", &App::GetPath) @@ -408,14 +471,12 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( .SetMethod("allowNTLMCredentialsForAllDomains", &App::AllowNTLMCredentialsForAllDomains) .SetMethod("getLocale", &App::GetLocale) +#if defined(USE_NSS_CERTS) + .SetMethod("importCertificate", &App::ImportCertificate) +#endif .SetMethod("makeSingleInstance", &App::MakeSingleInstance); } -// static -mate::Handle App::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new App); -} - } // namespace api } // namespace atom @@ -427,7 +488,6 @@ void AppendSwitch(const std::string& switch_string, mate::Arguments* args) { auto command_line = base::CommandLine::ForCurrentProcess(); if (switch_string == atom::switches::kPpapiFlashPath || - switch_string == atom::switches::kClientCertificate || switch_string == switches::kLogNetLog) { base::FilePath path; args->GetNext(&path); diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 5025a3869dd4..c99d5df77937 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -14,6 +14,11 @@ #include "chrome/browser/process_singleton.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "native_mate/handle.h" +#include "net/base/completion_callback.h" + +#if defined(USE_NSS_CERTS) +#include "chrome/browser/certificate_manager_model.h" +#endif namespace base { class FilePath; @@ -28,12 +33,15 @@ namespace atom { namespace api { class App : public AtomBrowserClient::Delegate, - public mate::EventEmitter, + public mate::EventEmitter, public BrowserObserver, public content::GpuDataManagerObserver { public: static mate::Handle Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + // Called when window with disposition needs to be created. void OnCreateWindow(const GURL& target_url, const std::string& frame_name, @@ -41,9 +49,16 @@ class App : public AtomBrowserClient::Delegate, int render_process_id, int render_frame_id); +#if defined(USE_NSS_CERTS) + void OnCertificateManagerModelCreated( + scoped_ptr options, + const net::CompletionCallback& callback, + scoped_ptr model); +#endif + protected: - App(); - virtual ~App(); + explicit App(v8::Isolate* isolate); + ~App() override; // BrowserObserver: void OnBeforeQuit(bool* prevent_default) override; @@ -77,14 +92,6 @@ class App : public AtomBrowserClient::Delegate, // content::GpuDataManagerObserver: void OnGpuProcessCrashed(base::TerminationStatus exit_code) override; -#if defined(OS_MACOSX) - void OnPlatformThemeChanged() override; -#endif - - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - private: // Get/Set the pre-defined path in PathService. base::FilePath GetPath(mate::Arguments* args, const std::string& name); @@ -98,12 +105,17 @@ class App : public AtomBrowserClient::Delegate, const ProcessSingleton::NotificationCallback& callback); std::string GetLocale(); -#if defined(OS_WIN) - bool IsAeroGlassEnabled(); +#if defined(USE_NSS_CERTS) + void ImportCertificate(const base::DictionaryValue& options, + const net::CompletionCallback& callback); #endif scoped_ptr process_singleton_; +#if defined(USE_NSS_CERTS) + scoped_ptr certificate_manager_model_; +#endif + DISALLOW_COPY_AND_ASSIGN(App); }; diff --git a/atom/browser/api/atom_api_auto_updater.cc b/atom/browser/api/atom_api_auto_updater.cc index 1a02a54d4533..cdf3406ae788 100644 --- a/atom/browser/api/atom_api_auto_updater.cc +++ b/atom/browser/api/atom_api_auto_updater.cc @@ -34,8 +34,9 @@ namespace atom { namespace api { -AutoUpdater::AutoUpdater() { +AutoUpdater::AutoUpdater(v8::Isolate* isolate) { auto_updater::AutoUpdater::SetDelegate(this); + Init(isolate); } AutoUpdater::~AutoUpdater() { @@ -78,14 +79,6 @@ void AutoUpdater::OnWindowAllClosed() { QuitAndInstall(); } -mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("setFeedURL", &auto_updater::AutoUpdater::SetFeedURL) - .SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates) - .SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall); -} - void AutoUpdater::QuitAndInstall() { // If we don't have any window then quitAndInstall immediately. WindowList* window_list = WindowList::GetInstance(); @@ -102,7 +95,16 @@ void AutoUpdater::QuitAndInstall() { // static mate::Handle AutoUpdater::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new AutoUpdater); + return mate::CreateHandle(isolate, new AutoUpdater(isolate)); +} + +// static +void AutoUpdater::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("setFeedURL", &auto_updater::AutoUpdater::SetFeedURL) + .SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates) + .SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall); } } // namespace api diff --git a/atom/browser/api/atom_api_auto_updater.h b/atom/browser/api/atom_api_auto_updater.h index 95b91041e9e3..857647258adf 100644 --- a/atom/browser/api/atom_api_auto_updater.h +++ b/atom/browser/api/atom_api_auto_updater.h @@ -16,15 +16,18 @@ namespace atom { namespace api { -class AutoUpdater : public mate::EventEmitter, +class AutoUpdater : public mate::EventEmitter, public auto_updater::Delegate, public WindowListObserver { public: static mate::Handle Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: - AutoUpdater(); - virtual ~AutoUpdater(); + explicit AutoUpdater(v8::Isolate* isolate); + ~AutoUpdater() override; // Delegate implementations. void OnError(const std::string& error) override; @@ -39,10 +42,6 @@ class AutoUpdater : public mate::EventEmitter, // WindowListObserver: void OnWindowAllClosed() override; - // mate::Wrappable implementations: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - private: void QuitAndInstall(); diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index e49a2ee2f676..8698b43485bf 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -186,8 +186,10 @@ void SetCookieOnIO(scoped_refptr getter, } // namespace -Cookies::Cookies(content::BrowserContext* browser_context) - : request_context_getter_(browser_context->GetRequestContext()) { +Cookies::Cookies(v8::Isolate* isolate, + content::BrowserContext* browser_context) + : request_context_getter_(browser_context->GetRequestContext()) { + Init(isolate); } Cookies::~Cookies() { @@ -223,7 +225,7 @@ void Cookies::Set(const base::DictionaryValue& details, mate::Handle Cookies::Create( v8::Isolate* isolate, content::BrowserContext* browser_context) { - return mate::CreateHandle(isolate, new Cookies(browser_context)); + return mate::CreateHandle(isolate, new Cookies(isolate, browser_context)); } // static diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h index 302fd1b25110..33fee56960f4 100644 --- a/atom/browser/api/atom_api_cookies.h +++ b/atom/browser/api/atom_api_cookies.h @@ -46,8 +46,8 @@ class Cookies : public mate::TrackableObject { v8::Local prototype); protected: - explicit Cookies(content::BrowserContext* browser_context); - ~Cookies(); + Cookies(v8::Isolate* isolate, content::BrowserContext* browser_context); + ~Cookies() override; void Get(const base::DictionaryValue& filter, const GetCallback& callback); void Remove(const GURL& url, const std::string& name, diff --git a/atom/browser/api/atom_api_debugger.cc b/atom/browser/api/atom_api_debugger.cc index eab60311f3dd..03490360133a 100644 --- a/atom/browser/api/atom_api_debugger.cc +++ b/atom/browser/api/atom_api_debugger.cc @@ -31,9 +31,10 @@ WrapDebuggerCallback g_wrap_debugger; } // namespace -Debugger::Debugger(content::WebContents* web_contents) +Debugger::Debugger(v8::Isolate* isolate, content::WebContents* web_contents) : web_contents_(web_contents), previous_request_id_(0) { + Init(isolate); } Debugger::~Debugger() { @@ -150,7 +151,8 @@ void Debugger::SendCommand(mate::Arguments* args) { mate::Handle Debugger::Create( v8::Isolate* isolate, content::WebContents* web_contents) { - auto handle = mate::CreateHandle(isolate, new Debugger(web_contents)); + auto handle = mate::CreateHandle( + isolate, new Debugger(isolate, web_contents)); g_wrap_debugger.Run(handle.ToV8()); return handle; } @@ -165,16 +167,8 @@ void Debugger::BuildPrototype(v8::Isolate* isolate, .SetMethod("sendCommand", &Debugger::SendCommand); } -void ClearWrapDebugger() { - g_wrap_debugger.Reset(); -} - void SetWrapDebugger(const WrapDebuggerCallback& callback) { g_wrap_debugger = callback; - - // Cleanup the wrapper on exit. - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( - base::Bind(ClearWrapDebugger)); } } // namespace api diff --git a/atom/browser/api/atom_api_debugger.h b/atom/browser/api/atom_api_debugger.h index 5454108e8b09..1e97fa89abd0 100644 --- a/atom/browser/api/atom_api_debugger.h +++ b/atom/browser/api/atom_api_debugger.h @@ -42,8 +42,8 @@ class Debugger: public mate::TrackableObject, v8::Local prototype); protected: - explicit Debugger(content::WebContents* web_contents); - ~Debugger(); + Debugger(v8::Isolate* isolate, content::WebContents* web_contents); + ~Debugger() override; // content::DevToolsAgentHostClient: void AgentHostClosed(content::DevToolsAgentHost* agent_host, diff --git a/atom/browser/api/atom_api_desktop_capturer.cc b/atom/browser/api/atom_api_desktop_capturer.cc index cdae6f0c44cd..9200a89224c2 100644 --- a/atom/browser/api/atom_api_desktop_capturer.cc +++ b/atom/browser/api/atom_api_desktop_capturer.cc @@ -38,7 +38,8 @@ namespace atom { namespace api { -DesktopCapturer::DesktopCapturer() { +DesktopCapturer::DesktopCapturer(v8::Isolate* isolate) { + Init(isolate); } DesktopCapturer::~DesktopCapturer() { @@ -88,19 +89,19 @@ void DesktopCapturer::OnSourceThumbnailChanged(int index) { bool DesktopCapturer::OnRefreshFinished() { Emit("finished", media_list_->GetSources()); - media_list_.reset(); return false; } -mate::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("startHandling", &DesktopCapturer::StartHandling); -} - // static mate::Handle DesktopCapturer::Create(v8::Isolate* isolate) { - return mate::CreateHandle(isolate, new DesktopCapturer); + return mate::CreateHandle(isolate, new DesktopCapturer(isolate)); +} + +// static +void DesktopCapturer::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("startHandling", &DesktopCapturer::StartHandling); } } // namespace api diff --git a/atom/browser/api/atom_api_desktop_capturer.h b/atom/browser/api/atom_api_desktop_capturer.h index c22c8a44835f..e71141fa5218 100644 --- a/atom/browser/api/atom_api_desktop_capturer.h +++ b/atom/browser/api/atom_api_desktop_capturer.h @@ -14,18 +14,21 @@ namespace atom { namespace api { -class DesktopCapturer: public mate::EventEmitter, +class DesktopCapturer: public mate::EventEmitter, public DesktopMediaListObserver { public: static mate::Handle Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + void StartHandling(bool capture_window, bool capture_screen, const gfx::Size& thumbnail_size); protected: - DesktopCapturer(); - ~DesktopCapturer(); + explicit DesktopCapturer(v8::Isolate* isolate); + ~DesktopCapturer() override; // DesktopMediaListObserver overrides. void OnSourceAdded(int index) override; @@ -36,10 +39,6 @@ class DesktopCapturer: public mate::EventEmitter, bool OnRefreshFinished() override; private: - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - scoped_ptr media_list_; DISALLOW_COPY_AND_ASSIGN(DesktopCapturer); diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc index 3edd5f9c2549..96826a250f57 100644 --- a/atom/browser/api/atom_api_download_item.cc +++ b/atom/browser/api/atom_api_download_item.cc @@ -57,9 +57,11 @@ std::map>> g_download_item_objects; } // namespace -DownloadItem::DownloadItem(content::DownloadItem* download_item) +DownloadItem::DownloadItem(v8::Isolate* isolate, + content::DownloadItem* download_item) : download_item_(download_item) { download_item_->AddObserver(this); + Init(isolate); AttachAsUserData(download_item); } @@ -173,7 +175,7 @@ mate::Handle DownloadItem::Create( if (existing) return mate::CreateHandle(isolate, static_cast(existing)); - auto handle = mate::CreateHandle(isolate, new DownloadItem(item)); + auto handle = mate::CreateHandle(isolate, new DownloadItem(isolate, item)); g_wrap_download_item.Run(handle.ToV8()); // Reference this object in case it got garbage collected. @@ -182,16 +184,8 @@ mate::Handle DownloadItem::Create( return handle; } -void ClearWrapDownloadItem() { - g_wrap_download_item.Reset(); -} - void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) { g_wrap_download_item = callback; - - // Cleanup the wrapper on exit. - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( - base::Bind(ClearWrapDownloadItem)); } } // namespace api diff --git a/atom/browser/api/atom_api_download_item.h b/atom/browser/api/atom_api_download_item.h index 64469b9b34d1..bc7ddd821bb9 100644 --- a/atom/browser/api/atom_api_download_item.h +++ b/atom/browser/api/atom_api_download_item.h @@ -23,7 +23,6 @@ class DownloadItem : public mate::TrackableObject, static mate::Handle Create(v8::Isolate* isolate, content::DownloadItem* item); - // mate::TrackableObject: static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); @@ -41,7 +40,7 @@ class DownloadItem : public mate::TrackableObject, base::FilePath GetSavePath() const; protected: - explicit DownloadItem(content::DownloadItem* download_item); + DownloadItem(v8::Isolate* isolate, content::DownloadItem* download_item); ~DownloadItem(); // Override content::DownloadItem::Observer methods diff --git a/atom/browser/api/atom_api_global_shortcut.cc b/atom/browser/api/atom_api_global_shortcut.cc index f5a03e4abf90..2b1e5d6591a0 100644 --- a/atom/browser/api/atom_api_global_shortcut.cc +++ b/atom/browser/api/atom_api_global_shortcut.cc @@ -19,7 +19,8 @@ namespace atom { namespace api { -GlobalShortcut::GlobalShortcut() { +GlobalShortcut::GlobalShortcut(v8::Isolate* isolate) { + Init(isolate); } GlobalShortcut::~GlobalShortcut() { @@ -66,20 +67,21 @@ void GlobalShortcut::UnregisterAll() { GlobalShortcutListener::GetInstance()->UnregisterAccelerators(this); } -mate::ObjectTemplateBuilder GlobalShortcut::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) +// static +mate::Handle GlobalShortcut::Create(v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new GlobalShortcut(isolate)); +} + +// static +void GlobalShortcut::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) .SetMethod("register", &GlobalShortcut::Register) .SetMethod("isRegistered", &GlobalShortcut::IsRegistered) .SetMethod("unregister", &GlobalShortcut::Unregister) .SetMethod("unregisterAll", &GlobalShortcut::UnregisterAll); } -// static -mate::Handle GlobalShortcut::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new GlobalShortcut); -} - } // namespace api } // namespace atom diff --git a/atom/browser/api/atom_api_global_shortcut.h b/atom/browser/api/atom_api_global_shortcut.h index d7057b000320..41eb42f85569 100644 --- a/atom/browser/api/atom_api_global_shortcut.h +++ b/atom/browser/api/atom_api_global_shortcut.h @@ -23,13 +23,12 @@ class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, public: static mate::Handle Create(v8::Isolate* isolate); - protected: - GlobalShortcut(); - ~GlobalShortcut() override; + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); - // mate::Wrappable implementations: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; + protected: + explicit GlobalShortcut(v8::Isolate* isolate); + ~GlobalShortcut() override; private: typedef std::map AcceleratorCallbackMap; diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index e40ba17f464f..996c71739bc4 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -19,7 +19,7 @@ namespace atom { namespace api { -Menu::Menu() +Menu::Menu(v8::Isolate* isolate) : model_(new AtomMenuModel(this)), parent_(NULL) { } @@ -28,7 +28,7 @@ Menu::~Menu() { } void Menu::AfterInit(v8::Isolate* isolate) { - mate::Dictionary wrappable(isolate, GetWrapper(isolate)); + mate::Dictionary wrappable(isolate, GetWrapper()); mate::Dictionary delegate; if (!wrappable.Get("delegate", &delegate)) return; diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 1ae708863a73..5701985ab101 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -20,7 +20,7 @@ namespace api { class Menu : public mate::TrackableObject, public AtomMenuModel::Delegate { public: - static mate::Wrappable* Create(); + static mate::WrappableBase* Create(v8::Isolate* isolate); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); @@ -36,7 +36,7 @@ class Menu : public mate::TrackableObject, AtomMenuModel* model() const { return model_.get(); } protected: - Menu(); + explicit Menu(v8::Isolate* isolate); ~Menu() override; // mate::Wrappable: diff --git a/atom/browser/api/atom_api_menu_mac.h b/atom/browser/api/atom_api_menu_mac.h index 85227fa2a9d9..293e8ec4edae 100644 --- a/atom/browser/api/atom_api_menu_mac.h +++ b/atom/browser/api/atom_api_menu_mac.h @@ -17,7 +17,7 @@ namespace api { class MenuMac : public Menu { protected: - MenuMac(); + explicit MenuMac(v8::Isolate* isolate); void PopupAt(Window* window, int x, int y, int positioning_item = 0) override; diff --git a/atom/browser/api/atom_api_menu_mac.mm b/atom/browser/api/atom_api_menu_mac.mm index 71c677b0476e..d8ee089c1cba 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -15,7 +15,7 @@ namespace atom { namespace api { -MenuMac::MenuMac() { +MenuMac::MenuMac(v8::Isolate* isolate) : Menu(isolate) { } void MenuMac::PopupAt(Window* window, int x, int y, int positioning_item) { @@ -68,8 +68,8 @@ void Menu::SendActionToFirstResponder(const std::string& action) { } // static -mate::Wrappable* Menu::Create() { - return new MenuMac(); +mate::WrappableBase* Menu::Create(v8::Isolate* isolate) { + return new MenuMac(isolate); } } // namespace api diff --git a/atom/browser/api/atom_api_menu_views.cc b/atom/browser/api/atom_api_menu_views.cc index 4a3a97dd906e..4d1c902e1755 100644 --- a/atom/browser/api/atom_api_menu_views.cc +++ b/atom/browser/api/atom_api_menu_views.cc @@ -13,7 +13,7 @@ namespace atom { namespace api { -MenuViews::MenuViews() { +MenuViews::MenuViews(v8::Isolate* isolate) : Menu(isolate) { } void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item) { @@ -49,8 +49,8 @@ void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item) { } // static -mate::Wrappable* Menu::Create() { - return new MenuViews(); +mate::WrappableBase* Menu::Create(v8::Isolate* isolate) { + return new MenuViews(isolate); } } // namespace api diff --git a/atom/browser/api/atom_api_menu_views.h b/atom/browser/api/atom_api_menu_views.h index e4d17c77ca65..e1daa4904028 100644 --- a/atom/browser/api/atom_api_menu_views.h +++ b/atom/browser/api/atom_api_menu_views.h @@ -14,7 +14,7 @@ namespace api { class MenuViews : public Menu { public: - MenuViews(); + explicit MenuViews(v8::Isolate* isolate); protected: void PopupAt(Window* window, int x, int y, int positioning_item = 0) override; diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index 31b35e10cea8..32c50c696c7f 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -14,8 +14,9 @@ namespace atom { namespace api { -PowerMonitor::PowerMonitor() { +PowerMonitor::PowerMonitor(v8::Isolate* isolate) { base::PowerMonitor::Get()->AddObserver(this); + Init(isolate); } PowerMonitor::~PowerMonitor() { @@ -46,7 +47,13 @@ v8::Local PowerMonitor::Create(v8::Isolate* isolate) { return v8::Null(isolate); } - return CreateHandle(isolate, new PowerMonitor).ToV8(); + return mate::CreateHandle(isolate, new PowerMonitor(isolate)).ToV8(); +} + +// static +void PowerMonitor::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype); } } // namespace api diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index 8fb52eeec95e..50d4bb466ca9 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -19,8 +19,11 @@ class PowerMonitor : public mate::TrackableObject, public: static v8::Local Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: - PowerMonitor(); + explicit PowerMonitor(v8::Isolate* isolate); ~PowerMonitor() override; // base::PowerObserver implementations: diff --git a/atom/browser/api/atom_api_power_save_blocker.cc b/atom/browser/api/atom_api_power_save_blocker.cc index 58983e6c846a..b8adcb776e56 100644 --- a/atom/browser/api/atom_api_power_save_blocker.cc +++ b/atom/browser/api/atom_api_power_save_blocker.cc @@ -37,9 +37,10 @@ namespace atom { namespace api { -PowerSaveBlocker::PowerSaveBlocker() +PowerSaveBlocker::PowerSaveBlocker(v8::Isolate* isolate) : current_blocker_type_( - content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension) { + content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension) { + Init(isolate); } PowerSaveBlocker::~PowerSaveBlocker() { @@ -97,17 +98,18 @@ bool PowerSaveBlocker::IsStarted(int id) { return power_save_blocker_types_.find(id) != power_save_blocker_types_.end(); } -mate::ObjectTemplateBuilder PowerSaveBlocker::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("start", &PowerSaveBlocker::Start) - .SetMethod("stop", &PowerSaveBlocker::Stop) - .SetMethod("isStarted", &PowerSaveBlocker::IsStarted); +// static +mate::Handle PowerSaveBlocker::Create(v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new PowerSaveBlocker(isolate)); } // static -mate::Handle PowerSaveBlocker::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new PowerSaveBlocker); +void PowerSaveBlocker::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("start", &PowerSaveBlocker::Start) + .SetMethod("stop", &PowerSaveBlocker::Stop) + .SetMethod("isStarted", &PowerSaveBlocker::IsStarted); } } // namespace api diff --git a/atom/browser/api/atom_api_power_save_blocker.h b/atom/browser/api/atom_api_power_save_blocker.h index a698d746ceb0..c24ae0aa4b66 100644 --- a/atom/browser/api/atom_api_power_save_blocker.h +++ b/atom/browser/api/atom_api_power_save_blocker.h @@ -24,13 +24,12 @@ class PowerSaveBlocker : public mate::TrackableObject { public: static mate::Handle Create(v8::Isolate* isolate); - protected: - PowerSaveBlocker(); - ~PowerSaveBlocker() override; + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); - // mate::Wrappable implementations: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; + protected: + explicit PowerSaveBlocker(v8::Isolate* isolate); + ~PowerSaveBlocker() override; private: void UpdatePowerSaveBlocker(); diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index 09da9c71cadb..3835fac62d7a 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -22,37 +22,11 @@ namespace atom { namespace api { -Protocol::Protocol(AtomBrowserContext* browser_context) +Protocol::Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context) : request_context_getter_(browser_context->GetRequestContext()), job_factory_(browser_context->job_factory()) { CHECK(job_factory_); -} - -mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes) - .SetMethod("registerServiceWorkerSchemes", - &Protocol::RegisterServiceWorkerSchemes) - .SetMethod("registerStringProtocol", - &Protocol::RegisterProtocol) - .SetMethod("registerBufferProtocol", - &Protocol::RegisterProtocol) - .SetMethod("registerFileProtocol", - &Protocol::RegisterProtocol) - .SetMethod("registerHttpProtocol", - &Protocol::RegisterProtocol) - .SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol) - .SetMethod("isProtocolHandled", &Protocol::IsProtocolHandled) - .SetMethod("interceptStringProtocol", - &Protocol::InterceptProtocol) - .SetMethod("interceptBufferProtocol", - &Protocol::InterceptProtocol) - .SetMethod("interceptFileProtocol", - &Protocol::InterceptProtocol) - .SetMethod("interceptHttpProtocol", - &Protocol::InterceptProtocol) - .SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol); + Init(isolate); } void Protocol::RegisterStandardSchemes( @@ -150,7 +124,35 @@ std::string Protocol::ErrorCodeToString(ProtocolError error) { // static mate::Handle Protocol::Create( v8::Isolate* isolate, AtomBrowserContext* browser_context) { - return mate::CreateHandle(isolate, new Protocol(browser_context)); + return mate::CreateHandle(isolate, new Protocol(isolate, browser_context)); +} + +// static +void Protocol::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes) + .SetMethod("registerServiceWorkerSchemes", + &Protocol::RegisterServiceWorkerSchemes) + .SetMethod("registerStringProtocol", + &Protocol::RegisterProtocol) + .SetMethod("registerBufferProtocol", + &Protocol::RegisterProtocol) + .SetMethod("registerFileProtocol", + &Protocol::RegisterProtocol) + .SetMethod("registerHttpProtocol", + &Protocol::RegisterProtocol) + .SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol) + .SetMethod("isProtocolHandled", &Protocol::IsProtocolHandled) + .SetMethod("interceptStringProtocol", + &Protocol::InterceptProtocol) + .SetMethod("interceptBufferProtocol", + &Protocol::InterceptProtocol) + .SetMethod("interceptFileProtocol", + &Protocol::InterceptProtocol) + .SetMethod("interceptHttpProtocol", + &Protocol::InterceptProtocol) + .SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol); } } // namespace api diff --git a/atom/browser/api/atom_api_protocol.h b/atom/browser/api/atom_api_protocol.h index 107fbf1ce712..d7fa7f5211f8 100644 --- a/atom/browser/api/atom_api_protocol.h +++ b/atom/browser/api/atom_api_protocol.h @@ -30,7 +30,7 @@ class AtomURLRequestJobFactory; namespace api { -class Protocol : public mate::Wrappable { +class Protocol : public mate::Wrappable { public: using Handler = base::Callback)>; @@ -40,12 +40,11 @@ class Protocol : public mate::Wrappable { static mate::Handle Create( v8::Isolate* isolate, AtomBrowserContext* browser_context); - protected: - explicit Protocol(AtomBrowserContext* browser_context); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); - // mate::Wrappable implementations: - virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate); + protected: + Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context); private: // Possible errors. diff --git a/atom/browser/api/atom_api_screen.cc b/atom/browser/api/atom_api_screen.cc index 407a71f0cc41..9d845e390e31 100644 --- a/atom/browser/api/atom_api_screen.cc +++ b/atom/browser/api/atom_api_screen.cc @@ -47,9 +47,10 @@ std::vector MetricsToArray(uint32_t metrics) { } // namespace -Screen::Screen(gfx::Screen* screen) : screen_(screen) { - displays_ = screen_->GetAllDisplays(); +Screen::Screen(v8::Isolate* isolate, gfx::Screen* screen) + : screen_(screen) { screen_->AddObserver(this); + Init(isolate); } Screen::~Screen() { @@ -65,7 +66,7 @@ gfx::Display Screen::GetPrimaryDisplay() { } std::vector Screen::GetAllDisplays() { - return displays_; + return screen_->GetAllDisplays(); } gfx::Display Screen::GetDisplayNearestPoint(const gfx::Point& point) { @@ -77,39 +78,18 @@ gfx::Display Screen::GetDisplayMatching(const gfx::Rect& match_rect) { } void Screen::OnDisplayAdded(const gfx::Display& new_display) { - displays_.push_back(new_display); Emit("display-added", new_display); } void Screen::OnDisplayRemoved(const gfx::Display& old_display) { - auto iter = FindById(&displays_, old_display.id()); - if (iter == displays_.end()) - return; - - displays_.erase(iter); Emit("display-removed", old_display); } void Screen::OnDisplayMetricsChanged(const gfx::Display& display, uint32_t changed_metrics) { - auto iter = FindById(&displays_, display.id()); - if (iter == displays_.end()) - return; - - *iter = display; Emit("display-metrics-changed", display, MetricsToArray(changed_metrics)); } -mate::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("getCursorScreenPoint", &Screen::GetCursorScreenPoint) - .SetMethod("getPrimaryDisplay", &Screen::GetPrimaryDisplay) - .SetMethod("getAllDisplays", &Screen::GetAllDisplays) - .SetMethod("getDisplayNearestPoint", &Screen::GetDisplayNearestPoint) - .SetMethod("getDisplayMatching", &Screen::GetDisplayMatching); -} - // static v8::Local Screen::Create(v8::Isolate* isolate) { if (!Browser::Get()->is_ready()) { @@ -126,7 +106,18 @@ v8::Local Screen::Create(v8::Isolate* isolate) { return v8::Null(isolate); } - return mate::CreateHandle(isolate, new Screen(screen)).ToV8(); + return mate::CreateHandle(isolate, new Screen(isolate, screen)).ToV8(); +} + +// static +void Screen::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("getCursorScreenPoint", &Screen::GetCursorScreenPoint) + .SetMethod("getPrimaryDisplay", &Screen::GetPrimaryDisplay) + .SetMethod("getAllDisplays", &Screen::GetAllDisplays) + .SetMethod("getDisplayNearestPoint", &Screen::GetDisplayNearestPoint) + .SetMethod("getDisplayMatching", &Screen::GetDisplayMatching); } } // namespace api diff --git a/atom/browser/api/atom_api_screen.h b/atom/browser/api/atom_api_screen.h index f724130fa7fc..c17b61288525 100644 --- a/atom/browser/api/atom_api_screen.h +++ b/atom/browser/api/atom_api_screen.h @@ -21,14 +21,17 @@ namespace atom { namespace api { -class Screen : public mate::EventEmitter, +class Screen : public mate::EventEmitter, public gfx::DisplayObserver { public: static v8::Local Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: - explicit Screen(gfx::Screen* screen); - virtual ~Screen(); + Screen(v8::Isolate* isolate, gfx::Screen* screen); + ~Screen() override; gfx::Point GetCursorScreenPoint(); gfx::Display GetPrimaryDisplay(); @@ -42,13 +45,8 @@ class Screen : public mate::EventEmitter, void OnDisplayMetricsChanged(const gfx::Display& display, uint32_t changed_metrics) override; - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - private: gfx::Screen* screen_; - std::vector displays_; DISALLOW_COPY_AND_ASSIGN(Screen); }; diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 0f104c760724..39c435b723ea 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -9,9 +9,7 @@ #include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/api/atom_api_download_item.h" -#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/atom_api_web_request.h" -#include "atom/browser/api/save_page_handler.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_permission_manager.h" @@ -23,12 +21,13 @@ #include "atom/common/native_mate_converters/net_converter.h" #include "atom/common/node_includes.h" #include "base/files/file_path.h" +#include "base/guid.h" #include "base/prefs/pref_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" #include "brightray/browser/net/devtools_network_conditions.h" -#include "brightray/browser/net/devtools_network_controller.h" +#include "brightray/browser/net/devtools_network_controller_handle.h" #include "chrome/common/pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" @@ -287,13 +286,15 @@ void ClearHostResolverCacheInIO( } // namespace -Session::Session(AtomBrowserContext* browser_context) - : browser_context_(browser_context) { - AttachAsUserData(browser_context); - +Session::Session(v8::Isolate* isolate, AtomBrowserContext* browser_context) + : devtools_network_emulation_client_id_(base::GenerateGUID()), + browser_context_(browser_context) { // Observe DownloadManger to get download notifications. content::BrowserContext::GetDownloadManager(browser_context)-> AddObserver(this); + + Init(isolate); + AttachAsUserData(browser_context); } Session::~Session() { @@ -303,13 +304,15 @@ Session::~Session() { void Session::OnDownloadCreated(content::DownloadManager* manager, content::DownloadItem* item) { - auto web_contents = item->GetWebContents(); - if (SavePageHandler::IsSavePageTypes(item->GetMimeType())) + if (item->IsSavePackageDownload()) return; + + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); bool prevent_default = Emit( "will-download", DownloadItem::Create(isolate(), item), - api::WebContents::CreateFrom(isolate(), web_contents)); + item->GetWebContents()); if (prevent_default) { item->Cancel(true); item->Remove(); @@ -381,25 +384,19 @@ void Session::EnableNetworkEmulation(const mate::Dictionary& options) { download_throughput, upload_throughput)); } - auto controller = browser_context_->GetDevToolsNetworkController(); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&brightray::DevToolsNetworkController::SetNetworkState, - base::Unretained(controller), - std::string(), - base::Passed(&conditions))); + browser_context_->network_controller_handle()->SetNetworkState( + devtools_network_emulation_client_id_, std::move(conditions)); + browser_context_->network_delegate()->SetDevToolsNetworkEmulationClientId( + devtools_network_emulation_client_id_); } void Session::DisableNetworkEmulation() { - scoped_ptr conditions( - new brightray::DevToolsNetworkConditions(false)); - auto controller = browser_context_->GetDevToolsNetworkController(); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&brightray::DevToolsNetworkController::SetNetworkState, - base::Unretained(controller), - std::string(), - base::Passed(&conditions))); + scoped_ptr conditions; + browser_context_->network_controller_handle()->SetNetworkState( + devtools_network_emulation_client_id_, std::move(conditions)); + browser_context_->network_delegate()->SetDevToolsNetworkEmulationClientId( + std::string()); } void Session::SetCertVerifyProc(v8::Local val, @@ -458,7 +455,8 @@ mate::Handle Session::CreateFrom( if (existing) return mate::CreateHandle(isolate, static_cast(existing)); - auto handle = mate::CreateHandle(isolate, new Session(browser_context)); + auto handle = mate::CreateHandle( + isolate, new Session(isolate, browser_context)); g_wrap_session.Run(handle.ToV8()); return handle; } @@ -493,16 +491,8 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetProperty("webRequest", &Session::WebRequest); } -void ClearWrapSession() { - g_wrap_session.Reset(); -} - void SetWrapSession(const WrapSessionCallback& callback) { g_wrap_session = callback; - - // Cleanup the wrapper on exit. - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( - base::Bind(ClearWrapSession)); } } // namespace api diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 02d8ba5cdec0..5e08a85aa7d1 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -58,7 +58,7 @@ class Session: public mate::TrackableObject, v8::Local prototype); protected: - explicit Session(AtomBrowserContext* browser_context); + Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); ~Session(); // content::DownloadManager::Observer: @@ -86,6 +86,9 @@ class Session: public mate::TrackableObject, v8::Global cookies_; v8::Global web_request_; + // The X-DevTools-Emulate-Network-Conditions-Client-Id. + std::string devtools_network_emulation_client_id_; + scoped_refptr browser_context_; DISALLOW_COPY_AND_ASSIGN(Session); diff --git a/atom/browser/api/atom_api_system_preferences.cc b/atom/browser/api/atom_api_system_preferences.cc new file mode 100644 index 000000000000..b8c665456ade --- /dev/null +++ b/atom/browser/api/atom_api_system_preferences.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_system_preferences.h" + +#include "atom/common/native_mate_converters/callback.h" +#include "atom/common/node_includes.h" +#include "native_mate/dictionary.h" + +#if defined(OS_WIN) +#include "ui/base/win/shell.h" +#endif + +namespace atom { + +namespace api { + +SystemPreferences::SystemPreferences(v8::Isolate* isolate) { + Init(isolate); +} + +SystemPreferences::~SystemPreferences() { +} + +#if defined(OS_WIN) +bool SystemPreferences::IsAeroGlassEnabled() { + return ui::win::IsAeroGlassEnabled(); +} +#endif + +#if !defined(OS_MACOSX) +bool SystemPreferences::IsDarkMode() { + return false; +} +#endif + +// static +mate::Handle SystemPreferences::Create( + v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new SystemPreferences(isolate)); +} + +// static +void SystemPreferences::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) +#if defined(OS_WIN) + .SetMethod("isAeroGlassEnabled", &SystemPreferences::IsAeroGlassEnabled) +#elif defined(OS_MACOSX) + .SetMethod("subscribeNotification", + &SystemPreferences::SubscribeNotification) + .SetMethod("unsubscribeNotification", + &SystemPreferences::UnsubscribeNotification) + .SetMethod("getUserDefault", &SystemPreferences::GetUserDefault) +#endif + .SetMethod("isDarkMode", &SystemPreferences::IsDarkMode); +} + +} // namespace api + +} // namespace atom + +namespace { + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("systemPreferences", atom::api::SystemPreferences::Create(isolate)); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_system_preferences, Initialize); diff --git a/atom/browser/api/atom_api_system_preferences.h b/atom/browser/api/atom_api_system_preferences.h new file mode 100644 index 000000000000..fed1c52247b7 --- /dev/null +++ b/atom/browser/api/atom_api_system_preferences.h @@ -0,0 +1,48 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_SYSTEM_PREFERENCES_H_ +#define ATOM_BROWSER_API_ATOM_API_SYSTEM_PREFERENCES_H_ + +#include + +#include "atom/browser/api/event_emitter.h" +#include "base/callback.h" +#include "native_mate/handle.h" + +namespace atom { + +namespace api { + +class SystemPreferences : public mate::EventEmitter { + public: + static mate::Handle Create(v8::Isolate* isolate); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + +#if defined(OS_WIN) + bool IsAeroGlassEnabled(); +#elif defined(OS_MACOSX) + int SubscribeNotification(const std::string& name, + const base::Closure& callback); + void UnsubscribeNotification(int id); + v8::Local GetUserDefault(const std::string& name, + const std::string& type); +#endif + bool IsDarkMode(); + + protected: + explicit SystemPreferences(v8::Isolate* isolate); + ~SystemPreferences() override; + + private: + DISALLOW_COPY_AND_ASSIGN(SystemPreferences); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_SYSTEM_PREFERENCES_H_ diff --git a/atom/browser/api/atom_api_system_preferences_mac.mm b/atom/browser/api/atom_api_system_preferences_mac.mm new file mode 100644 index 000000000000..2d12b278ae92 --- /dev/null +++ b/atom/browser/api/atom_api_system_preferences_mac.mm @@ -0,0 +1,83 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_system_preferences.h" + +#include + +#import + +#include "atom/common/native_mate_converters/gurl_converter.h" +#include "base/strings/sys_string_conversions.h" +#include "net/base/mac/url_conversions.h" + +namespace atom { + +namespace api { + +namespace { + +int g_next_id = 0; + +// The map to convert |id| to |int|. +std::map g_id_map; + +} // namespace + +int SystemPreferences::SubscribeNotification(const std::string& name, + const base::Closure& callback) { + int request_id = g_next_id++; + __block base::Closure copied_callback = callback; + g_id_map[request_id] = [[NSDistributedNotificationCenter defaultCenter] + addObserverForName:base::SysUTF8ToNSString(name) + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + copied_callback.Run(); + } + ]; + return request_id; +} + +void SystemPreferences::UnsubscribeNotification(int request_id) { + auto iter = g_id_map.find(request_id); + if (iter != g_id_map.end()) { + id observer = iter->second; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:observer]; + g_id_map.erase(iter); + } +} + +v8::Local SystemPreferences::GetUserDefault( + const std::string& name, const std::string& type) { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSString* key = base::SysUTF8ToNSString(name); + if (type == "string") { + return mate::StringToV8( + isolate(), base::SysNSStringToUTF8([defaults stringForKey:key])); + } else if (type == "boolean") { + return v8::Boolean::New(isolate(), [defaults boolForKey:key]); + } else if (type == "float") { + return v8::Number::New(isolate(), [defaults floatForKey:key]); + } else if (type == "integer") { + return v8::Integer::New(isolate(), [defaults integerForKey:key]); + } else if (type == "double") { + return v8::Number::New(isolate(), [defaults doubleForKey:key]); + } else if (type == "url") { + return mate::ConvertToV8( + isolate(), net::GURLWithNSURL([defaults URLForKey:key])); + } else { + return v8::Undefined(isolate()); + } +} + +bool SystemPreferences::IsDarkMode() { + NSString* mode = [[NSUserDefaults standardUserDefaults] + stringForKey:@"AppleInterfaceStyle"]; + return [mode isEqualToString:@"Dark"]; +} + +} // namespace api + +} // namespace atom diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index 5f351f485049..34fe81b92b22 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -22,7 +22,7 @@ namespace atom { namespace api { -Tray::Tray(const gfx::Image& image) +Tray::Tray(v8::Isolate* isolate, const gfx::Image& image) : tray_icon_(TrayIcon::Create()) { tray_icon_->SetImage(image); tray_icon_->AddObserver(this); @@ -32,13 +32,13 @@ Tray::~Tray() { } // static -mate::Wrappable* Tray::New(v8::Isolate* isolate, const gfx::Image& image) { +mate::WrappableBase* Tray::New(v8::Isolate* isolate, const gfx::Image& image) { if (!Browser::Get()->is_ready()) { isolate->ThrowException(v8::Exception::Error(mate::StringToV8( isolate, "Cannot create Tray before app is ready"))); return nullptr; } - return new Tray(image); + return new Tray(isolate, image); } void Tray::OnClicked(const gfx::Rect& bounds, int modifiers) { diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index 0e0d153ad0d3..84e68220dba8 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -32,13 +32,14 @@ class Menu; class Tray : public mate::TrackableObject, public TrayIconObserver { public: - static mate::Wrappable* New(v8::Isolate* isolate, const gfx::Image& image); + static mate::WrappableBase* New( + v8::Isolate* isolate, const gfx::Image& image); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); protected: - explicit Tray(const gfx::Image& image); + Tray(v8::Isolate* isolate, const gfx::Image& image); ~Tray() override; // TrayIconObserver: diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index abad4add4fb0..a48277490b00 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -215,13 +215,17 @@ content::ServiceWorkerContext* GetServiceWorkerContext( } // namespace -WebContents::WebContents(content::WebContents* web_contents) +WebContents::WebContents(v8::Isolate* isolate, + content::WebContents* web_contents) : content::WebContentsObserver(web_contents), + embedder_(nullptr), type_(REMOTE), request_id_(0), background_throttling_(true) { - AttachAsUserData(web_contents); web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent()); + + Init(isolate); + AttachAsUserData(web_contents); } WebContents::WebContents(v8::Isolate* isolate, @@ -269,7 +273,6 @@ WebContents::WebContents(v8::Isolate* isolate, } Observe(web_contents); - AttachAsUserData(web_contents); InitWithWebContents(web_contents); managed_web_contents()->GetView()->SetDelegate(this); @@ -298,6 +301,9 @@ WebContents::WebContents(v8::Isolate* isolate, if (owner_window) SetOwnerWindow(owner_window); } + + Init(isolate); + AttachAsUserData(web_contents); } WebContents::~WebContents() { @@ -792,6 +798,14 @@ bool WebContents::IsLoading() const { return web_contents()->IsLoading(); } +bool WebContents::IsLoadingMainFrame() const { + // Comparing site instances works because Electron always creates a new site + // instance when navigating, regardless of origin. See AtomBrowserClient. + return (web_contents()->GetLastCommittedURL().is_empty() || + web_contents()->GetSiteInstance() != + web_contents()->GetPendingSiteInstance()) && IsLoading(); +} + bool WebContents::IsWaitingForResponse() const { return web_contents()->IsWaitingForResponse(); } @@ -848,14 +862,20 @@ void WebContents::OpenDevTools(mate::Arguments* args) { if (type_ == REMOTE) return; - bool detach = false; + std::string state; if (type_ == WEB_VIEW) { - detach = true; + state = "detach"; } else if (args && args->Length() == 1) { + bool detach = false; mate::Dictionary options; - args->GetNext(&options) && options.Get("detach", &detach); + if (args->GetNext(&options)) { + options.Get("mode", &state); + options.Get("detach", &detach); + if (state.empty() && detach) + state = "detach"; + } } - managed_web_contents()->SetCanDock(!detach); + managed_web_contents()->SetDockState(state); managed_web_contents()->ShowDevTools(); } @@ -1188,6 +1208,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("_getURL", &WebContents::GetURL) .SetMethod("getTitle", &WebContents::GetTitle) .SetMethod("isLoading", &WebContents::IsLoading) + .SetMethod("isLoadingMainFrame", &WebContents::IsLoadingMainFrame) .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) .SetMethod("_stop", &WebContents::Stop) .SetMethod("_goBack", &WebContents::GoBack) @@ -1274,7 +1295,8 @@ mate::Handle WebContents::CreateFrom( return mate::CreateHandle(isolate, static_cast(existing)); // Otherwise create a new WebContents wrapper object. - auto handle = mate::CreateHandle(isolate, new WebContents(web_contents)); + auto handle = mate::CreateHandle( + isolate, new WebContents(isolate, web_contents)); g_wrap_web_contents.Run(handle.ToV8()); return handle; } @@ -1287,16 +1309,8 @@ mate::Handle WebContents::Create( return handle; } -void ClearWrapWebContents() { - g_wrap_web_contents.Reset(); -} - void SetWrapWebContents(const WrapWebContentsCallback& callback) { g_wrap_web_contents = callback; - - // Cleanup the wrapper on exit. - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( - base::Bind(ClearWrapWebContents)); } } // namespace api diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 0cb2a348e170..9deb1696c411 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -55,6 +55,9 @@ class WebContents : public mate::TrackableObject, static mate::Handle Create( v8::Isolate* isolate, const mate::Dictionary& options); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + int GetID() const; bool Equal(const WebContents* web_contents) const; void LoadURL(const GURL& url, const mate::Dictionary& options); @@ -62,6 +65,7 @@ class WebContents : public mate::TrackableObject, GURL GetURL() const; base::string16 GetTitle() const; bool IsLoading() const; + bool IsLoadingMainFrame() const; bool IsWaitingForResponse() const; void Stop(); void ReloadIgnoringCache(); @@ -155,12 +159,8 @@ class WebContents : public mate::TrackableObject, v8::Local DevToolsWebContents(v8::Isolate* isolate); v8::Local Debugger(v8::Isolate* isolate); - // mate::TrackableObject: - static void BuildPrototype(v8::Isolate* isolate, - v8::Local prototype); - protected: - explicit WebContents(content::WebContents* web_contents); + WebContents(v8::Isolate* isolate, content::WebContents* web_contents); WebContents(v8::Isolate* isolate, const mate::Dictionary& options); ~WebContents(); diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc index a987369ed82d..867901df065b 100644 --- a/atom/browser/api/atom_api_web_request.cc +++ b/atom/browser/api/atom_api_web_request.cc @@ -36,8 +36,10 @@ namespace atom { namespace api { -WebRequest::WebRequest(AtomBrowserContext* browser_context) +WebRequest::WebRequest(v8::Isolate* isolate, + AtomBrowserContext* browser_context) : browser_context_(browser_context) { + Init(isolate); } WebRequest::~WebRequest() { @@ -81,7 +83,7 @@ void WebRequest::SetListener(Method method, Event type, mate::Arguments* args) { mate::Handle WebRequest::Create( v8::Isolate* isolate, AtomBrowserContext* browser_context) { - return mate::CreateHandle(isolate, new WebRequest(browser_context)); + return mate::CreateHandle(isolate, new WebRequest(isolate, browser_context)); } // static diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h index 9a6e17a04605..edcdcc5339e3 100644 --- a/atom/browser/api/atom_api_web_request.h +++ b/atom/browser/api/atom_api_web_request.h @@ -21,13 +21,12 @@ class WebRequest : public mate::TrackableObject { static mate::Handle Create(v8::Isolate* isolate, AtomBrowserContext* browser_context); - // mate::TrackableObject: static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); protected: - explicit WebRequest(AtomBrowserContext* browser_context); - ~WebRequest(); + WebRequest(v8::Isolate* isolate, AtomBrowserContext* browser_context); + ~WebRequest() override; // C++ can not distinguish overloaded member function. template diff --git a/atom/browser/api/atom_api_web_view_manager.cc b/atom/browser/api/atom_api_web_view_manager.cc index e57c5ffb6bb4..f06c1105526e 100644 --- a/atom/browser/api/atom_api_web_view_manager.cc +++ b/atom/browser/api/atom_api_web_view_manager.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/web_contents_preferences.h" #include "atom/browser/web_view_manager.h" +#include "atom/common/native_mate_converters/content_converter.h" #include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/node_includes.h" #include "content/public/browser/browser_context.h" @@ -12,22 +12,6 @@ using atom::WebContentsPreferences; -namespace mate { - -template<> -struct Converter { - static bool FromV8(v8::Isolate* isolate, v8::Local val, - content::WebContents** out) { - atom::api::WebContents* contents; - if (!Converter::FromV8(isolate, val, &contents)) - return false; - *out = contents->web_contents(); - return true; - } -}; - -} // namespace mate - namespace { atom::WebViewManager* GetWebViewManager(content::WebContents* web_contents) { diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index fd9dd94458c3..ae5eec64cf93 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -151,7 +151,7 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { api_web_contents_ = web_contents.get(); // Keep a copy of the options for later use. - mate::Dictionary(isolate, web_contents->GetWrapper(isolate)).Set( + mate::Dictionary(isolate, web_contents->GetWrapper()).Set( "browserWindowOptions", options); // Creates BrowserWindow. @@ -287,7 +287,7 @@ void Window::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) { #endif // static -mate::Wrappable* Window::New(v8::Isolate* isolate, mate::Arguments* args) { +mate::WrappableBase* Window::New(v8::Isolate* isolate, mate::Arguments* args) { if (!Browser::Get()->is_ready()) { isolate->ThrowException(v8::Exception::Error(mate::StringToV8( isolate, "Cannot create BrowserWindow before app is ready"))); @@ -440,6 +440,10 @@ std::vector Window::GetMaximumSize() { return result; } +void Window::SetSheetOffset(double offset) { + window_->SetSheetOffset(offset); +} + void Window::SetResizable(bool resizable) { window_->SetResizable(resizable); } @@ -746,6 +750,7 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("getMinimumSize", &Window::GetMinimumSize) .SetMethod("setMaximumSize", &Window::SetMaximumSize) .SetMethod("getMaximumSize", &Window::GetMaximumSize) + .SetMethod("setSheetOffset", &Window::SetSheetOffset) .SetMethod("setResizable", &Window::SetResizable) .SetMethod("isResizable", &Window::IsResizable) .SetMethod("setMovable", &Window::SetMovable) @@ -812,7 +817,7 @@ v8::Local Window::From(v8::Isolate* isolate, NativeWindow* native_window) { auto existing = TrackableObject::FromWrappedClass(isolate, native_window); if (existing) - return existing->GetWrapper(isolate); + return existing->GetWrapper(); else return v8::Null(isolate); } diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index d26ff5b36748..a00e063d99f8 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -38,7 +38,7 @@ class WebContents; class Window : public mate::TrackableObject, public NativeWindowObserver { public: - static mate::Wrappable* New(v8::Isolate* isolate, mate::Arguments* args); + static mate::WrappableBase* New(v8::Isolate* isolate, mate::Arguments* args); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); @@ -51,7 +51,7 @@ class Window : public mate::TrackableObject, protected: Window(v8::Isolate* isolate, const mate::Dictionary& options); - virtual ~Window(); + ~Window() override; // NativeWindowObserver: void WillCloseWindow(bool* prevent_default) override; @@ -110,6 +110,7 @@ class Window : public mate::TrackableObject, std::vector GetMinimumSize(); void SetMaximumSize(int width, int height); std::vector GetMaximumSize(); + void SetSheetOffset(double offset); void SetResizable(bool resizable); bool IsResizable(); void SetMovable(bool movable); diff --git a/atom/browser/api/event.cc b/atom/browser/api/event.cc index 5c87292ea52e..f456cf2758bb 100644 --- a/atom/browser/api/event.cc +++ b/atom/browser/api/event.cc @@ -11,31 +11,15 @@ namespace mate { -namespace { - -v8::Persistent template_; - -} // namespace - -Event::Event() +Event::Event(v8::Isolate* isolate) : sender_(NULL), message_(NULL) { + Init(isolate); } Event::~Event() { } -ObjectTemplateBuilder Event::GetObjectTemplateBuilder(v8::Isolate* isolate) { - if (template_.IsEmpty()) - template_.Reset(isolate, ObjectTemplateBuilder(isolate) - .SetMethod("preventDefault", &Event::PreventDefault) - .SetMethod("sendReply", &Event::SendReply) - .Build()); - - return ObjectTemplateBuilder( - isolate, v8::Local::New(isolate, template_)); -} - void Event::SetSenderAndMessage(content::WebContents* sender, IPC::Message* message) { DCHECK(!sender_); @@ -52,7 +36,7 @@ void Event::WebContentsDestroyed() { } void Event::PreventDefault(v8::Isolate* isolate) { - GetWrapper(isolate)->Set(StringToV8(isolate, "defaultPrevented"), + GetWrapper()->Set(StringToV8(isolate, "defaultPrevented"), v8::True(isolate)); } @@ -66,7 +50,15 @@ bool Event::SendReply(const base::string16& json) { // static Handle Event::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new Event); + return mate::CreateHandle(isolate, new Event(isolate)); +} + +// static +void Event::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("preventDefault", &Event::PreventDefault) + .SetMethod("sendReply", &Event::SendReply); } } // namespace mate diff --git a/atom/browser/api/event.h b/atom/browser/api/event.h index 5cdc08324b72..81db638a0dc2 100644 --- a/atom/browser/api/event.h +++ b/atom/browser/api/event.h @@ -15,11 +15,14 @@ class Message; namespace mate { -class Event : public Wrappable, +class Event : public Wrappable, public content::WebContentsObserver { public: static Handle Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + // Pass the sender and message to be replied. void SetSenderAndMessage(content::WebContents* sender, IPC::Message* message); @@ -30,11 +33,8 @@ class Event : public Wrappable, bool SendReply(const base::string16& json); protected: - Event(); - virtual ~Event(); - - // Wrappable implementations: - ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override; + explicit Event(v8::Isolate* isolate); + ~Event() override; // content::WebContentsObserver implementations: void WebContentsDestroyed() override; diff --git a/atom/browser/api/event_emitter.cc b/atom/browser/api/event_emitter.cc index be7018dafa44..7e392fddee34 100644 --- a/atom/browser/api/event_emitter.cc +++ b/atom/browser/api/event_emitter.cc @@ -34,11 +34,13 @@ v8::Local CreateEventObject(v8::Isolate* isolate) { } // namespace -EventEmitter::EventEmitter() { -} +namespace internal { -v8::Local EventEmitter::CreateJSEvent( - v8::Isolate* isolate, content::WebContents* sender, IPC::Message* message) { +v8::Local CreateJSEvent( + v8::Isolate* isolate, + v8::Local object, + content::WebContents* sender, + IPC::Message* message) { v8::Local event; bool use_native_event = sender && message; @@ -49,16 +51,20 @@ v8::Local EventEmitter::CreateJSEvent( } else { event = CreateEventObject(isolate); } - mate::Dictionary(isolate, event).Set("sender", GetWrapper(isolate)); + mate::Dictionary(isolate, event).Set("sender", object); return event; } -v8::Local EventEmitter::CreateCustomEvent( - v8::Isolate* isolate, v8::Local custom_event) { +v8::Local CreateCustomEvent( + v8::Isolate* isolate, + v8::Local object, + v8::Local custom_event) { v8::Local event = CreateEventObject(isolate); (void)event->SetPrototype(custom_event->CreationContext(), custom_event); - mate::Dictionary(isolate, event).Set("sender", GetWrapper(isolate)); + mate::Dictionary(isolate, event).Set("sender", object); return event; } +} // namespace internal + } // namespace mate diff --git a/atom/browser/api/event_emitter.h b/atom/browser/api/event_emitter.h index 42816d42a45b..99f6ed46e48f 100644 --- a/atom/browser/api/event_emitter.h +++ b/atom/browser/api/event_emitter.h @@ -20,17 +20,38 @@ class Message; namespace mate { +namespace internal { + +v8::Local CreateJSEvent(v8::Isolate* isolate, + v8::Local object, + content::WebContents* sender, + IPC::Message* message); +v8::Local CreateCustomEvent( + v8::Isolate* isolate, + v8::Local object, + v8::Local event); + +} // namespace internal + // Provide helperers to emit event in JavaScript. -class EventEmitter : public Wrappable { +template +class EventEmitter : public Wrappable { public: typedef std::vector> ValueArray; + // Make the convinient methods visible: + // https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members + v8::Local GetWrapper() { return Wrappable::GetWrapper(); } + v8::Isolate* isolate() const { return Wrappable::isolate(); } + // this.emit(name, event, args...); template bool EmitCustomEvent(const base::StringPiece& name, v8::Local event, const Args&... args) { - return EmitWithEvent(name, CreateCustomEvent(isolate(), event), args...); + return EmitWithEvent( + name, + internal::CreateCustomEvent(isolate(), GetWrapper(), event), args...); } // this.emit(name, new Event(), args...); @@ -47,12 +68,13 @@ class EventEmitter : public Wrappable { const Args&... args) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); - v8::Local event = CreateJSEvent(isolate(), sender, message); + v8::Local event = internal::CreateJSEvent( + isolate(), GetWrapper(), sender, message); return EmitWithEvent(name, event, args...); } protected: - EventEmitter(); + EventEmitter() {} private: // this.emit(name, event, args...); @@ -62,17 +84,11 @@ class EventEmitter : public Wrappable { const Args&... args) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); - EmitEvent(isolate(), GetWrapper(isolate()), name, event, args...); + EmitEvent(isolate(), GetWrapper(), name, event, args...); return event->Get( StringToV8(isolate(), "defaultPrevented"))->BooleanValue(); } - v8::Local CreateJSEvent(v8::Isolate* isolate, - content::WebContents* sender, - IPC::Message* message); - v8::Local CreateCustomEvent( - v8::Isolate* isolate, v8::Local event); - DISALLOW_COPY_AND_ASSIGN(EventEmitter); }; diff --git a/atom/browser/api/save_page_handler.cc b/atom/browser/api/save_page_handler.cc index 1e5bc094cf63..e07ec3ac0abb 100644 --- a/atom/browser/api/save_page_handler.cc +++ b/atom/browser/api/save_page_handler.cc @@ -73,11 +73,6 @@ void SavePageHandler::Destroy(content::DownloadItem* item) { delete this; } -// static -bool SavePageHandler::IsSavePageTypes(const std::string& type) { - return type == "multipart/related" || type == "text/html"; -} - } // namespace api } // namespace atom diff --git a/atom/browser/api/save_page_handler.h b/atom/browser/api/save_page_handler.h index dd1692a8badc..951a9414e03f 100644 --- a/atom/browser/api/save_page_handler.h +++ b/atom/browser/api/save_page_handler.h @@ -37,8 +37,6 @@ class SavePageHandler : public content::DownloadManager::Observer, bool Handle(const base::FilePath& full_path, const content::SavePageType& save_type); - static bool IsSavePageTypes(const std::string& type); - private: void Destroy(content::DownloadItem* item); diff --git a/atom/browser/api/trackable_object.cc b/atom/browser/api/trackable_object.cc index 77a936cde02c..5148df627c8d 100644 --- a/atom/browser/api/trackable_object.cc +++ b/atom/browser/api/trackable_object.cc @@ -37,15 +37,6 @@ TrackableObjectBase::~TrackableObjectBase() { cleanup_.Run(); } -void TrackableObjectBase::AfterInit(v8::Isolate* isolate) { - if (wrapped_) - AttachAsUserData(wrapped_); -} - -void TrackableObjectBase::MarkDestroyed() { - GetWrapper(isolate())->SetAlignedPointerInInternalField(0, nullptr); -} - base::Closure TrackableObjectBase::GetDestroyClosure() { return base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr()); } diff --git a/atom/browser/api/trackable_object.h b/atom/browser/api/trackable_object.h index 7c4ed03fe052..1c71d84e42c9 100644 --- a/atom/browser/api/trackable_object.h +++ b/atom/browser/api/trackable_object.h @@ -21,7 +21,7 @@ class SupportsUserData; namespace mate { // Users should use TrackableObject instead. -class TrackableObjectBase : public mate::EventEmitter { +class TrackableObjectBase { public: TrackableObjectBase(); @@ -32,13 +32,7 @@ class TrackableObjectBase : public mate::EventEmitter { void AttachAsUserData(base::SupportsUserData* wrapped); protected: - ~TrackableObjectBase() override; - - // mate::Wrappable: - void AfterInit(v8::Isolate* isolate) override; - - // Mark the JS object as destroyed. - void MarkDestroyed(); + virtual ~TrackableObjectBase(); // Returns a closure that can destroy the native class. base::Closure GetDestroyClosure(); @@ -65,8 +59,14 @@ class TrackableObjectBase : public mate::EventEmitter { // All instances of TrackableObject will be kept in a weak map and can be got // from its ID. template -class TrackableObject : public TrackableObjectBase { +class TrackableObject : public TrackableObjectBase, + public mate::EventEmitter { public: + // Mark the JS object as destroyed. + void MarkDestroyed() { + Wrappable::GetWrapper()->SetAlignedPointerInInternalField(0, nullptr); + } + // Finds out the TrackableObject from its ID in weak map. static T* FromWeakMapID(v8::Isolate* isolate, int32_t id) { if (!weak_map_) @@ -106,6 +106,7 @@ class TrackableObject : public TrackableObjectBase { protected: TrackableObject() {} + ~TrackableObject() override { RemoveFromWeakMap(); } @@ -113,41 +114,18 @@ class TrackableObject : public TrackableObjectBase { void AfterInit(v8::Isolate* isolate) override { if (!weak_map_) { weak_map_.reset(new atom::IDWeakMap); - RegisterDestructionCallback( - base::Bind(&TrackableObject::ReleaseAllWeakReferences)); } - weak_map_id_ = weak_map_->Add(isolate, GetWrapper(isolate)); - TrackableObjectBase::AfterInit(isolate); + weak_map_id_ = weak_map_->Add(isolate, Wrappable::GetWrapper()); + if (wrapped_) + AttachAsUserData(wrapped_); } private: - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override { - if (template_.IsEmpty()) { - auto templ = v8::ObjectTemplate::New(isolate); - T::BuildPrototype(isolate, templ); - template_.Reset(isolate, templ); - } - - return ObjectTemplateBuilder( - isolate, v8::Local::New(isolate, template_)); - } - - // Releases all weak references in weak map, called when app is terminating. - static void ReleaseAllWeakReferences() { - weak_map_.reset(); - } - - static v8::Persistent template_; static scoped_ptr weak_map_; DISALLOW_COPY_AND_ASSIGN(TrackableObject); }; -template -v8::Persistent TrackableObject::template_; - template scoped_ptr TrackableObject::weak_map_; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 9a59c3f9d412..f4add58e3a89 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -37,7 +37,6 @@ #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/common/web_preferences.h" -#include "net/cert/x509_certificate.h" #include "net/ssl/ssl_cert_request_info.h" #include "ppapi/host/ppapi_host.h" #include "ui/base/l10n/l10n_util.h" @@ -55,26 +54,6 @@ std::string g_custom_schemes = ""; // Custom schemes to be registered to handle service worker. std::string g_custom_service_worker_schemes = ""; -scoped_refptr ImportCertFromFile( - const base::FilePath& path) { - if (path.empty()) - return nullptr; - - std::string cert_data; - if (!base::ReadFileToString(path, &cert_data)) - return nullptr; - - net::CertificateList certs = - net::X509Certificate::CreateCertificateListFromBytes( - cert_data.data(), cert_data.size(), - net::X509Certificate::FORMAT_AUTO); - - if (certs.empty()) - return nullptr; - - return certs[0]; -} - } // namespace // static @@ -242,16 +221,6 @@ void AtomBrowserClient::SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, scoped_ptr delegate) { - // --client-certificate=`path` - auto cmd = base::CommandLine::ForCurrentProcess(); - if (cmd->HasSwitch(switches::kClientCertificate)) { - auto cert_path = cmd->GetSwitchValuePath(switches::kClientCertificate); - auto certificate = ImportCertFromFile(cert_path); - if (certificate.get()) - delegate->ContinueWithCertificate(certificate.get()); - return; - } - if (!cert_request_info->client_certs.empty() && delegate_) { delegate_->SelectClientCertificate( web_contents, cert_request_info, std::move(delegate)); diff --git a/atom/browser/atom_download_manager_delegate.cc b/atom/browser/atom_download_manager_delegate.cc index 16c0cf708b85..295747a33c4e 100644 --- a/atom/browser/atom_download_manager_delegate.cc +++ b/atom/browser/atom_download_manager_delegate.cc @@ -70,7 +70,9 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated( return; NativeWindow* window = nullptr; - auto relay = NativeWindowRelay::FromWebContents(item->GetWebContents()); + content::WebContents* web_contents = item->GetWebContents(); + auto relay = web_contents ? NativeWindowRelay::FromWebContents(web_contents) + : nullptr; if (relay) window = relay->window.get(); diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc index 68576a52f248..59ac258ea13a 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.cc +++ b/atom/browser/atom_resource_dispatcher_host_delegate.cc @@ -5,6 +5,7 @@ #include "atom/browser/atom_resource_dispatcher_host_delegate.h" #include "atom/browser/login_handler.h" +#include "atom/browser/web_contents_permission_helper.h" #include "atom/common/platform_util.h" #include "content/public/browser/browser_thread.h" #include "net/base/escape.h" @@ -14,20 +15,46 @@ using content::BrowserThread; namespace atom { +namespace { + +void OnOpenExternal(const GURL& escaped_url, + bool allowed) { + if (allowed) + platform_util::OpenExternal(escaped_url, true); +} + +void HandleExternalProtocolInUI( + const GURL& url, + const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter, + bool has_user_gesture) { + content::WebContents* web_contents = web_contents_getter.Run(); + if (!web_contents) + return; + + GURL escaped_url(net::EscapeExternalHandlerValue(url.spec())); + auto callback = base::Bind(&OnOpenExternal, escaped_url); + auto permission_helper = + WebContentsPermissionHelper::FromWebContents(web_contents); + permission_helper->RequestOpenExternalPermission(callback, has_user_gesture); +} + +} // namespace + AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() { } bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol( const GURL& url, int child_id, - const content::ResourceRequestInfo::WebContentsGetter&, + const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter, bool is_main_frame, ui::PageTransition transition, bool has_user_gesture) { - GURL escaped_url(net::EscapeExternalHandlerValue(url.spec())); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind( - base::IgnoreResult(platform_util::OpenExternal), escaped_url, true)); + base::Bind(&HandleExternalProtocolInUI, + url, + web_contents_getter, + has_user_gesture)); return true; } diff --git a/atom/browser/auto_updater_mac.mm b/atom/browser/auto_updater_mac.mm index a55cdd281265..9af920f59299 100644 --- a/atom/browser/auto_updater_mac.mm +++ b/atom/browser/auto_updater_mac.mm @@ -22,6 +22,12 @@ SQRLUpdater* g_updater = nil; } // namespace +namespace { + +bool g_update_available = false; + +} + // static void AutoUpdater::SetFeedURL(const std::string& feed) { if (g_updater == nil) { @@ -69,6 +75,7 @@ void AutoUpdater::CheckForUpdates() { take:1] subscribeNext:^(SQRLDownloadedUpdate *downloadedUpdate) { if (downloadedUpdate) { + g_update_available = true; SQRLUpdate* update = downloadedUpdate.update; // There is a new update that has been downloaded. delegate->OnUpdateDownloaded( @@ -77,6 +84,7 @@ void AutoUpdater::CheckForUpdates() { base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970), base::SysNSStringToUTF8(update.updateURL.absoluteString)); } else { + g_update_available = false; // When the completed event is sent with no update, then we know there // is no update available. delegate->OnUpdateNotAvailable(); @@ -89,11 +97,16 @@ void AutoUpdater::CheckForUpdates() { } void AutoUpdater::QuitAndInstall() { - [[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) { - Delegate* delegate = AutoUpdater::GetDelegate(); + Delegate* delegate = AutoUpdater::GetDelegate(); + if (g_update_available) { + [[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) { + if (delegate) + delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription)); + }]; + } else { if (delegate) - delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription)); - }]; + delegate->OnError("No update available, can't quit and install"); + } } } // namespace auto_updater diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index b60df0dd44a5..093209ef7c47 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -9,7 +9,10 @@ #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/native_window.h" #include "atom/browser/window_list.h" +#include "base/files/file_util.h" #include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "brightray/browser/brightray_paths.h" namespace atom { @@ -139,6 +142,11 @@ void Browser::WillFinishLaunching() { } void Browser::DidFinishLaunching() { + // Make sure the userData directory is created. + base::FilePath user_data; + if (PathService::Get(brightray::DIR_USER_DATA, &user_data)) + base::CreateDirectoryAndGetError(user_data, nullptr); + is_ready_ = true; FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching()); } @@ -187,8 +195,4 @@ void Browser::OnWindowAllClosed() { FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWindowAllClosed()); } -void Browser::PlatformThemeChanged() { - FOR_EACH_OBSERVER(BrowserObserver, observers_, OnPlatformThemeChanged()); -} - } // namespace atom diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 67aae152c317..8329b14bc2c4 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -82,6 +82,9 @@ class Browser : public WindowListObserver { // Set as default handler for a protocol. bool SetAsDefaultProtocolClient(const std::string& protocol); + // Query the current state of default handler for a protocol. + bool IsDefaultProtocolClient(const std::string& protocol); + #if defined(OS_MACOSX) // Hide the application. void Hide(); @@ -89,9 +92,6 @@ class Browser : public WindowListObserver { // Show the application. void Show(); - // Check if the system is in Dark Mode. - bool IsDarkMode(); - // Bounce the dock icon. enum BounceType { BOUNCE_CRITICAL = 0, @@ -151,9 +151,6 @@ class Browser : public WindowListObserver { // Request basic auth login. void RequestLogin(LoginHandler* login_handler); - // Tell the application that plaform's theme changed. - void PlatformThemeChanged(); - void AddObserver(BrowserObserver* obs) { observers_.AddObserver(obs); } diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index 6c7d4abaf645..d994bb4109bb 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -42,6 +42,10 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol) { return false; } +bool Browser::IsDefaultProtocolClient(const std::string& protocol) { + return false; +} + std::string Browser::GetExecutableFileVersion() const { return brightray::GetApplicationVersion(); } diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 0294894fcd61..c10369a2e7af 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -27,11 +27,6 @@ void Browser::Show() { [[AtomApplication sharedApplication] unhide:nil]; } -bool Browser::IsDarkMode() { - NSString *mode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; - return [mode isEqualToString: @"Dark"]; -} - void Browser::AddRecentDocument(const base::FilePath& path) { NSString* path_string = base::mac::FilePathToNSString(path); if (!path_string) @@ -65,6 +60,30 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol) { return return_code == noErr; } +bool Browser::IsDefaultProtocolClient(const std::string& protocol) { + if (protocol.empty()) + return false; + + NSString* identifier = [base::mac::MainBundle() bundleIdentifier]; + if (!identifier) + return false; + + NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()]; + + CFStringRef bundle = + LSCopyDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol_ns)); + NSString* bundleId = static_cast( + base::mac::CFTypeRefToNSObjectAutorelease(bundle)); + if (!bundleId) + return false; + + // Ensure the comparison is case-insensitive + // as LS does not persist the case of the bundle id. + NSComparisonResult result = + [bundleId caseInsensitiveCompare:identifier]; + return result == NSOrderedSame; +} + void Browser::SetAppUserModelID(const base::string16& name) { } diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index da327eb90a02..f6d76bc13fb3 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -45,8 +45,6 @@ class BrowserObserver { // The browser requests HTTP login. virtual void OnLogin(LoginHandler* login_handler) {} - virtual void OnPlatformThemeChanged() {} - protected: virtual ~BrowserObserver() {} }; diff --git a/atom/browser/browser_win.cc b/atom/browser/browser_win.cc index 9531406f8cd4..7b1402ba2d2c 100644 --- a/atom/browser/browser_win.cc +++ b/atom/browser/browser_win.cc @@ -225,6 +225,50 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol) { return true; } +bool Browser::IsDefaultProtocolClient(const std::string& protocol) { + if (protocol.empty()) + return false; + + base::FilePath path; + if (!PathService::Get(base::FILE_EXE, &path)) { + LOG(ERROR) << "Error getting app exe path"; + return false; + } + + // Main Registry Key + HKEY root = HKEY_CURRENT_USER; + std::string keyPathStr = "Software\\Classes\\" + protocol; + std::wstring keyPath = std::wstring(keyPathStr.begin(), keyPathStr.end()); + + // Command Key + std::string cmdPathStr = keyPathStr + "\\shell\\open\\command"; + std::wstring cmdPath = std::wstring(cmdPathStr.begin(), cmdPathStr.end()); + + base::win::RegKey key; + base::win::RegKey commandKey; + if (FAILED(key.Open(root, keyPath.c_str(), KEY_ALL_ACCESS))) + // Key doesn't exist, we can confirm that it is not set + return false; + + if (FAILED(commandKey.Open(root, cmdPath.c_str(), KEY_ALL_ACCESS))) + // Key doesn't exist, we can confirm that it is not set + return false; + + std::wstring keyVal; + if (FAILED(commandKey.ReadValue(L"", &keyVal))) + // Default value not set, we can confirm that it is not set + return false; + + std::wstring exePath(path.value()); + std::wstring exe = L"\"" + exePath + L"\" \"%1\""; + if (keyVal == exe) { + // Default value is the same as current file path + return true; + } else { + return false; + } +} + PCWSTR Browser::GetAppUserModelID() { if (app_user_model_id_.empty()) { SetAppUserModelID(base::ReplaceStringPlaceholders( diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index f4db929bf575..7662162ab618 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -24,9 +24,6 @@ // Don't add the "Enter Full Screen" menu item automatically. [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"]; - // Add observer to monitor the system's Dark Mode theme. - [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(platformThemeChanged:) name:@"AppleInterfaceThemeChangedNotification" object:nil]; - atom::Browser::Get()->WillFinishLaunching(); } @@ -62,8 +59,4 @@ return flag; } -- (void)platformThemeChanged:(NSNotification *)notify { - atom::Browser::Get()->PlatformThemeChanged(); -} - @end diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index d7ed86165b9f..2379bdd053c5 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -54,6 +54,7 @@ NativeWindow::NativeWindow( enable_larger_than_screen_(false), is_closed_(false), has_dialog_attached_(false), + sheet_offset_(0.0), aspect_ratio_(0.0), inspectable_web_contents_(inspectable_web_contents), weak_factory_(this) { @@ -254,6 +255,14 @@ gfx::Size NativeWindow::GetMaximumSize() { return GetSizeConstraints().GetMaximumSize(); } +void NativeWindow::SetSheetOffset(const double offset) { + sheet_offset_ = offset; +} + +double NativeWindow::GetSheetOffset() { + return sheet_offset_; +} + void NativeWindow::SetRepresentedFilename(const std::string& filename) { } diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index f91d041c057e..f3acf6222314 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -123,6 +123,8 @@ class NativeWindow : public base::SupportsUserData, virtual gfx::Size GetMinimumSize(); virtual void SetMaximumSize(const gfx::Size& size); virtual gfx::Size GetMaximumSize(); + virtual void SetSheetOffset(const double offset); + virtual double GetSheetOffset(); virtual void SetResizable(bool resizable) = 0; virtual bool IsResizable() = 0; virtual void SetMovable(bool movable) = 0; @@ -326,6 +328,9 @@ class NativeWindow : public base::SupportsUserData, // it should be cancelled when we can prove that the window is responsive. base::CancelableClosure window_unresposive_closure_; + // Used to display sheets at the appropriate vertical offset + double sheet_offset_; + // Used to maintain the aspect ratio of a view which is inside of the // content view. double aspect_ratio_; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index b24e828e578d..04569d8f50f9 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -243,6 +243,13 @@ bool ScopedDisableResize::disable_resize_ = false; return NO; } +- (NSRect)window:(NSWindow*)window + willPositionSheet:(NSWindow*)sheet usingRect:(NSRect)rect { + NSView* view = window.contentView; + rect.origin.y = view.frame.size.height - shell_->GetSheetOffset(); + return rect; +} + @end @interface AtomNSWindow : NSWindow { @@ -595,10 +602,16 @@ bool NativeWindowMac::IsVisible() { } void NativeWindowMac::Maximize() { + if (IsMaximized()) + return; + [window_ zoom:nil]; } void NativeWindowMac::Unmaximize() { + if (!IsMaximized()) + return; + [window_ zoom:nil]; } diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index 86bfe12079ad..3143cd3b2575 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -4,15 +4,16 @@ #include "atom/browser/net/atom_network_delegate.h" -#include #include #include "atom/common/native_mate_converters/net_converter.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "brightray/browser/net/devtools_network_transaction.h" #include "content/public/browser/browser_thread.h" #include "net/url_request/url_request.h" +using brightray::DevToolsNetworkTransaction; using content::BrowserThread; namespace atom { @@ -226,6 +227,12 @@ void AtomNetworkDelegate::SetResponseListenerInIO( response_listeners_[type] = { patterns, callback }; } +void AtomNetworkDelegate::SetDevToolsNetworkEmulationClientId( + const std::string& client_id) { + base::AutoLock auto_lock(lock_); + client_id_ = client_id; +} + int AtomNetworkDelegate::OnBeforeURLRequest( net::URLRequest* request, const net::CompletionCallback& callback, @@ -241,6 +248,16 @@ int AtomNetworkDelegate::OnBeforeSendHeaders( net::URLRequest* request, const net::CompletionCallback& callback, net::HttpRequestHeaders* headers) { + std::string client_id; + { + base::AutoLock auto_lock(lock_); + client_id = client_id_; + } + + if (!client_id.empty()) + headers->SetHeader( + DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId, + client_id); if (!ContainsKey(response_listeners_, kOnBeforeSendHeaders)) return brightray::NetworkDelegate::OnBeforeSendHeaders( request, callback, headers); diff --git a/atom/browser/net/atom_network_delegate.h b/atom/browser/net/atom_network_delegate.h index ee159df60f13..701a953c265e 100644 --- a/atom/browser/net/atom_network_delegate.h +++ b/atom/browser/net/atom_network_delegate.h @@ -7,9 +7,11 @@ #include #include +#include #include "brightray/browser/network_delegate.h" #include "base/callback.h" +#include "base/synchronization/lock.h" #include "base/values.h" #include "extensions/common/url_pattern.h" #include "net/base/net_errors.h" @@ -68,6 +70,8 @@ class AtomNetworkDelegate : public brightray::NetworkDelegate { const URLPatterns& patterns, const ResponseListener& callback); + void SetDevToolsNetworkEmulationClientId(const std::string& client_id); + protected: // net::NetworkDelegate: int OnBeforeURLRequest(net::URLRequest* request, @@ -116,6 +120,11 @@ class AtomNetworkDelegate : public brightray::NetworkDelegate { std::map response_listeners_; std::map callbacks_; + base::Lock lock_; + + // Client id for devtools network emulation. + std::string client_id_; + DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate); }; diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 50522ddda178..9d04617b2098 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 0.37.6 + 0.37.8 CFBundleShortVersionString - 0.37.6 + 0.37.8 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 473cd151f9ac..9f74c14d4461 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,37,6,0 - PRODUCTVERSION 0,37,6,0 + FILEVERSION 0,37,8,0 + PRODUCTVERSION 0,37,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "0.37.6" + VALUE "FileVersion", "0.37.8" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "0.37.6" + VALUE "ProductVersion", "0.37.8" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/accelerator_util_mac.mm b/atom/browser/ui/accelerator_util_mac.mm index be631b021249..d3395884c97f 100644 --- a/atom/browser/ui/accelerator_util_mac.mm +++ b/atom/browser/ui/accelerator_util_mac.mm @@ -13,12 +13,6 @@ namespace accelerator_util { void SetPlatformAccelerator(ui::Accelerator* accelerator) { unichar character; unichar characterIgnoringModifiers; - ui::MacKeyCodeForWindowsKeyCode(accelerator->key_code(), - 0, - &character, - &characterIgnoringModifiers); - NSString* characters = - [[[NSString alloc] initWithCharacters:&character length:1] autorelease]; NSUInteger modifiers = (accelerator->IsCtrlDown() ? NSControlKeyMask : 0) | @@ -26,6 +20,18 @@ void SetPlatformAccelerator(ui::Accelerator* accelerator) { (accelerator->IsAltDown() ? NSAlternateKeyMask : 0) | (accelerator->IsShiftDown() ? NSShiftKeyMask : 0); + ui::MacKeyCodeForWindowsKeyCode(accelerator->key_code(), + modifiers, + &character, + &characterIgnoringModifiers); + + if (character != characterIgnoringModifiers) { + modifiers ^= NSShiftKeyMask; + } + + NSString* characters = + [[[NSString alloc] initWithCharacters:&character length:1] autorelease]; + scoped_ptr platform_accelerator( new ui::PlatformAcceleratorCocoa(characters, modifiers)); accelerator->set_platform_accelerator(std::move(platform_accelerator)); diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index d018e1d09dbc..7c944832d88d 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -91,4 +91,12 @@ void WebContentsPermissionHelper::RequestPointerLockPermission( user_gesture); } +void WebContentsPermissionHelper::RequestOpenExternalPermission( + const base::Callback& callback, + bool user_gesture) { + RequestPermission((content::PermissionType)(PermissionType::OPEN_EXTERNAL), + callback, + user_gesture); +} + } // namespace atom diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 90ae6dff56f5..89da64b75833 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -19,7 +19,8 @@ class WebContentsPermissionHelper enum class PermissionType { POINTER_LOCK = static_cast(content::PermissionType::NUM) + 1, - FULLSCREEN + FULLSCREEN, + OPEN_EXTERNAL, }; void RequestFullscreenPermission( @@ -30,6 +31,9 @@ class WebContentsPermissionHelper void RequestWebNotificationPermission( const base::Callback& callback); void RequestPointerLockPermission(bool user_gesture); + void RequestOpenExternalPermission( + const base::Callback& callback, + bool user_gesture); private: explicit WebContentsPermissionHelper(content::WebContents* web_contents); diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index 4bfb0ed4c1be..1b0ea799e7d7 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/atom/common/api/atom_api_asar.cc @@ -18,21 +18,39 @@ namespace { -v8::Persistent template_; - -class Archive : public mate::Wrappable { +class Archive : public mate::Wrappable { public: static v8::Local Create(v8::Isolate* isolate, const base::FilePath& path) { scoped_ptr archive(new asar::Archive(path)); if (!archive->Init()) return v8::False(isolate); - return (new Archive(std::move(archive)))->GetWrapper(isolate); + return (new Archive(isolate, std::move(archive)))->GetWrapper(); + } + + static void BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetProperty("path", &Archive::GetPath) + .SetMethod("getFileInfo", &Archive::GetFileInfo) + .SetMethod("stat", &Archive::Stat) + .SetMethod("readdir", &Archive::Readdir) + .SetMethod("realpath", &Archive::Realpath) + .SetMethod("copyFileOut", &Archive::CopyFileOut) + .SetMethod("getFd", &Archive::GetFD) + .SetMethod("destroy", &Archive::Destroy); } protected: - explicit Archive(scoped_ptr archive) - : archive_(std::move(archive)) {} + Archive(v8::Isolate* isolate, scoped_ptr archive) + : archive_(std::move(archive)) { + Init(isolate); + } + + // Returns the path of the file. + base::FilePath GetPath() { + return archive_->path(); + } // Reads the offset and size of file. v8::Local GetFileInfo(v8::Isolate* isolate, @@ -101,24 +119,6 @@ class Archive : public mate::Wrappable { archive_.reset(); } - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) { - if (template_.IsEmpty()) - template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) - .SetValue("path", archive_->path()) - .SetMethod("getFileInfo", &Archive::GetFileInfo) - .SetMethod("stat", &Archive::Stat) - .SetMethod("readdir", &Archive::Readdir) - .SetMethod("realpath", &Archive::Realpath) - .SetMethod("copyFileOut", &Archive::CopyFileOut) - .SetMethod("getFd", &Archive::GetFD) - .SetMethod("destroy", &Archive::Destroy) - .Build()); - - return mate::ObjectTemplateBuilder( - isolate, v8::Local::New(isolate, template_)); - } - private: scoped_ptr archive_; diff --git a/atom/common/api/atom_api_id_weak_map.cc b/atom/common/api/atom_api_id_weak_map.cc index f32e33682dff..0d3ddae3cc09 100644 --- a/atom/common/api/atom_api_id_weak_map.cc +++ b/atom/common/api/atom_api_id_weak_map.cc @@ -12,7 +12,7 @@ namespace atom { namespace api { -IDWeakMap::IDWeakMap() { +IDWeakMap::IDWeakMap(v8::Isolate* isolate) { } IDWeakMap::~IDWeakMap() { @@ -52,8 +52,8 @@ void IDWeakMap::BuildPrototype(v8::Isolate* isolate, } // static -mate::Wrappable* IDWeakMap::Create(v8::Isolate* isolate) { - return new IDWeakMap; +mate::WrappableBase* IDWeakMap::Create(v8::Isolate* isolate) { + return new IDWeakMap(isolate); } } // namespace api diff --git a/atom/common/api/atom_api_id_weak_map.h b/atom/common/api/atom_api_id_weak_map.h index 0cf656f455bc..616112ffe6f4 100644 --- a/atom/common/api/atom_api_id_weak_map.h +++ b/atom/common/api/atom_api_id_weak_map.h @@ -13,15 +13,15 @@ namespace atom { namespace api { -class IDWeakMap : public mate::Wrappable { +class IDWeakMap : public mate::Wrappable { public: - static mate::Wrappable* Create(v8::Isolate* isolate); + static mate::WrappableBase* Create(v8::Isolate* isolate); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); protected: - IDWeakMap(); + explicit IDWeakMap(v8::Isolate* isolate); ~IDWeakMap(); private: diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index 3dda326f59fd..1c90fe7080fb 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -168,35 +168,15 @@ bool ReadImageSkiaFromICO(gfx::ImageSkia* image, const base::FilePath& path) { } #endif -v8::Persistent template_; - } // namespace -NativeImage::NativeImage() {} - -NativeImage::NativeImage(const gfx::Image& image) : image_(image) {} +NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image) + : image_(image) { + Init(isolate); +} NativeImage::~NativeImage() {} -mate::ObjectTemplateBuilder NativeImage::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - if (template_.IsEmpty()) - template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) - .SetMethod("toPng", &NativeImage::ToPNG) - .SetMethod("toJpeg", &NativeImage::ToJPEG) - .SetMethod("getNativeHandle", &NativeImage::GetNativeHandle) - .SetMethod("toDataURL", &NativeImage::ToDataURL) - .SetMethod("toDataUrl", &NativeImage::ToDataURL) // deprecated. - .SetMethod("isEmpty", &NativeImage::IsEmpty) - .SetMethod("getSize", &NativeImage::GetSize) - .SetMethod("setTemplateImage", &NativeImage::SetTemplateImage) - .SetMethod("isTemplateImage", &NativeImage::IsTemplateImage) - .Build()); - - return mate::ObjectTemplateBuilder( - isolate, v8::Local::New(isolate, template_)); -} - v8::Local NativeImage::ToPNG(v8::Isolate* isolate) { scoped_refptr png = image_.As1xPNGBytes(); return node::Buffer::Copy(isolate, @@ -255,13 +235,13 @@ bool NativeImage::IsTemplateImage() { // static mate::Handle NativeImage::CreateEmpty(v8::Isolate* isolate) { - return mate::CreateHandle(isolate, new NativeImage); + return mate::CreateHandle(isolate, new NativeImage(isolate, gfx::Image())); } // static mate::Handle NativeImage::Create( v8::Isolate* isolate, const gfx::Image& image) { - return mate::CreateHandle(isolate, new NativeImage(image)); + return mate::CreateHandle(isolate, new NativeImage(isolate, image)); } // static @@ -330,6 +310,21 @@ mate::Handle NativeImage::CreateFromDataURL( return CreateEmpty(isolate); } +// static +void NativeImage::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("toPng", &NativeImage::ToPNG) + .SetMethod("toJpeg", &NativeImage::ToJPEG) + .SetMethod("getNativeHandle", &NativeImage::GetNativeHandle) + .SetMethod("toDataURL", &NativeImage::ToDataURL) + .SetMethod("toDataUrl", &NativeImage::ToDataURL) // deprecated. + .SetMethod("isEmpty", &NativeImage::IsEmpty) + .SetMethod("getSize", &NativeImage::GetSize) + .SetMethod("setTemplateImage", &NativeImage::SetTemplateImage) + .SetMethod("isTemplateImage", &NativeImage::IsTemplateImage); +} + } // namespace api } // namespace atom diff --git a/atom/common/api/atom_api_native_image.h b/atom/common/api/atom_api_native_image.h index 145f5ff1dcdc..79844604706c 100644 --- a/atom/common/api/atom_api_native_image.h +++ b/atom/common/api/atom_api_native_image.h @@ -29,7 +29,7 @@ namespace atom { namespace api { -class NativeImage : public mate::Wrappable { +class NativeImage : public mate::Wrappable { public: static mate::Handle CreateEmpty(v8::Isolate* isolate); static mate::Handle Create( @@ -45,18 +45,14 @@ class NativeImage : public mate::Wrappable { static mate::Handle CreateFromDataURL( v8::Isolate* isolate, const GURL& url); - // The default constructor should only be used by image_converter.cc. - NativeImage(); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); const gfx::Image& image() const { return image_; } protected: - explicit NativeImage(const gfx::Image& image); - virtual ~NativeImage(); - - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; + NativeImage(v8::Isolate* isolate, const gfx::Image& image); + ~NativeImage() override; private: v8::Local ToPNG(v8::Isolate* isolate); diff --git a/atom/common/api/atom_api_v8_util.cc b/atom/common/api/atom_api_v8_util.cc index 0ebd939398f1..109f9f0f36c2 100644 --- a/atom/common/api/atom_api_v8_util.cc +++ b/atom/common/api/atom_api_v8_util.cc @@ -4,7 +4,9 @@ #include -#include "atom/common/api/object_life_monitor.h" +#include "atom/common/api/remote_callback_freer.h" +#include "atom/common/api/remote_object_freer.h" +#include "atom/common/native_mate_converters/content_converter.h" #include "atom/common/node_includes.h" #include "native_mate/dictionary.h" #include "v8/include/v8-profiler.h" @@ -51,12 +53,6 @@ int32_t GetObjectHash(v8::Local object) { return object->GetIdentityHash(); } -void SetDestructor(v8::Isolate* isolate, - v8::Local object, - v8::Local callback) { - atom::ObjectLifeMonitor::BindTo(isolate, object, callback); -} - void TakeHeapSnapshot(v8::Isolate* isolate) { isolate->GetHeapProfiler()->TakeHeapSnapshot(); } @@ -68,8 +64,9 @@ void Initialize(v8::Local exports, v8::Local unused, dict.SetMethod("setHiddenValue", &SetHiddenValue); dict.SetMethod("deleteHiddenValue", &DeleteHiddenValue); dict.SetMethod("getObjectHash", &GetObjectHash); - dict.SetMethod("setDestructor", &SetDestructor); dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot); + dict.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo); + dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo); } } // namespace diff --git a/atom/common/api/object_life_monitor.cc b/atom/common/api/object_life_monitor.cc index 916ad8a5177a..ffcc0d718420 100644 --- a/atom/common/api/object_life_monitor.cc +++ b/atom/common/api/object_life_monitor.cc @@ -10,30 +10,28 @@ namespace atom { -// static -void ObjectLifeMonitor::BindTo(v8::Isolate* isolate, - v8::Local target, - v8::Local destructor) { - new ObjectLifeMonitor(isolate, target, destructor); -} - ObjectLifeMonitor::ObjectLifeMonitor(v8::Isolate* isolate, - v8::Local target, - v8::Local destructor) + v8::Local target) : isolate_(isolate), context_(isolate, isolate->GetCurrentContext()), target_(isolate, target), - destructor_(isolate, destructor), weak_ptr_factory_(this) { target_.SetWeak(this, OnObjectGC, v8::WeakCallbackType::kParameter); } +ObjectLifeMonitor::~ObjectLifeMonitor() { + if (target_.IsEmpty()) + return; + target_.ClearWeak(); + target_.Reset(); +} + // static void ObjectLifeMonitor::OnObjectGC( const v8::WeakCallbackInfo& data) { ObjectLifeMonitor* self = data.GetParameter(); self->target_.Reset(); - self->RunCallback(); + self->RunDestructor(); data.SetSecondPassCallback(Free); } @@ -43,13 +41,4 @@ void ObjectLifeMonitor::Free( delete data.GetParameter(); } -void ObjectLifeMonitor::RunCallback() { - v8::HandleScope handle_scope(isolate_); - v8::Local context = v8::Local::New( - isolate_, context_); - v8::Context::Scope context_scope(context); - v8::Local::New(isolate_, destructor_)->Call( - context->Global(), 0, nullptr); -} - } // namespace atom diff --git a/atom/common/api/object_life_monitor.h b/atom/common/api/object_life_monitor.h index 82d923fcedb7..59d5fdb5cffd 100644 --- a/atom/common/api/object_life_monitor.h +++ b/atom/common/api/object_life_monitor.h @@ -12,25 +12,19 @@ namespace atom { class ObjectLifeMonitor { - public: - static void BindTo(v8::Isolate* isolate, - v8::Local target, - v8::Local destructor); + protected: + ObjectLifeMonitor(v8::Isolate* isolate, v8::Local target); + virtual ~ObjectLifeMonitor(); + + virtual void RunDestructor() = 0; private: - ObjectLifeMonitor(v8::Isolate* isolate, - v8::Local target, - v8::Local destructor); - static void OnObjectGC(const v8::WeakCallbackInfo& data); static void Free(const v8::WeakCallbackInfo& data); - void RunCallback(); - v8::Isolate* isolate_; v8::Global context_; v8::Global target_; - v8::Global destructor_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/atom/common/api/remote_callback_freer.cc b/atom/common/api/remote_callback_freer.cc new file mode 100644 index 000000000000..7bc377efc5e7 --- /dev/null +++ b/atom/common/api/remote_callback_freer.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/api/remote_callback_freer.h" + +#include "atom/common/api/api_messages.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" + +namespace atom { + +// static +void RemoteCallbackFreer::BindTo(v8::Isolate* isolate, + v8::Local target, + int object_id, + content::WebContents* web_contents) { + new RemoteCallbackFreer(isolate, target, object_id, web_contents); +} + +RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate, + v8::Local target, + int object_id, + content::WebContents* web_contents) + : ObjectLifeMonitor(isolate, target), + content::WebContentsObserver(web_contents), + object_id_(object_id) { +} + +RemoteCallbackFreer::~RemoteCallbackFreer() { +} + +void RemoteCallbackFreer::RunDestructor() { + base::string16 channel = + base::ASCIIToUTF16("ELECTRON_RENDERER_RELEASE_CALLBACK"); + base::ListValue args; + args.AppendInteger(object_id_); + Send(new AtomViewMsg_Message(routing_id(), channel, args)); + + Observe(nullptr); +} + +void RemoteCallbackFreer::RenderViewDeleted(content::RenderViewHost*) { + delete this; +} + +} // namespace atom diff --git a/atom/common/api/remote_callback_freer.h b/atom/common/api/remote_callback_freer.h new file mode 100644 index 000000000000..8fe80c8d4774 --- /dev/null +++ b/atom/common/api/remote_callback_freer.h @@ -0,0 +1,40 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_ +#define ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_ +#include "atom/common/api/object_life_monitor.h" +#include "content/public/browser/web_contents_observer.h" + +namespace atom { + +class RemoteCallbackFreer : public ObjectLifeMonitor, + public content::WebContentsObserver { + public: + static void BindTo(v8::Isolate* isolate, + v8::Local target, + int object_id, + content::WebContents* web_conents); + + protected: + RemoteCallbackFreer(v8::Isolate* isolate, + v8::Local target, + int object_id, + content::WebContents* web_conents); + ~RemoteCallbackFreer() override; + + void RunDestructor() override; + + // content::WebContentsObserver: + void RenderViewDeleted(content::RenderViewHost*) override; + + private: + int object_id_; + + DISALLOW_COPY_AND_ASSIGN(RemoteCallbackFreer); +}; + +} // namespace atom + +#endif // ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_ diff --git a/atom/common/api/remote_object_freer.cc b/atom/common/api/remote_object_freer.cc new file mode 100644 index 000000000000..1762f1d1e068 --- /dev/null +++ b/atom/common/api/remote_object_freer.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/api/remote_object_freer.h" + +#include "atom/common/api/api_messages.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "content/public/renderer/render_view.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "third_party/WebKit/public/web/WebView.h" + +using blink::WebLocalFrame; +using blink::WebView; + +namespace atom { + +namespace { + +content::RenderView* GetCurrentRenderView() { + WebLocalFrame* frame = WebLocalFrame::frameForCurrentContext(); + if (!frame) + return nullptr; + + WebView* view = frame->view(); + if (!view) + return nullptr; // can happen during closing. + + return content::RenderView::FromWebView(view); +} + +} // namespace + +// static +void RemoteObjectFreer::BindTo( + v8::Isolate* isolate, v8::Local target, int object_id) { + new RemoteObjectFreer(isolate, target, object_id); +} + +RemoteObjectFreer::RemoteObjectFreer( + v8::Isolate* isolate, v8::Local target, int object_id) + : ObjectLifeMonitor(isolate, target), + object_id_(object_id) { +} + +RemoteObjectFreer::~RemoteObjectFreer() { +} + +void RemoteObjectFreer::RunDestructor() { + content::RenderView* render_view = GetCurrentRenderView(); + if (!render_view) + return; + + base::string16 channel = base::ASCIIToUTF16("ipc-message"); + base::ListValue args; + args.AppendString("ELECTRON_BROWSER_DEREFERENCE"); + args.AppendInteger(object_id_); + render_view->Send( + new AtomViewHostMsg_Message(render_view->GetRoutingID(), channel, args)); +} + +} // namespace atom diff --git a/atom/common/api/remote_object_freer.h b/atom/common/api/remote_object_freer.h new file mode 100644 index 000000000000..c2b5d8b7d30b --- /dev/null +++ b/atom/common/api/remote_object_freer.h @@ -0,0 +1,32 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_ +#define ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_ + +#include "atom/common/api/object_life_monitor.h" + +namespace atom { + +class RemoteObjectFreer : public ObjectLifeMonitor { + public: + static void BindTo( + v8::Isolate* isolate, v8::Local target, int object_id); + + protected: + RemoteObjectFreer( + v8::Isolate* isolate, v8::Local target, int object_id); + ~RemoteObjectFreer() override; + + void RunDestructor() override; + + private: + int object_id_; + + DISALLOW_COPY_AND_ASSIGN(RemoteObjectFreer); +}; + +} // namespace atom + +#endif // ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_ diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 65921c77581a..4ce73bc0aeec 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 0 #define ATOM_MINOR_VERSION 37 -#define ATOM_PATCH_VERSION 6 +#define ATOM_PATCH_VERSION 8 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/native_mate_converters/content_converter.cc b/atom/common/native_mate_converters/content_converter.cc index f5d81d085bc1..154dcf88ce37 100644 --- a/atom/common/native_mate_converters/content_converter.cc +++ b/atom/common/native_mate_converters/content_converter.cc @@ -145,6 +145,8 @@ v8::Local Converter::ToV8( return StringToV8(isolate, "pointerLock"); else if (val == (content::PermissionType)(PermissionType::FULLSCREEN)) return StringToV8(isolate, "fullscreen"); + else if (val == (content::PermissionType)(PermissionType::OPEN_EXTERNAL)) + return StringToV8(isolate, "openExternal"); return StringToV8(isolate, "unknown"); } @@ -178,4 +180,17 @@ v8::Local Converter::ToV8( return atom::api::WebContents::CreateFrom(isolate, val).ToV8(); } +// static +bool Converter::FromV8( + v8::Isolate* isolate, + v8::Local val, + content::WebContents** out) { + atom::api::WebContents* web_contents = nullptr; + if (!ConvertFromV8(isolate, val, &web_contents) || !web_contents) + return false; + + *out = web_contents->web_contents(); + return true; +} + } // namespace mate diff --git a/atom/common/native_mate_converters/content_converter.h b/atom/common/native_mate_converters/content_converter.h index b1a42b6897ca..f2e7211ce5d1 100644 --- a/atom/common/native_mate_converters/content_converter.h +++ b/atom/common/native_mate_converters/content_converter.h @@ -57,6 +57,8 @@ template<> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, content::WebContents* val); + static bool FromV8(v8::Isolate* isolate, v8::Local val, + content::WebContents** out); }; } // namespace mate diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 86fb3a6d03a3..31105886eb7c 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -44,6 +44,7 @@ REFERENCE_MODULE(atom_browser_power_save_blocker); REFERENCE_MODULE(atom_browser_protocol); REFERENCE_MODULE(atom_browser_global_shortcut); REFERENCE_MODULE(atom_browser_session); +REFERENCE_MODULE(atom_browser_system_preferences); REFERENCE_MODULE(atom_browser_tray); REFERENCE_MODULE(atom_browser_web_contents); REFERENCE_MODULE(atom_browser_web_view_manager); diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index ce28cc98facf..562171d51f25 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -119,9 +119,6 @@ const char kPpapiFlashPath[] = "ppapi-flash-path"; // Ppapi Flash version. const char kPpapiFlashVersion[] = "ppapi-flash-version"; -// Path to client certificate. -const char kClientCertificate[] = "client-certificate"; - // Disable HTTP cache. const char kDisableHttpCache[] = "disable-http-cache"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 52d64c00d51a..5746a7bbfe54 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -68,7 +68,6 @@ namespace switches { extern const char kEnablePlugins[]; extern const char kPpapiFlashPath[]; extern const char kPpapiFlashVersion[]; -extern const char kClientCertificate[]; extern const char kDisableHttpCache[]; extern const char kRegisterStandardSchemes[]; extern const char kRegisterServiceWorkerSchemes[]; diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index e00b901bfff6..0eebc94fc141 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -54,8 +54,9 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback { } // namespace -WebFrame::WebFrame() +WebFrame::WebFrame(v8::Isolate* isolate) : web_frame_(blink::WebLocalFrame::frameForCurrentContext()) { + Init(isolate); } WebFrame::~WebFrame() { @@ -67,7 +68,7 @@ void WebFrame::SetName(const std::string& name) { double WebFrame::SetZoomLevel(double level) { double ret = web_frame_->view()->setZoomLevel(level); - mate::EmitEvent(isolate(), GetWrapper(isolate()), "zoom-level-changed", ret); + mate::EmitEvent(isolate(), GetWrapper(), "zoom-level-changed", ret); return ret; } @@ -162,9 +163,15 @@ void WebFrame::ExecuteJavaScript(const base::string16& code, callback.release()); } -mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) +// static +mate::Handle WebFrame::Create(v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new WebFrame(isolate)); +} + +// static +void WebFrame::BuildPrototype( + v8::Isolate* isolate, v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) .SetMethod("setName", &WebFrame::SetName) .SetMethod("setZoomLevel", &WebFrame::SetZoomLevel) .SetMethod("getZoomLevel", &WebFrame::GetZoomLevel) @@ -187,11 +194,6 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( .SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript); } -// static -mate::Handle WebFrame::Create(v8::Isolate* isolate) { - return CreateHandle(isolate, new WebFrame); -} - } // namespace api } // namespace atom diff --git a/atom/renderer/api/atom_api_web_frame.h b/atom/renderer/api/atom_api_web_frame.h index d55b24fd25ea..e1eeb224930b 100644 --- a/atom/renderer/api/atom_api_web_frame.h +++ b/atom/renderer/api/atom_api_web_frame.h @@ -26,13 +26,16 @@ namespace api { class SpellCheckClient; -class WebFrame : public mate::Wrappable { +class WebFrame : public mate::Wrappable { public: static mate::Handle Create(v8::Isolate* isolate); + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + private: - WebFrame(); - virtual ~WebFrame(); + explicit WebFrame(v8::Isolate* isolate); + ~WebFrame() override; void SetName(const std::string& name); @@ -66,10 +69,6 @@ class WebFrame : public mate::Wrappable { // Excecuting scripts. void ExecuteJavaScript(const base::string16& code, mate::Arguments* args); - // mate::Wrappable: - virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate); - scoped_ptr spell_check_client_; blink::WebLocalFrame* web_frame_; diff --git a/chromium_src/chrome/browser/certificate_manager_model.cc b/chromium_src/chrome/browser/certificate_manager_model.cc new file mode 100644 index 000000000000..b0db6edc357c --- /dev/null +++ b/chromium_src/chrome/browser/certificate_manager_model.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/certificate_manager_model.h" + +#include + +#include "base/bind.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_context.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "net/base/crypto_module.h" +#include "net/base/net_errors.h" +#include "net/cert/nss_cert_database.h" +#include "net/cert/x509_certificate.h" + +using content::BrowserThread; + +namespace { + +net::NSSCertDatabase* g_nss_cert_database = nullptr; + +net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext( + content::ResourceContext* context, + const base::Callback& callback) { + // This initialization is not thread safe. This CHECK ensures that this code + // is only run on a single thread. + CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + if (!g_nss_cert_database) { + // Linux has only a single persistent slot compared to ChromeOS's separate + // public and private slot. + // Redirect any slot usage to this persistent slot on Linux. + g_nss_cert_database = new net::NSSCertDatabase( + crypto::ScopedPK11Slot( + crypto::GetPersistentNSSKeySlot()) /* public slot */, + crypto::ScopedPK11Slot( + crypto::GetPersistentNSSKeySlot()) /* private slot */); + } + return g_nss_cert_database; +} + +} // namespace + +// CertificateManagerModel is created on the UI thread. It needs a +// NSSCertDatabase handle (and on ChromeOS it needs to get the TPM status) which +// needs to be done on the IO thread. +// +// The initialization flow is roughly: +// +// UI thread IO Thread +// +// CertificateManagerModel::Create +// \--------------------------------------v +// CertificateManagerModel::GetCertDBOnIOThread +// | +// GetNSSCertDatabaseForResourceContext +// | +// CertificateManagerModel::DidGetCertDBOnIOThread +// v--------------------------------------/ +// CertificateManagerModel::DidGetCertDBOnUIThread +// | +// new CertificateManagerModel +// | +// callback + +// static +void CertificateManagerModel::Create( + content::BrowserContext* browser_context, + const CreationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&CertificateManagerModel::GetCertDBOnIOThread, + browser_context->GetResourceContext(), + callback)); +} + +CertificateManagerModel::CertificateManagerModel( + net::NSSCertDatabase* nss_cert_database, + bool is_user_db_available) + : cert_db_(nss_cert_database), + is_user_db_available_(is_user_db_available) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +CertificateManagerModel::~CertificateManagerModel() { +} + +int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, + const std::string& data, + const base::string16& password, + bool is_extractable, + net::CertificateList* imported_certs) { + return cert_db_->ImportFromPKCS12(module, data, password, + is_extractable, imported_certs); +} + +int CertificateManagerModel::ImportUserCert(const std::string& data) { + return cert_db_->ImportUserCert(data); +} + +bool CertificateManagerModel::ImportCACerts( + const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported) { + return cert_db_->ImportCACerts(certificates, trust_bits, not_imported); +} + +bool CertificateManagerModel::ImportServerCert( + const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported) { + return cert_db_->ImportServerCert(certificates, trust_bits, + not_imported); +} + +bool CertificateManagerModel::SetCertTrust( + const net::X509Certificate* cert, + net::CertType type, + net::NSSCertDatabase::TrustBits trust_bits) { + return cert_db_->SetCertTrust(cert, type, trust_bits); +} + +bool CertificateManagerModel::Delete(net::X509Certificate* cert) { + return cert_db_->DeleteCertAndKey(cert); +} + +// static +void CertificateManagerModel::DidGetCertDBOnUIThread( + net::NSSCertDatabase* cert_db, + bool is_user_db_available, + const CreationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + scoped_ptr model(new CertificateManagerModel( + cert_db, is_user_db_available)); + callback.Run(std::move(model)); +} + +// static +void CertificateManagerModel::DidGetCertDBOnIOThread( + const CreationCallback& callback, + net::NSSCertDatabase* cert_db) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + bool is_user_db_available = !!cert_db->GetPublicSlot(); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&CertificateManagerModel::DidGetCertDBOnUIThread, + cert_db, + is_user_db_available, + callback)); +} + +// static +void CertificateManagerModel::GetCertDBOnIOThread( + content::ResourceContext* context, + const CreationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext( + context, + base::Bind(&CertificateManagerModel::DidGetCertDBOnIOThread, + callback)); + if (cert_db) + DidGetCertDBOnIOThread(callback, cert_db); +} diff --git a/chromium_src/chrome/browser/certificate_manager_model.h b/chromium_src/chrome/browser/certificate_manager_model.h new file mode 100644 index 000000000000..1eb350876803 --- /dev/null +++ b/chromium_src/chrome/browser/certificate_manager_model.h @@ -0,0 +1,119 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_ +#define CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_ + +#include +#include + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "net/cert/nss_cert_database.h" + +namespace content { +class BrowserContext; +class ResourceContext; +} // namespace content + +// CertificateManagerModel provides the data to be displayed in the certificate +// manager dialog, and processes changes from the view. +class CertificateManagerModel { + public: + typedef base::Callback)> + CreationCallback; + + // Creates a CertificateManagerModel. The model will be passed to the callback + // when it is ready. The caller must ensure the model does not outlive the + // |browser_context|. + static void Create(content::BrowserContext* browser_context, + const CreationCallback& callback); + + ~CertificateManagerModel(); + + bool is_user_db_available() const { return is_user_db_available_; } + + // Accessor for read-only access to the underlying NSSCertDatabase. + const net::NSSCertDatabase* cert_db() const { return cert_db_; } + + // Import private keys and certificates from PKCS #12 encoded + // |data|, using the given |password|. If |is_extractable| is false, + // mark the private key as unextractable from the module. + // Returns a net error code on failure. + int ImportFromPKCS12(net::CryptoModule* module, + const std::string& data, + const base::string16& password, + bool is_extractable, + net::CertificateList* imported_certs); + + // Import user certificate from DER encoded |data|. + // Returns a net error code on failure. + int ImportUserCert(const std::string& data); + + // Import CA certificates. + // Tries to import all the certificates given. The root will be trusted + // according to |trust_bits|. Any certificates that could not be imported + // will be listed in |not_imported|. + // |trust_bits| should be a bit field of TRUST* values from NSSCertDatabase. + // Returns false if there is an internal error, otherwise true is returned and + // |not_imported| should be checked for any certificates that were not + // imported. + bool ImportCACerts(const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported); + + // Import server certificate. The first cert should be the server cert. Any + // additional certs should be intermediate/CA certs and will be imported but + // not given any trust. + // Any certificates that could not be imported will be listed in + // |not_imported|. + // |trust_bits| can be set to explicitly trust or distrust the certificate, or + // use TRUST_DEFAULT to inherit trust as normal. + // Returns false if there is an internal error, otherwise true is returned and + // |not_imported| should be checked for any certificates that were not + // imported. + bool ImportServerCert( + const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported); + + // Set trust values for certificate. + // |trust_bits| should be a bit field of TRUST* values from NSSCertDatabase. + // Returns true on success or false on failure. + bool SetCertTrust(const net::X509Certificate* cert, + net::CertType type, + net::NSSCertDatabase::TrustBits trust_bits); + + // Delete the cert. Returns true on success. |cert| is still valid when this + // function returns. + bool Delete(net::X509Certificate* cert); + + private: + CertificateManagerModel(net::NSSCertDatabase* nss_cert_database, + bool is_user_db_available); + + // Methods used during initialization, see the comment at the top of the .cc + // file for details. + static void DidGetCertDBOnUIThread( + net::NSSCertDatabase* cert_db, + bool is_user_db_available, + const CreationCallback& callback); + static void DidGetCertDBOnIOThread( + const CreationCallback& callback, + net::NSSCertDatabase* cert_db); + static void GetCertDBOnIOThread(content::ResourceContext* context, + const CreationCallback& callback); + + net::NSSCertDatabase* cert_db_; + // Whether the certificate database has a public slot associated with the + // profile. If not set, importing certificates is not allowed with this model. + bool is_user_db_available_; + + DISALLOW_COPY_AND_ASSIGN(CertificateManagerModel); +}; + +#endif // CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_ diff --git a/chromium_src/chrome/browser/process_singleton_posix.cc b/chromium_src/chrome/browser/process_singleton_posix.cc index 7e54d9b5d370..5c26d7b6157a 100644 --- a/chromium_src/chrome/browser/process_singleton_posix.cc +++ b/chromium_src/chrome/browser/process_singleton_posix.cc @@ -111,7 +111,7 @@ const base::FilePath::CharType kSingletonCookieFilename[] = const base::FilePath::CharType kSingletonLockFilename[] = FILE_PATH_LITERAL("SingletonLock"); const base::FilePath::CharType kSingletonSocketFilename[] = - FILE_PATH_LITERAL("SingletonSocket"); + FILE_PATH_LITERAL("SS"); // Set the close-on-exec bit on a file descriptor. // Returns 0 on success, -1 on failure. @@ -717,6 +717,9 @@ ProcessSingleton::ProcessSingleton( const NotificationCallback& notification_callback) : notification_callback_(notification_callback), current_pid_(base::GetCurrentProcId()) { + // The user_data_dir may have not been created yet. + base::CreateDirectoryAndGetError(user_data_dir, nullptr); + socket_path_ = user_data_dir.Append(kSingletonSocketFilename); lock_path_ = user_data_dir.Append(kSingletonLockFilename); cookie_path_ = user_data_dir.Append(kSingletonCookieFilename); @@ -943,6 +946,19 @@ bool ProcessSingleton::Create() { #endif } +#if defined(MAS_BUILD) + // For Mac App Store build, the tmp dir could be too long to fit + // addr->sun_path, so we need to make it as short as possible. + base::FilePath tmp_dir; + if (!base::GetTempDir(&tmp_dir)) { + LOG(ERROR) << "Failed to get temporary directory."; + return false; + } + if (!socket_dir_.Set(tmp_dir.Append("S"))) { + LOG(ERROR) << "Failed to set socket directory."; + return false; + } +#else // Create the socket file somewhere in /tmp which is usually mounted as a // normal filesystem. Some network filesystems (notably AFS) are screwy and // do not support Unix domain sockets. @@ -950,6 +966,7 @@ bool ProcessSingleton::Create() { LOG(ERROR) << "Failed to create socket directory."; return false; } +#endif // Check that the directory was created with the correct permissions. int dir_mode = 0; diff --git a/chromium_src/chrome/browser/process_singleton_win.cc b/chromium_src/chrome/browser/process_singleton_win.cc index fd4c22e7405c..b488ad457674 100644 --- a/chromium_src/chrome/browser/process_singleton_win.cc +++ b/chromium_src/chrome/browser/process_singleton_win.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/process/process.h" #include "base/process/process_info.h" #include "base/strings/string_number_conversions.h" @@ -190,6 +191,8 @@ ProcessSingleton::ProcessSingleton( user_data_dir_(user_data_dir), should_kill_remote_process_callback_( base::Bind(&TerminateAppWithError)) { + // The user_data_dir may have not been created yet. + base::CreateDirectoryAndGetError(user_data_dir, nullptr); } ProcessSingleton::~ProcessSingleton() { diff --git a/default_app/default_app.js b/default_app/default_app.js index 200e8c585af2..5f5b2c800316 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -15,7 +15,7 @@ exports.load = function (appUrl) { width: 800, height: 600, autoHideMenuBar: true, - backgroundColor: '#A5ECFA', + backgroundColor: '#FFFFFF', useContentSize: true }) mainWindow.loadURL(appUrl) diff --git a/default_app/index.html b/default_app/index.html index 5c8440cfdadb..c10d67a089ac 100644 --- a/default_app/index.html +++ b/default_app/index.html @@ -3,9 +3,11 @@ Electron @@ -89,11 +132,29 @@ }; -

- -

+
+ + + + Electron + + + + + +
+ Docs + Repository + Blog +
+
diff --git a/docs-translations/es/README.md b/docs-translations/es/README.md index e69e76b1c4c7..c2358bbb0161 100644 --- a/docs-translations/es/README.md +++ b/docs-translations/es/README.md @@ -35,7 +35,7 @@ * [content-tracing](../../docs/api/content-tracing.md) * [dialog](../../docs/api/dialog.md) * [global-shortcut](../../docs/api/global-shortcut.md) -* [ipc (proceso principal)](../../docs/api/ipc-main-process.md) +* [ipc (proceso principal)](../../docs/api/ipc-main.md) * [menu](../../docs/api/menu.md) * [menu-item](../../docs/api/menu-item.md) * [power-monitor](../../docs/api/power-monitor.md) @@ -66,6 +66,6 @@ * [Diferencias Técnicas con NW.js (anteriormente conocido como node-webkit)](development/atom-shell-vs-node-webkit.md) * [Repaso del Sistema de Compilación](development/build-system-overview.md) * [Instrucciones de Compilación (Mac)](development/build-instructions-osx.md) -* [Instrucciones de Compilación (Windows)](../../development/build-instructions-windows.md) +* [Instrucciones de Compilación (Windows)](development/build-instructions-windows.md) * [Instrucciones de Compilación (Linux)](development/build-instructions-linux.md) -* [Configurando un Servidor de Símbolos en el depurador](../../development/setting-up-symbol-server.md) +* [Configurando un Servidor de Símbolos en el depurador](development/setting-up-symbol-server.md) diff --git a/docs-translations/es/api/process.md b/docs-translations/es/api/process.md index 9e95ba988541..e50a2c1aac62 100644 --- a/docs-translations/es/api/process.md +++ b/docs-translations/es/api/process.md @@ -5,8 +5,8 @@ al node convencional: * `process.type` String - El tipo del proceso puede ser `browser` (ej. proceso principal) o `renderer`. -* `process.versions['electron']` String - Versión de Electron. -* `process.versions['chrome']` String - Versión de Chromium. +* `process.versions.electron` String - Versión de Electron. +* `process.versions.chrome` String - Versión de Chromium. * `process.resourcesPath` String - Ruta al código fuente JavaScript. ## Events diff --git a/docs-translations/es/api/synopsis.md b/docs-translations/es/api/synopsis.md index 534fafcf2f7c..54c264a1aa74 100644 --- a/docs-translations/es/api/synopsis.md +++ b/docs-translations/es/api/synopsis.md @@ -1,4 +1,4 @@ -# Synopsis +# Sinopsis Todos los [Módulos integrados de Node.js](http://nodejs.org/api/) se encuentran disponibles en Electron y módulos de terceros son támbien totalmente compatibles diff --git a/docs-translations/es/tutorial/application-packaging.md b/docs-translations/es/tutorial/application-packaging.md index f8768c49dd02..0e46a44d2b48 100644 --- a/docs-translations/es/tutorial/application-packaging.md +++ b/docs-translations/es/tutorial/application-packaging.md @@ -105,7 +105,7 @@ originalFs.readFileSync('/path/to/example.asar'); A pesar de que hemos intentado que los paquetes `asar` funcionen como directorios de la mejor forma posible, aún existen limitaciones debido a la naturaleza de bajo nivel de la API Node. -### Los paquetes son de sólo lecutra +### Los paquetes son de sólo lectura Los paquetes `asar` no pueden ser modificados, por lo cual todas las funciones que modifiquen archivos no funcionarán. @@ -132,7 +132,7 @@ Las APIs que requieren el desempaquetamiento adicional son: ### Información falsa en `fs.stat` El objeto `Stats` retornado por `fs.stat` y otras funciones relacionadas, -no es preciso, ya que los archivos del paquete `asar` no existen el sistema de archivos. +no es preciso, ya que los archivos del paquete `asar` no existen en el sistema de archivos. La utilización del objeto `Stats` sólo es recomendable para obtener el tamaño del archivo y/o comprobar el tipo de archivo. diff --git a/docs-translations/es/tutorial/devtools-extension.md b/docs-translations/es/tutorial/devtools-extension.md index f54c3e0eaa83..495f56058d5f 100644 --- a/docs-translations/es/tutorial/devtools-extension.md +++ b/docs-translations/es/tutorial/devtools-extension.md @@ -1,13 +1,13 @@ # Extensión DevTools -Para facilitar la depuración, Electron provee un soporte básico para la extensión -[Chrome DevTools Extension][devtools-extension]. +Para facilitar la depuración, Electron provee un soporte básico para la extensión +[Chrome DevTools][devtools-extension]. Para la mayoría de las extensiones devtools, simplemente puedes descargar el código fuente y utilizar `BrowserWindow.addDevToolsExtension` para cargarlas, las extensiones cargadas serán recordadas para que no sea necesario llamar a la función cada vez que creas una ventana. -Por ejemplo, para usar la extensión [React DevTools Extension](https://github.com/facebook/react-devtools), primero debes descargar el código fuente: +Por ejemplo, para usar la extensión [React DevTools](https://github.com/facebook/react-devtools), primero debes descargar el código fuente: ```bash $ cd /some-directory diff --git a/docs-translations/es/tutorial/quick-start.md b/docs-translations/es/tutorial/quick-start.md index 2e6825669faf..9e65042c5035 100644 --- a/docs-translations/es/tutorial/quick-start.md +++ b/docs-translations/es/tutorial/quick-start.md @@ -1,8 +1,6 @@ -# Intro - ## Introducción -Electron permite la creación de aplicaciones de escritorio utilizando JavaScript puro, a través de un runtime con APIs nativas. Puedes verlo como una variante de io.js, enfocado en aplicaciones de escritorio, en vez de servidores web. +Electron permite la creación de aplicaciones de escritorio utilizando JavaScript puro, a través de un runtime con APIs nativas. Puedes verlo como una variante de io.js, enfocado a aplicaciones de escritorio, en vez de servidores web. Esto no significa que Electron sea un binding de librerías GUI para JavaScript. Electron utiliza páginas web como su GUI, por lo cual puedes verlo como un navegador Chromium mínimo, @@ -117,7 +115,7 @@ Finalmente el `index.html` es la página web que mostraremos:

Hello World!

We are using io.js - and Electron . + and Electron . ``` diff --git a/docs-translations/jp/api/process.md b/docs-translations/jp/api/process.md index ea15fc747a20..6ad3c32d76fe 100644 --- a/docs-translations/jp/api/process.md +++ b/docs-translations/jp/api/process.md @@ -3,8 +3,8 @@ Electronの`process`オブジェクトは上流nodeの1つから次のような違いがあります。 * `process.type` String - プロセスの種類で、`browser` (例 メインプロセス)または `renderer`を設定できます。 -* `process.versions['electron']` String - Electronのバージョン -* `process.versions['chrome']` String - Chromiumのバージョン +* `process.versions.electron` String - Electronのバージョン +* `process.versions.chrome` String - Chromiumのバージョン * `process.resourcesPath` String - JavaScriptのソースコードのパスを設定します。 * `process.mas` Boolean - Mac app Store用のビルドで、値は`true`です。ほかのビルドの場合は`undefined`です。 diff --git a/docs-translations/ko-KR/api/process.md b/docs-translations/ko-KR/api/process.md index 3cdc10dd78cf..a7377a629b20 100644 --- a/docs-translations/ko-KR/api/process.md +++ b/docs-translations/ko-KR/api/process.md @@ -4,8 +4,8 @@ Electron의 `process` 객체는 기존의 node와는 달리 약간의 차이점 * `process.type` String - 프로세스의 타입, `browser` (메인 프로세스) 또는 `renderer`가 됩니다. -* `process.versions['electron']` String - Electron의 버전. -* `process.versions['chrome']` String - Chromium의 버전. +* `process.versions.electron` String - Electron의 버전. +* `process.versions.chrome` String - Chromium의 버전. * `process.resourcesPath` String - JavaScript 소스 코드의 경로. * `process.mas` Boolean - Mac 앱 스토어용 빌드일 때 `true`로 지정됩니다. 다른 빌드일 땐 `undefined`로 지정됩니다. diff --git a/docs-translations/ko-KR/tutorial/quick-start.md b/docs-translations/ko-KR/tutorial/quick-start.md index eb95232a06fc..f7eb700d037c 100644 --- a/docs-translations/ko-KR/tutorial/quick-start.md +++ b/docs-translations/ko-KR/tutorial/quick-start.md @@ -129,7 +129,7 @@ app.on('ready', function() {

헬로 월드!

이 어플리케이션은 node , Chrome , - Electron 을 사용합니다. + Electron 을 사용합니다. ``` diff --git a/docs-translations/pt-BR/api/process.md b/docs-translations/pt-BR/api/process.md index 1c20e2df1187..71502de80d62 100644 --- a/docs-translations/pt-BR/api/process.md +++ b/docs-translations/pt-BR/api/process.md @@ -3,8 +3,8 @@ O objeto `process` no Electron tem as seguintes diferenças do objeto no upstrea * `process.type` String - Tipo de processo, pode ser `browser` (processo principal) ou `renderer`. -* `process.versions['electron']` String - Versão do Electron. -* `process.versions['chrome']` String - Versão do Chromium. +* `process.versions.electron` String - Versão do Electron. +* `process.versions.chrome` String - Versão do Chromium. * `process.resourcesPath` String - Caminho para o código fonte JavaScript. * `process.mas` Boolean - Para build da Mac App Store, este valor é `true`, para outros builds é `undefined`. diff --git a/docs-translations/pt-BR/tutorial/quick-start.md b/docs-translations/pt-BR/tutorial/quick-start.md index 76b128df5d04..6a21123ecf6e 100644 --- a/docs-translations/pt-BR/tutorial/quick-start.md +++ b/docs-translations/pt-BR/tutorial/quick-start.md @@ -130,7 +130,7 @@ Finalmente o `index.html` é a página web que você quer mostrar:

Hello World!

Nós estamos usando io.js - e Electron . + e Electron . ``` diff --git a/docs-translations/tr-TR/README.md b/docs-translations/tr-TR/README.md new file mode 100644 index 000000000000..b055e63f5af1 --- /dev/null +++ b/docs-translations/tr-TR/README.md @@ -0,0 +1,86 @@ +Lütfen kullandığınız dokümanın Electron versiyonunuzla aynı olduğundan emin olun. +Versiyon numarası okuduğunuz dokümanın URL'sindekiyle aynı olmalı. Eğer aynı değilse, muhtemelen geliştirme aşamasındaki API değişikliklerini içerebilecek dokümantasyonudur. +Eğer öyleyse, atom.io üzerinden [mevcut sürümler](http://electron.atom.io/docs/)e göz atabilirsiniz ya da eğer GitHub arayüzünü kullanıyorsanız "Switch branches/tags" açılır menüsünden versiyonunuza uygun olanı seçebilirsiniz. + +## SSS(Sıkça Sorulan Sorular) + +Bir problem(issue) bildirmeden önce sıkça sorulan sorulara göz atın: +* [Electron SSS](https://github.com/electron/electron/tree/master/docs/faq/electron-faq.md) + +## Klavuzlar + +* [Desteklenen Platformlar ](https://github.com/electron/electron/tree/master/docs/tutorial/supported-platforms.md) +* [Uygulama Dağıtımı](https://github.com/electron/electron/tree/master/docs/tutorial/application-distribution.md) +* [Mac Uygulama Mağazası Başvuru Klavuzu](https://github.com/electron/electron/tree/master/docs/tutorial/mac-app-store-submission-guide.md) +* [Uygulama Paketleme](https://github.com/electron/electron/tree/master/docs/tutorial/application-packaging.md) +* [Native Node Modüllerini Kullanma](https://github.com/electron/electron/tree/master/docs/tutorial/using-native-node-modules.md) +* [Ana Süreç(Main Process) Hata ayıklama](https://github.com/electron/electron/tree/master/docs/tutorial/debugging-main-process.md) +* [Selenium ve WebDriver kullanımı](https://github.com/electron/electron/tree/master/docs/tutorial/using-selenium-and-webdriver.md) +* [DevTools Eklentisi](https://github.com/electron/electron/tree/master/docs/tutorial/devtools-extension.md) +* [Pepper Flash Kullanımı](https://github.com/electron/electron/tree/master/docs/tutorial/using-pepper-flash-plugin.md) +* [Widevine CDM Kullanımı](https://github.com/electron/electron/tree/master/docs/tutorial/using-widevine-cdm-plugin.md) +* [CI Sistem Testleri (Travis, Jenkins)](https://github.com/electron/electron/tree/master/docs/tutorial/testing-on-headless-ci.md) + +## Eğitimler + +* [Quick Start](https://github.com/electron/electron/tree/master/docs/tutorial/quick-start.md) +* [Desktop Environment Integration](https://github.com/electron/electron/tree/master/docs/tutorial/desktop-environment-integration.md) +* [Online/Offline Event Detection](https://github.com/electron/electron/tree/master/docs/tutorial/online-offline-events.md) + +## API Kaynakları + +* [Synopsis](https://github.com/electron/electron/tree/master/docs/api/synopsis.md) +* [Process Object](https://github.com/electron/electron/tree/master/docs/api/process.md) +* [Desteklenen Chrome Komut Satırı Anahtarları](https://github.com/electron/electron/tree/master/docs/api/chrome-command-line-switches.md) +* [Environment Değişkenleri](https://github.com/electron/electron/tree/master/docs/api/environment-variables.md) + +### Özel DOM Elementleri: + +* [`File` Nesnesi](api/file-object.md) +* [`` Etiketi](https://github.com/electron/electron/tree/master/docs/api/web-view-tag.md) +* [`window.open` Fonksiyonu](https://github.com/electron/electron/tree/master/docs/api/window-open.md) + +### Ana Süreç(Main Process) Modülleri: + +* [app](https://github.com/electron/electron/tree/master/docs/api/app.md) +* [autoUpdater](https://github.com/electron/electron/tree/master/docs/api/auto-updater.md) +* [BrowserWindow](https://github.com/electron/electron/tree/master/docs/api/browser-window.md) +* [contentTracing](https://github.com/electron/electron/tree/master/docs/api/content-tracing.md) +* [dialog](https://github.com/electron/electron/tree/master/docs/api/dialog.md) +* [globalShortcut](https://github.com/electron/electron/tree/master/docs/api/global-shortcut.md) +* [ipcMain](https://github.com/electron/electron/tree/master/docs/api/ipc-main.md) +* [Menu](https://github.com/electron/electron/tree/master/docs/api/menu.md) +* [MenuItem](https://github.com/electron/electron/tree/master/docs/api/menu-item.md) +* [powerMonitor](https://github.com/electron/electron/tree/master/docs/api/power-monitor.md) +* [powerSaveBlocker](https://github.com/electron/electron/tree/master/docs/api/power-save-blocker.md) +* [protocol](https://github.com/electron/electron/tree/master/docs/api/protocol.md) +* [session](https://github.com/electron/electron/tree/master/docs/api/session.md) +* [webContents](https://github.com/electron/electron/tree/master/docs/api/web-contents.md) +* [Tray](https://github.com/electron/electron/tree/master/docs/api/tray.md) + +### Renderer Process Modülelri (Web Page): + +* [desktopCapturer](https://github.com/electron/electron/tree/master/docs/api/desktop-capturer.md) +* [ipcRenderer](https://github.com/electron/electron/tree/master/docs/api/ipc-renderer.md) +* [remote](https://github.com/electron/electron/tree/master/docs/api/remote.md) +* [webFrame](https://github.com/electron/electron/tree/master/docs/api/web-frame.md) + +### Her İki Süreç İçin Geçerli Modüller: + +* [clipboard](https://github.com/electron/electron/tree/master/docs/api/clipboard.md) +* [crashReporter](https://github.com/electron/electron/tree/master/docs/api/crash-reporter.md) +* [nativeImage](https://github.com/electron/electron/tree/master/docs/api/native-image.md) +* [screen](https://github.com/electron/electron/tree/master/docs/api/screen.md) +* [shell](https://github.com/electron/electron/tree/master/docs/api/shell.md) + +## Geliştirme + +* [Kodlama Stili](https://github.com/electron/electron/tree/master/docs/development/coding-style.md) +* [Kaynak Kod Dizin Yapısı](https://github.com/electron/electron/tree/master/docs/development/source-code-directory-structure.md) +* [NW.js(node-webkit adıyla bilinen) İle Arasındaki Teknik Farklılıklar](https://github.com/electron/electron/tree/master/docs/development/atom-shell-vs-node-webkit.md) +* [Build Sisyem Genel Bakış](https://github.com/electron/electron/tree/master/docs/development/build-system-overview.md) +* [(OS X) Build Komutları](https://github.com/electron/electron/tree/master/docs/development/build-instructions-osx.md) +* [(Windows) Build Komutları](https://github.com/electron/electron/tree/master/docs/development/build-instructions-windows.md) +* [(Linux) Build Komutları](https://github.com/electron/electron/tree/master/docs/development/build-instructions-linux.md) +* [(Windows) Hata Ayıklama Komutları](https://github.com/electron/electron/tree/master/docs/development/debug-instructions-windows.md) +* [Simge Sunucusu(Symbol Server) Hata Ayıklama Kurulumu](https://github.com/electron/electron/tree/master/docs/development/setting-up-symbol-server.md) diff --git a/docs-translations/tr-TR/api/accelerator.md b/docs-translations/tr-TR/api/accelerator.md new file mode 100644 index 000000000000..9105e5c6e460 --- /dev/null +++ b/docs-translations/tr-TR/api/accelerator.md @@ -0,0 +1,49 @@ +# Hızlandırıcı + +> Kısayol Tanımlama. + +Hızlandırıcılar `+` karakteriyle birden fazla niteleyici ile kombinlenebilir. + +Örnek: + +* `CommandOrControl+A` +* `CommandOrControl+Shift+Z` + +## Platform bilgileri + +Linux ve Windows'ta `Command` tuşu herhangi bir etki göstermez. Bunun yerine +`CommandOrControl` niteleyicisini kullanın. Bu işlem OS X'te `Command`, +Linux ve Windows'ta `Control` tuşunun işlevini sağlar. `Alt` ise tüm platformlarda mevcuttur. + +`Super` tuşu Windows ve Linux'te `Windows` tuşuna, OS X'te ise `Cmd` tuşuna eşleştirilmiştir. + +## Mevcut düzenleyiciler + +* `Command` (ya da kısa tanım için `Cmd`) +* `Control` (ya da kısa tanım için `Ctrl`) +* `CommandOrControl` (ya da kısa tanım için `CmdOrCtrl`) +* `Alt` +* `Option` +* `AltGr` +* `Shift` +* `Super` + +## Mevcut tuş kodları + +* `0`dan `9`a +* `A`dan `Z`ye +* `F1`dan `F24`e +* Noktalama işaretleri `~`, `!`, `@`, `#`, `$`, vb. +* `Plus` +* `Space` +* `Backspace` +* `Delete` +* `Insert` +* `Return` (ya da `Enter`) +* `Up`, `Down`, `Left` ve `Right` +* `Home` ve `End` +* `PageUp` ve `PageDown` +* `Escape` (ya da kısa tanım için `Esc`) +* `VolumeUp`, `VolumeDown` ve `VolumeMute` +* `MediaNextTrack`, `MediaPreviousTrack`, `MediaStop` ve `MediaPlayPause` +* `PrintScreen` diff --git a/docs-translations/tr-TR/api/file-object.md b/docs-translations/tr-TR/api/file-object.md new file mode 100644 index 000000000000..a99bdb329c5d --- /dev/null +++ b/docs-translations/tr-TR/api/file-object.md @@ -0,0 +1,29 @@ +# `File` nesnesi + +> Dosya ve dosya sistemlerinde HTML5 `File` nesnesini native olarak çalışır. + +DOM Dosya arayüzü HTML5 dosya API'sini kullanarak kullanıcılara doğrudan native dosyalar üzerinde çalışmasına olanak sağlar. Electron'da `File` arayüzü için `path` özelliğini eklemiştir. + +`dragged-onto-the-app`'dan tam dosya yolu alma örneği: + +```html +
+ Dosyalarınızı buraya sürükleyin +
+ + +``` diff --git a/docs-translations/tr-TR/styleguide.md b/docs-translations/tr-TR/styleguide.md new file mode 100644 index 000000000000..5e8f75240721 --- /dev/null +++ b/docs-translations/tr-TR/styleguide.md @@ -0,0 +1,95 @@ +# Electron Dokümantasyonu Stil Rehberi + +Size uygun bölümü bulun: [Electron Dokümantasyonunu okumak](#reading-electron-documentation) +ya da [Electron Dokümantasyonunu yazmak](#writing-electron-documentation). + +## Electron Dokümantasyonunu Yazmak + +Electron Dokümantasyonunu geliştirmek için aşağıdaki yöntemleri takip edin. + +- Her sayfada en fazla bir tane `h1` etiketi olmalıdır. +- Kod bloklarında `cmd` yerine `bash` kullanın.(syntax highlighter için). +- `h1` Başlığı nesne ismiyle eşleşmeli (ör. `browser-window` → + `BrowserWindow`). + - Hyphen separated filenames, however, are fine. +- No headers following headers, add at least a one-sentence description. +- Methods headers are wrapped in `code` ticks. +- Event headers are wrapped in single 'quotation' marks. +- No nesting lists more than 2 levels (unfortunately because of markdown + renderer). +- Add section titles: Events, Class Methods and Instance Methods. +- Use 'will' over 'would' when describing outcomes. +- Events and methods are `h3` headers. +- Optional arguments written as `function (required[, optional])`. +- Optional arguments are denoted when called out in list. +- Line length is 80-column wrapped. +- Platform specific methods are noted in italics following method header. + - ```### `method(foo, bar)` _OS X_``` +- Prefer 'in the ___ process' over 'on' + +### Dokümantasyon Çevirisi + +Electron Dokümantasyonunun çevirileri `docs-translations` klasörü içerisindedir. + +To add another set (or partial set): + +- Create a subdirectory named by language abbreviation. +- Within that subdirectory, duplicate the `docs` directory, keeping the + names of directories and files same. +- Translate the files. +- Update the `README.md` within your language directory to link to the files + you have translated. +- Add a link to your translation directory on the main Electron [README](https://github.com/electron/electron#documentation-translations). + +## Electron Dokümantasyonunu Okumak + +Electron Dokümantasyon sözdizimini(syntax) anlayabileceğiniz bir kaç ipucu: + +### Metodlar + +[Method](https://developer.mozilla.org/en-US/docs/Glossary/Method) dokümantasyonunun bir örneği: + +--- + +`methodName(required[, optional]))` + +* `require` String (**required**) +* `optional` Integer + +--- + +The method name is followed by the arguments it takes. Optional arguments are +notated by brackets surrounding the optional argument as well as the comma +required if this optional argument follows another argument. + +Below the method is more detailed information on each of the arguments. The type +of argument is notated by either the common types: +[`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), +[`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), +[`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), +[`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +or a custom type like Electron's [`webContent`](https://github.com/electron/electron/tree/master/docs/api/web-content.md). + +### Events + +[event](https://developer.mozilla.org/en-US/docs/Web/API/Event) Dokümantasyonunun bir örneği: + +--- + +Event: 'wake-up' + +Returns: + +* `time` String + +--- + +The event is a string that is used after a `.on` listener method. If it returns +a value it and its type is noted below. If you were to listen and respond to +this event it might look something like this: + +```javascript +Alarm.on('wake-up', function(time) { + console.log(time) +}) +``` diff --git a/docs-translations/zh-CN/api/download-item.md b/docs-translations/zh-CN/api/download-item.md new file mode 100644 index 000000000000..e869e8f30570 --- /dev/null +++ b/docs-translations/zh-CN/api/download-item.md @@ -0,0 +1,96 @@ +# DownloadItem + +`DownloadItem`(下载项)是一个在Electron中展示下载项的 +[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)。 +它被用于`Session`模块中的`will-download`事件,允许用户去控制下载项。 + +```javascript +// In the main process. +win.webContents.session.on('will-download', function(event, item, webContents) { + // Set the save path, making Electron not to prompt a save dialog. + item.setSavePath('/tmp/save.pdf'); + console.log(item.getMimeType()); + console.log(item.getFilename()); + console.log(item.getTotalBytes()); + item.on('updated', function() { + console.log('Received bytes: ' + item.getReceivedBytes()); + }); + item.on('done', function(e, state) { + if (state == "completed") { + console.log("Download successfully"); + } else { + console.log("Download is cancelled or interrupted that can't be resumed"); + } + }); +``` + +## 事件 + +### 事件: 'updated' + +当`downloadItem`获得更新时发射。 + +### 事件: 'done' + +* `event` Event +* `state` String + * `completed` - 下载成功完成。 + * `cancelled` - 下载被取消。 + * `interrupted` - 与文件服务器错误的中断连接。 + +当下载处于一个终止状态时发射。这包括了一个完成的下载,一个被取消的下载(via `downloadItem.cancel()`), +和一个被意外中断的下载(无法恢复)。 + +## 方法 + +`downloadItem`对象有以下一些方法: + +### `downloadItem.setSavePath(path)` + +* `path` String - 设置下载项的保存文件路径. + +这个API仅仅在`session`的`will-download`回调函数中可用。 +如果用户没有这个API去设置保存路径,Electron会用原始程序去确定保存路径(通常提示一个保存对话框)。 + +### `downloadItem.pause()` + +暂停下载。 + +### `downloadItem.resume()` + +恢复被暂停的下载。 + +### `downloadItem.cancel()` + +取消下载操作。 + +### `downloadItem.getURL()` + +以`String`形式返回一个该下载项的下载源url。 + +### `downloadItem.getMimeType()` + +返回一个表示mime类型的`String`。 + +### `downloadItem.hasUserGesture()` + +返回一个`Boolean`表示下载是否有用户动作。 + +### `downloadItem.getFilename()` + +返回一个表示下载项文件名的`String`。 + +**Note:** 此文件名不一定总是保存在本地硬盘上的实际文件名。 +如果用户在下载保存对话框中修改了文件名,保存的文件的实际名称会与`downloadItem.getFilename()`方法返回的文件名不同。 + +### `downloadItem.getTotalBytes()` + +返回一个表示下载项总字节数大小的`Integer`。如果大小未知,返回0。 + +### `downloadItem.getReceivedBytes()` + +返回一个表示下载项已经接收的字节数大小的`Integer`。 + +### `downloadItem.getContentDisposition()` + +以`String`形式返回响应头(response header)中的`Content-Disposition`域。 diff --git a/docs-translations/zh-CN/api/frameless-window.md b/docs-translations/zh-CN/api/frameless-window.md new file mode 100644 index 000000000000..833f070ad27c --- /dev/null +++ b/docs-translations/zh-CN/api/frameless-window.md @@ -0,0 +1,87 @@ +# Frameless Window + +无边框窗口指的是不包含除页面本身以外任何其它可视部分的窗口([chrome](https://developer.mozilla.org/en-US/docs/Glossary/Chrome))。 +像工具栏,是窗口的一部分,但并不属于页面。这些是[`BrowserWindow`](browser-window.md) 类的选项。 + +## 创建无边框窗口 + +为了创建一个无边框窗口,你需要设置[BrowserWindow](browser-window.md)的`frame`为`false`: + + + +```javascript +const BrowserWindow = require('electron').BrowserWindow; +var win = new BrowserWindow({ width: 800, height: 600, frame: false }); +``` + +### OS X上的替代方案 + +在Mac OS X 10.10 Yosemite或者更新的版本中,有一个替代方案去生成一个无边框窗口。 +不同于设置`frame`为`false`会隐藏标题栏以及失去对窗口的控制,你可能想隐藏标题栏使你的页面内容显示在整个窗口上 +,同时又想保持对窗口的控制("traffic lights")。你可以通过指定`titleBarStyle`这一新选项达到目标: + +```javascript +var win = new BrowserWindow({ 'titleBarStyle': 'hidden' }); +``` + +## 透明窗口 + +通过设置`transparent` 选项为 `true`,你能使无边框窗口透明: + +```javascript +var win = new BrowserWindow({ transparent: true, frame: false }); +``` + +### 限制 + +* 你无法点击透明的区域。我们正在采用一个新的API去设置窗口的外形以解决这个问题, + 详见[our issue](https://github.com/electron/electron/issues/1335)。 +* 透明窗口是不可调整大小的。在某些平台上,设置`resizable`为`true`也许会造成这个透明窗口停止工作。 +* `blur`滤光器器只适用于网页,所以没法将模糊效果用于窗口之下(i.e. 其它在用户的系统中打开的应用)。 +* 在Windows系统中,当DWM被禁用时透明窗口不会工作。 +* Linux用户需要在命令行中输入`--enable-transparent-visuals --disable-gpu` + 去禁用GPU以及允许ARGB去渲染透明窗口,这是由于一个Linux上的上游bug[alpha channel doesn't work on some + NVidia drivers](https://code.google.com/p/chromium/issues/detail?id=369209)造成的 +* 在Mac上,透明窗口的阴影不会显示出来. + +## 可拖动区域 + +默认情况下,无边框窗口是不可拖动的。应用在CSS中设置`-webkit-app-region: drag` +告诉Electron哪个区域是可拖动的(比如系统标准的标题栏),应用也可以设置`-webkit-app-region: no-drag` +在可拖动区域中排除不可拖动的区域。需要注意的是,目前只支持矩形区域。 + +为了让整个窗口可拖动,你可以在`body`的样式中添加`-webkit-app-region: drag`: + +```html + + +``` + +另外需要注意的是,如果你设置了整个窗口可拖动,你必须标记按钮为不可拖动的(non-draggable), +否则用户不能点击它们: + +```css +button { + -webkit-app-region: no-drag; +} +``` + +如果你设置一个自定义的标题栏可拖动,你同样需要设置标题栏中所有的按钮为不可拖动(non-draggable)。 + +## 文本选择 + +在一个无边框窗口中,拖动动作会与文本选择发生冲突。比如,当你拖动标题栏,偶尔会选中标题栏上的文本。 +为了防止这种情况发生,你需要向下面这样在一个可拖动区域中禁用文本选择: + +```css +.titlebar { + -webkit-user-select: none; + -webkit-app-region: drag; +} +``` + +## 上下文菜单 + +在一些平台上,可拖动区域会被认为是非客户端框架(non-client frame),所有当你点击右键时,一个系统菜单会弹出。 +为了保证上下文菜单在所有平台下正确的显示,你不应该在可拖动区域使用自定义上下文菜单。 + diff --git a/docs-translations/zh-CN/api/process.md b/docs-translations/zh-CN/api/process.md index 7e8173dfb3b3..89a5e02a6758 100644 --- a/docs-translations/zh-CN/api/process.md +++ b/docs-translations/zh-CN/api/process.md @@ -4,8 +4,8 @@ Electron 中的 `process` 对象 与 upstream node 中的有以下的不同点: * `process.type` String - 进程类型, 可以是 `browser` (i.e. main process) 或 `renderer`. -* `process.versions['electron']` String - Electron的版本. -* `process.versions['chrome']` String - Chromium的版本. +* `process.versions.electron` String - Electron的版本. +* `process.versions.chrome` String - Chromium的版本. * `process.resourcesPath` String - JavaScript源代码路径. * `process.mas` Boolean - 在Mac App Store 创建, 它的值为 `true`, 在其它的地方值为 `undefined`. diff --git a/docs-translations/zh-CN/api/screen.md b/docs-translations/zh-CN/api/screen.md index 0de4c975afbd..3919ebabb779 100644 --- a/docs-translations/zh-CN/api/screen.md +++ b/docs-translations/zh-CN/api/screen.md @@ -23,7 +23,7 @@ app.on('ready', function() { }); ``` -另一个例子,在次页外创建一个窗口: +另一个例子,在此页外创建一个窗口: ```javascript const electron = require('electron'); @@ -54,7 +54,7 @@ app.on('ready', function() { ## `Display` 对象 -`Display` 对象表示了物力方式连接系统. 一个伪造的 `Display` 或许存在于一个无头系统中,或者一个 `Display` 相当于一个远程的、虚拟的 display. +`Display` 对象表示一个连接到系统的物理显示. 一个虚设的 `Display` 或许存在于一个无头系统(headless system)中,或者一个 `Display` 对应一个远程的、虚拟的display. * `display` object * `id` Integer - 与display 相关的唯一性标志. @@ -132,4 +132,4 @@ app.on('ready', function() { * `width` Integer * `height` Integer -返回与提供的边界范围最密切相关的 display. \ No newline at end of file +返回与提供的边界范围最密切相关的 display. diff --git a/docs-translations/zh-CN/tutorial/desktop-environment-integration.md b/docs-translations/zh-CN/tutorial/desktop-environment-integration.md index f069065cd65e..3f946da34e1c 100644 --- a/docs-translations/zh-CN/tutorial/desktop-environment-integration.md +++ b/docs-translations/zh-CN/tutorial/desktop-environment-integration.md @@ -3,6 +3,47 @@ 本章将会说明怎样使用 Electron APIs 把你的应用和桌面环境集成到一块。 +## Notifications (Windows, Linux, OS X) + +这三个操作系统都为用户提供了发送通知的方法。Electron让开发人员通过 +[HTML5 Notification API](https://notifications.spec.whatwg.org/) +便利的去发送通知,用操作系统自带的通知APIs去显示。 + +**Note:** 因为这是一个HTML5API,所以只在渲染进程中起作用 + +```javascript +var myNotification = new Notification('Title', { + body: 'Lorem Ipsum Dolor Sit Amet' +}); + +myNotification.onclick = function () { + console.log('Notification clicked') +} +``` + +尽管代码和用户体验在不同的操作系统中基本相同,但还是有一些差异。 + +### Windows + +* 在Windows 10上, 通知"可以工作". +* 在Windows 8.1和Windows 8系统下,你需要将你的应用通过一个[Application User +Model ID][app-user-model-id]安装到开始屏幕上。需要注意的是,这不是将你的应用固定到开始屏幕。 +* 在Windows 7以及更低的版本中,通知不被支持。不过你可以使用[Tray API][tray-balloon]发送一个"气泡通知"。 + +此外,通知支持的最大字符长队为250。Windows团队建议通知应该保持在200个字符以下。 + +### Linux + +通知使用`libnotify`发送,它能在任何支持[Desktop Notifications +Specification][notification-spec]的桌面环境中显示,包括 Cinnamon, Enlightenment, Unity, +GNOME, KDE。 + +### OS X + +在OS X系统中,通知是直接转发的,你应该了解[Apple's Human Interface guidelines regarding notifications](https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/NotificationCenter.html)。 + +注意通知被限制在256个字节以内,如果超出,则会被截断。 + ## 最近文档 (Windows & OS X) Windows 和 OS X 提供获取最近文档列表的便捷方式,那就是打开跳转列表或者鱼眼菜单。 @@ -165,3 +206,17 @@ window.setDocumentEdited(true); [16]: https://cloud.githubusercontent.com/assets/639601/5082061/670a949a-6f14-11e4-987a-9aaa04b23c1d.png [17]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/browser-window.md [18]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/browser-window.md + +[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows +[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows +[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows +[setprogressbar]: ../api/browser-window.md#winsetprogressbarprogress +[setoverlayicon]: ../api/browser-window.md#winsetoverlayiconoverlay-description-windows-7 +[setrepresentedfilename]: ../api/browser-window.md#winsetrepresentedfilenamefilename-os-x +[setdocumentedited]: ../api/browser-window.md#winsetdocumenteditededited-os-x +[app-registration]: http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx +[unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher +[setthumbarbuttons]: ../api/browser-window.md#winsetthumbarbuttonsbuttons-windows-7 +[tray-balloon]: ../api/tray.md#traydisplayballoonoptions-windows +[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx +[notification-spec]: https://developer.gnome.org/notification-spec/ diff --git a/docs-translations/zh-CN/tutorial/quick-start.md b/docs-translations/zh-CN/tutorial/quick-start.md index 8bdcc05cb027..d267d3146051 100644 --- a/docs-translations/zh-CN/tutorial/quick-start.md +++ b/docs-translations/zh-CN/tutorial/quick-start.md @@ -89,7 +89,7 @@ app.on('ready', function() {

Hello World!

We are using io.js - and Electron . + and Electron . ``` diff --git a/docs-translations/zh-CN/tutorial/testing-on-headless-ci.md b/docs-translations/zh-CN/tutorial/testing-on-headless-ci.md new file mode 100644 index 000000000000..7da98b0a1a64 --- /dev/null +++ b/docs-translations/zh-CN/tutorial/testing-on-headless-ci.md @@ -0,0 +1,51 @@ +# Testing Electron with headless CI Systems (Travis CI, Jenkins) + +Electron基于Chromium,所以需要一个显示驱动使其运转。如果Chromium无法找到一个显示驱动, +ELectron 会启动失败,因此无论你如何去运行它,Electron不会执行你的任何测试。在Travis, Circle, +Jenkins 或者类似的系统上测试基于Electron的应用时,需要进行一些配置。本质上,我们需要使用一个 +虚拟的显示驱动。 + +## Configuring the Virtual Display Server + +首先安装[Xvfb](https://en.wikipedia.org/wiki/Xvfb)。 +这是一个虚拟的帧缓冲,实现了X11显示服务协议,所有的图形操作都在内存中表现,而不需要显示在 +任何屏幕输出设备上。这正是我们所需要的。 + +然后创建一个虚拟的xvfb屏幕并且导出一个指向他的名为`DISPLAY`的环境变量。Electron中的Chromium +会自动的去寻找`$DISPLAY`,所以你的应用不需要再去进行配置。这一步可以通过Paul Betts's的 +[xvfb-maybe](https://github.com/paulcbetts/xvfb-maybe)实现自动化:如果系统需要,在`xvfb-maybe`前加上你的测试命令 +然后这个小工具会自动的设置xvfb。在Windows或者Mac OS X系统下,它不会执行任何东西。 + +``` +## On Windows or OS X, this just invokes electron-mocha +## On Linux, if we are in a headless environment, this will be equivalent +## to xvfb-run electron-mocha ./test/*.js +xvfb-maybe electron-mocha ./test/*.js +``` + +### Travis CI + +在 Travis 上, 你的 `.travis.yml` 应该和下面的代码相似: + +``` +addons: + apt: + packages: + - xvfb + +install: + - export DISPLAY=':99.0' + - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & +``` + +### Jenkins + +Jenkins下, 有一个可用的[Xvfb插件](https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin). + +### Circle CI + +Circle CI 是非常棒的而且有xvfb,`$DISPLAY`也[已经搭建,所以不需要再进行设置](https://circleci.com/docs/environment#browsers)。 + +### AppVeyor + +AppVeyor运行于Windows上,支持 Selenium, Chromium, Electron 以及一些类似的工具,开箱即用,无需配置。 diff --git a/docs-translations/zh-TW/api/process.md b/docs-translations/zh-TW/api/process.md index a4f45352b9c4..ba3dd245f7a9 100644 --- a/docs-translations/zh-TW/api/process.md +++ b/docs-translations/zh-TW/api/process.md @@ -3,8 +3,8 @@ 在 Electron 裡的 `process` 物件具有以下幾個與 upstream node 的不同點: * `process.type` String - Process 的型態,可以是 `browser` (i.e. 主行程) 或 `renderer`. -* `process.versions['electron']` String - Electron 的版本 -* `process.versions['chrome']` String - Chromium 的版本 +* `process.versions.electron` String - Electron 的版本 +* `process.versions.chrome` String - Chromium 的版本 * `process.resourcesPath` String - JavaScript 源碼的路徑 # 方法 (Methods) diff --git a/docs/README.md b/docs/README.md index 19ec51343d4e..c8bd425d9759 100644 --- a/docs/README.md +++ b/docs/README.md @@ -62,6 +62,7 @@ an issue: * [powerSaveBlocker](api/power-save-blocker.md) * [protocol](api/protocol.md) * [session](api/session.md) +* [systemPreferences](api/system-preferences.md) * [webContents](api/web-contents.md) * [Tray](api/tray.md) @@ -89,4 +90,5 @@ an issue: * [Build Instructions (OS X)](development/build-instructions-osx.md) * [Build Instructions (Windows)](development/build-instructions-windows.md) * [Build Instructions (Linux)](development/build-instructions-linux.md) +* [Debug Instructions (Windows)](development/debug-instructions-windows.md) * [Setting Up Symbol Server in debugger](development/setting-up-symbol-server.md) diff --git a/docs/api/accelerator.md b/docs/api/accelerator.md index 83d8d3a7c2de..cf28e01037a2 100644 --- a/docs/api/accelerator.md +++ b/docs/api/accelerator.md @@ -1,7 +1,9 @@ # Accelerator -An accelerator is a string that represents a keyboard shortcut. It can contain -multiple modifiers and key codes, combined by the `+` character. +> Define keyboard shortcuts. + +Accelerators can contain multiple modifiers and key codes, combined by +the `+` character. Examples: diff --git a/docs/api/app.md b/docs/api/app.md index 84eeed51d6cc..1f682933d233 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -1,6 +1,6 @@ # app -The `app` module is responsible for controlling the application's lifecycle. +> Control your application's event lifecycle. The following example shows how to quit the application when the last window is closed: @@ -228,10 +228,6 @@ app.on('login', function(event, webContents, request, authInfo, callback) { Emitted when the gpu process crashes. -### Event: 'platform-theme-changed' _OS X_ - -Emitted when the system's Dark Mode theme is toggled. - ## Methods The `app` object has the following methods: @@ -388,6 +384,18 @@ This method checks if the current executable as the default handler for a protoc **Note:** On OS X, removing the app will automatically remove the app as the default protocol handler. +### `app.isDefaultProtocolClient(protocol)` _OS X_ _Windows_ + +* `protocol` String - The name of your protocol, without `://`. + +This method checks if the current executable is the default handler for a protocol +(aka URI scheme). If so, it will return true. Otherwise, it will return false. + +**Note:** On OS X, you can use this method to check if the app has been registered as the default protocol handler for a protocol. You can also verify this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the OS X machine. +Please refer to [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. + +The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. + ### `app.setUserTasks(tasks)` _Windows_ * `tasks` Array - Array of `Task` objects @@ -453,7 +461,7 @@ use this method to ensure single instance. An example of activating the window of primary instance when a second instance starts: -```js +```javascript var myWindow = null; var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { @@ -480,39 +488,17 @@ app.on('ready', function() { Changes the [Application User Model ID][app-user-model-id] to `id`. -### `app.isAeroGlassEnabled()` _Windows_ +### `app.importCertificate(options, callback)` _LINUX_ -This method returns `true` if [DWM composition](https://msdn.microsoft.com/en-us/library/windows/desktop/aa969540.aspx) -(Aero Glass) is enabled, and `false` otherwise. You can use it to determine if -you should create a transparent window or not (transparent windows won't work -correctly when DWM composition is disabled). +* `options` Object + * `certificate` String - Path for the pkcs12 file. + * `password` String - Passphrase for the certificate. +* `callback` Function + * `result` Integer - Result of import. -Usage example: - -```js -let browserOptions = {width: 1000, height: 800}; - -// Make the window transparent only if the platform supports it. -if (process.platform !== 'win32' || app.isAeroGlassEnabled()) { - browserOptions.transparent = true; - browserOptions.frame = false; -} - -// Create the window. -win = new BrowserWindow(browserOptions); - -// Navigate. -if (browserOptions.transparent) { - win.loadURL('file://' + __dirname + '/index.html'); -} else { - // No transparency, so we load a fallback that uses basic styles. - win.loadURL('file://' + __dirname + '/fallback.html'); -} -``` - -### `app.isDarkMode()` _OS X_ - -This method returns `true` if the system is in Dark Mode, and `false` otherwise. +Imports the certificate in pkcs12 format into the platform certificate store. +`callback` is called with the `result` of import operation, a value of `0` +indicates success while any other value indicates failure according to chromium [net_error_list](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h). ### `app.commandLine.appendSwitch(switch[, value])` @@ -582,3 +568,5 @@ Sets the `image` associated with this dock icon. [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx [CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115 +[LSCopyDefaultHandlerForURLScheme]: +https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index 8dd6d2412a4a..211d0c69a5f3 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -1,12 +1,17 @@ # autoUpdater -This module provides an interface for the `Squirrel` auto-updater framework. +> Enable apps to automatically update themselves. + +The `autoUpdater` module provides an interface for the [Squirrel](https://github.com/Squirrel) framework. You can quickly launch a multi-platform release server for distributing your application by using one of these projects: -- [electron-release-server][electron-release-server]: *A fully featured, self-hosted release server for electron applications, compatible with auto-updater* -- [squirrel-updates-server][squirrel-updates-server]: *A simple node.js server for Squirrel.Mac and Squirrel.Windows which uses GitHub releases* +- [electron-release-server][electron-release-server]: *A fully featured, + self-hosted release server for electron applications, compatible with + auto-updater* +- [squirrel-updates-server][squirrel-updates-server]: *A simple node.js server + for Squirrel.Mac and Squirrel.Windows which uses GitHub releases* ## Platform notices diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 877a6d0f7457..7289057b40eb 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1,7 +1,6 @@ # BrowserWindow -The `BrowserWindow` class gives you the ability to create a browser window. For -example: +> Create and control browser windows. ```javascript // In the main process. @@ -311,10 +310,11 @@ Emitted when an [App Command](https://msdn.microsoft.com/en-us/library/windows/d is invoked. These are typically related to keyboard media keys or browser commands, as well as the "Back" button built into some mice on Windows. -Commands are lowercased with underscores replaced with hyphens and the `APPCOMMAND_` prefix stripped off. +Commands are lowercased with underscores replaced with hyphens and the +`APPCOMMAND_` prefix stripped off. e.g. `APPCOMMAND_BROWSER_BACKWARD` is emitted as `browser-backward`. -```js +```javascript someWindow.on('app-command', function(e, cmd) { // Navigate the window back when the user hits their mouse back button if (cmd === 'browser-backward' && someWindow.webContents.canGoBack()) { @@ -675,6 +675,17 @@ Returns the title of the native window. **Note:** The title of web page can be different from the title of the native window. +### `win.setSheetOffset(offset)` _OS X_ + +Changes the attachment point for sheets on Mac OS X. By default, sheets are +attached just below the window frame, but you may want to display them beneath +a HTML-rendered toolbar. For example: + +```javascript +var toolbarRect = document.getElementById('toolbar').getBoundingClientRect(); +win.setSheetOffset(toolbarRect.height); +``` + ### `win.flashFrame(flag)` * `flag` Boolean @@ -812,8 +823,8 @@ cleared * `description` String - a description that will be provided to Accessibility screen readers -Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to convey some -sort of application status or to passively notify the user. +Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to +convey some sort of application status or to passively notify the user. ### `win.setHasShadow(hasShadow)` _OS X_ @@ -912,4 +923,4 @@ Returns whether the window is visible on all workspaces. Ignore all moused events that happened in the window. -[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=527 +[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=576 diff --git a/docs/api/chrome-command-line-switches.md b/docs/api/chrome-command-line-switches.md index 65f096eac6e0..17059435eaa7 100644 --- a/docs/api/chrome-command-line-switches.md +++ b/docs/api/chrome-command-line-switches.md @@ -1,9 +1,10 @@ # Supported Chrome command line switches -This page lists the command line switches used by the Chrome browser that are -also supported by Electron. You can use -[app.commandLine.appendSwitch][append-switch] to append them in your app's main -script before the [ready][ready] event of [app][app] module is emitted: +> Command line switches supported by Electron. + +You can use [app.commandLine.appendSwitch][append-switch] to append them in +your app's main script before the [ready][ready] event of the [app][app] module +is emitted: ```javascript const app = require('electron').app; @@ -15,10 +16,6 @@ app.on('ready', function() { }); ``` -## --client-certificate=`path` - -Sets the `path` of client certificate file. - ## --ignore-connections-limit=`domains` Ignore the connections limit for `domains` list separated by `,`. diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index 7f95a1af26d2..58e5a1c1b096 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -1,6 +1,7 @@ # clipboard -The `clipboard` module provides methods to perform copy and paste operations. +> Perform copy and paste operations on the system clipboard. + The following example shows how to write a string to the clipboard: ```javascript diff --git a/docs/api/content-tracing.md b/docs/api/content-tracing.md index 0b83c2759c67..e347790b63f4 100644 --- a/docs/api/content-tracing.md +++ b/docs/api/content-tracing.md @@ -1,9 +1,11 @@ # contentTracing -The `content-tracing` module is used to collect tracing data generated by the -underlying Chromium content module. This module does not include a web interface -so you need to open `chrome://tracing/` in a Chrome browser and load the -generated file to view the result. +> Collect tracing data from Chromium's content module for finding performance +bottlenecks and slow operations. + +This module does not include a web interface so you need to open +`chrome://tracing/` in a Chrome browser and load the generated file to view the +result. ```javascript const contentTracing = require('electron').contentTracing; @@ -26,7 +28,7 @@ contentTracing.startRecording(options, function() { ## Methods -The `content-tracing` module has the following methods: +The `contentTracing` module has the following methods: ### `contentTracing.getCategories(callback)` diff --git a/docs/api/crash-reporter.md b/docs/api/crash-reporter.md index 64e5602474a9..1f8229eaa792 100644 --- a/docs/api/crash-reporter.md +++ b/docs/api/crash-reporter.md @@ -1,6 +1,6 @@ # crashReporter -The `crash-reporter` module enables sending your app's crash reports. +> Submit crash reports to a remote server. The following is an example of automatically submitting a crash report to a remote server: @@ -60,7 +60,8 @@ ID. ## crash-reporter Payload -The crash reporter will send the following data to the `submitURL` as a `multipart/form-data` `POST`: +The crash reporter will send the following data to the `submitURL` as +a `multipart/form-data` `POST`: * `ver` String - The version of Electron. * `platform` String - e.g. 'win32'. diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md index 7863622e372a..dc523c3a42f9 100644 --- a/docs/api/desktop-capturer.md +++ b/docs/api/desktop-capturer.md @@ -1,7 +1,7 @@ # desktopCapturer -The `desktopCapturer` module can be used to get available sources that can be -used to be captured with `getUserMedia`. +> List `getUserMedia` sources for capturing audio, video, and images from a +microphone, camera, or screen. ```javascript // In the renderer process. @@ -40,7 +40,7 @@ function getUserMediaError(e) { When creating a constraints object for the `navigator.webkitGetUserMedia` call, if you are using a source from `desktopCapturer` your `chromeMediaSource` must -be set to `"desktop"` and your `audio` must be set to `false`. +be set to `"desktop"` and your `audio` must be set to `false`. If you wish to capture the audio and video from the entire desktop you can set @@ -65,13 +65,14 @@ Starts a request to get all desktop sources, `callback` will be called with The `sources` is an array of `Source` objects, each `Source` represents a captured screen or individual window, and has following properties: + * `id` String - The id of the captured window or screen used in `navigator.webkitGetUserMedia`. The format looks like `window:XX` or `screen:XX` where `XX` is a random generated number. * `name` String - The described name of the capturing screen or window. If the source is a screen, the name will be `Entire Screen` or `Screen `; if it is a window, the name will be the window's title. -* `thumbnail` [NativeImage](NativeImage.md) - A thumbnail image. +* `thumbnail` [NativeImage](native-image.md) - A thumbnail native image. **Note:** There is no guarantee that the size of `source.thumbnail` is always the same as the `thumnbailSize` in `options`. It also depends on the scale of diff --git a/docs/api/dialog.md b/docs/api/dialog.md index 9fbc20dd8305..a5f61a53e3f8 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -1,8 +1,6 @@ # dialog -The `dialog` module provides APIs to show native system dialogs, such as opening -files or alerting, so web applications can deliver the same user experience as -native applications. +> Display native system dialogs for opening and saving files, alerting, etc. An example of showing a dialog to select multiple files and directories: @@ -12,16 +10,13 @@ const dialog = require('electron').dialog; console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', 'multiSelections' ]})); ``` -The Dialog is opened from Electron's main thread. If you want to use the dialog object from a renderer process, remember to access it using the remote: +The Dialog is opened from Electron's main thread. If you want to use the dialog +object from a renderer process, remember to access it using the remote: ```javascript const dialog = require('electron').remote.dialog; ``` -**Note for OS X**: If you want to present dialogs as sheets, the only thing you -have to do is provide a `BrowserWindow` reference in the `browserWindow` -parameter. - ## Methods The `dialog` module has the following methods: @@ -125,3 +120,12 @@ This API can be called safely before the `ready` event the `app` module emits, it is usually used to report errors in early stage of startup. If called before the app `ready`event on Linux, the message will be emitted to stderr, and no GUI dialog will appear. + +## Sheets + +On Mac OS X, dialogs are presented as sheets attached to a window if you provide +a `BrowserWindow` reference in the `browserWindow` parameter, or modals if no +window is provided. + +You can call `BrowserWindow.getCurrentWindow().setSheetOffset(offset)` to change +the offset from the window frame where sheets are attached. diff --git a/docs/api/download-item.md b/docs/api/download-item.md index 756353b8ba3d..4418f61c19fb 100644 --- a/docs/api/download-item.md +++ b/docs/api/download-item.md @@ -1,7 +1,9 @@ # DownloadItem -`DownloadItem` is an EventEmitter represents a download item in Electron. It -is used in `will-download` event of `Session` module, and allows users to +> Control file downloads from remote sources. + +`DownloadItem` is an EventEmitter that represents a download item in Electron. +It is used in `will-download` event of `Session` module, and allows users to control the download item. ```javascript @@ -22,6 +24,7 @@ win.webContents.session.on('will-download', function(event, item, webContents) { console.log("Download is cancelled or interrupted that can't be resumed"); } }); +}); ``` ## Events diff --git a/docs/api/environment-variables.md b/docs/api/environment-variables.md index 3483c19d6431..7339661b09fe 100644 --- a/docs/api/environment-variables.md +++ b/docs/api/environment-variables.md @@ -1,5 +1,7 @@ # Environment variables +> Control application configuration and behavior without changing code. + Some behaviors of Electron are controlled by environment variables, because they are initialized earlier than command line and the app's code. diff --git a/docs/api/file-object.md b/docs/api/file-object.md index 01d49c39155f..31c6feddb50b 100644 --- a/docs/api/file-object.md +++ b/docs/api/file-object.md @@ -1,5 +1,7 @@ # `File` object +> Use the HTML5 `File` API to work natively with files on the filesystem. + The DOM's File interface provides abstraction around native files in order to let users work on native files directly with the HTML5 file API. Electron has added a `path` attribute to the `File` interface which exposes the file's real diff --git a/docs/api/frameless-window.md b/docs/api/frameless-window.md index 3c26a36b0329..12497db4dae9 100644 --- a/docs/api/frameless-window.md +++ b/docs/api/frameless-window.md @@ -1,6 +1,11 @@ # Frameless Window -A frameless window is a window that has no [chrome](https://developer.mozilla.org/en-US/docs/Glossary/Chrome), the parts of the window, like toolbars, that are not a part of the web page. These are options on the [`BrowserWindow`](browser-window.md) class. +> Open a window without toolbars, borders, or other graphical "chrome". + +A frameless window is a window that has no +[chrome](https://developer.mozilla.org/en-US/docs/Glossary/Chrome), the parts of +the window, like toolbars, that are not a part of the web page. These are +options on the [`BrowserWindow`](browser-window.md) class. ## Create a frameless window @@ -38,7 +43,8 @@ var win = new BrowserWindow({ transparent: true, frame: false }); ### Limitations * You can not click through the transparent area. We are going to introduce an - API to set window shape to solve this, see [our issue](https://github.com/electron/electron/issues/1335) for details. + API to set window shape to solve this, see + [our issue](https://github.com/electron/electron/issues/1335) for details. * Transparent windows are not resizable. Setting `resizable` to `true` may make a transparent window stop working on some platforms. * The `blur` filter only applies to the web page, so there is no way to apply diff --git a/docs/api/global-shortcut.md b/docs/api/global-shortcut.md index f20758406b7c..08453d68a46c 100644 --- a/docs/api/global-shortcut.md +++ b/docs/api/global-shortcut.md @@ -1,5 +1,7 @@ # globalShortcut +> Detect keyboard events when the application does not have keyboard focus. + The `globalShortcut` module can register/unregister a global keyboard shortcut with the operating system so that you can customize the operations for various shortcuts. diff --git a/docs/api/ipc-main.md b/docs/api/ipc-main.md index 84fbcfd5a72f..c8ba60ba3170 100644 --- a/docs/api/ipc-main.md +++ b/docs/api/ipc-main.md @@ -1,5 +1,7 @@ # ipcMain +> Communicate asynchronously from the main process to renderer processes. + The `ipcMain` module is an instance of the [EventEmitter](https://nodejs.org/api/events.html) class. When used in the main process, it handles asynchronous and synchronous messages sent from a renderer diff --git a/docs/api/ipc-renderer.md b/docs/api/ipc-renderer.md index 46a2331af43b..9835e42b4679 100644 --- a/docs/api/ipc-renderer.md +++ b/docs/api/ipc-renderer.md @@ -1,5 +1,7 @@ # ipcRenderer +> Communicate asynchronously from a renderer process to the main process. + The `ipcRenderer` module is an instance of the [EventEmitter](https://nodejs.org/api/events.html) class. It provides a few methods so you can send synchronous and asynchronous messages from the render diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index fb12e251153e..b67032271e61 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -1,7 +1,6 @@ # MenuItem -The `menu-item` module allows you to add items to an application or context -[`menu`](menu.md). +> Add items to native application menus and context menus. See [`menu`](menu.md) for examples. @@ -22,9 +21,10 @@ Create a new `MenuItem` with the following method: * `sublabel` String * `accelerator` [Accelerator](accelerator.md) * `icon` [NativeImage](native-image.md) - * `enabled` Boolean - If false, the menu item will be greyed out and unclickable. + * `enabled` Boolean - If false, the menu item will be greyed out and + unclickable. * `visible` Boolean - If false, the menu item will be entirely hidden. - * `checked` Boolean - Should only be specified for `checkbox` or `radio` type + * `checked` Boolean - Should only be specified for `checkbox` or `radio` type menu items. * `submenu` Menu - Should be specified for `submenu` type menu items. If `submenu` is specified, the `type: 'submenu'` can be omitted. If the value @@ -61,6 +61,9 @@ On OS X `role` can also have following additional values: * `help` - The submenu is a "Help" menu * `services` - The submenu is a "Services" menu +When specifying `role` on OS X, `label` and `accelerator` are the only options +that will affect the MenuItem. All other options will be ignored. + ## Instance Properties The following properties (and no others) can be updated on an existing `MenuItem`: @@ -77,4 +80,3 @@ selected. You can add a `click` function to do additional work. A `radio` menu item will turn on its `checked` property when clicked, and will turn off that property for all adjacent items in the same menu. Again, you can add a `click` function for additional behavior. - diff --git a/docs/api/menu.md b/docs/api/menu.md index cb5a0cbe2df8..4106dfdd4089 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -1,8 +1,7 @@ # Menu -The `menu` class is used to create native menus that can be used as -application menus and -[context menus](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/PopupGuide/ContextMenus). +> Create native application menus and context menus. + This module is a main process module which can be used in a render process via the `remote` module. diff --git a/docs/api/native-image.md b/docs/api/native-image.md index e0c3ae466248..1cbfcf410181 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -1,5 +1,7 @@ # nativeImage +> Create tray, dock, and application icons using PNG or JPG files. + In Electron, for the APIs that take images, you can pass either file paths or `nativeImage` instances. An empty image will be used when `null` is passed. diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index 4465b253a754..75546dc80159 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -1,7 +1,8 @@ # powerMonitor -The `power-monitor` module is used to monitor power state changes. You can -only use it in the main process. You should not use this module until the `ready` +> Monitor power state changes. + +You can only use it in the main process. You should not use this module until the `ready` event of the `app` module is emitted. For example: diff --git a/docs/api/power-save-blocker.md b/docs/api/power-save-blocker.md index a3ef798411f2..3ec07338646b 100644 --- a/docs/api/power-save-blocker.md +++ b/docs/api/power-save-blocker.md @@ -1,8 +1,6 @@ # powerSaveBlocker -The `powerSaveBlocker` module is used to block the system from entering -low-power (sleep) mode and thus allowing the app to keep the system and screen -active. +> Block the system from entering low-power (sleep) mode. For example: diff --git a/docs/api/process.md b/docs/api/process.md index 620ad6dc931a..1becdd46df09 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -1,15 +1,19 @@ # process +> Get information about the running application process. + The `process` object in Electron has the following differences from the one in upstream node: * `process.type` String - Process's type, can be `browser` (i.e. main process) or `renderer`. -* `process.versions['electron']` String - Version of Electron. -* `process.versions['chrome']` String - Version of Chromium. +* `process.versions.electron` String - Version of Electron. +* `process.versions.chrome` String - Version of Chromium. * `process.resourcesPath` String - Path to JavaScript source code. * `process.mas` Boolean - For Mac App Store build, this value is `true`, for other builds it is `undefined`. +* `process.windowsStore` Boolean - If the app is running as a Windows Store app + (appx), this value is `true`, for other builds it is `undefined`. ## Events @@ -21,7 +25,7 @@ beginning to load the web page or the main script. It can be used by the preload script to add removed Node global symbols back to the global scope when node integration is turned off: -```js +```javascript // preload.js var _setImmediate = setImmediate; var _clearImmediate = clearImmediate; @@ -42,6 +46,10 @@ built-in modules. The `process` object has the following method: +### `process.crash()` + +Causes the main thread of the current process crash. + ### `process.hang()` Causes the main thread of the current process hang. diff --git a/docs/api/protocol.md b/docs/api/protocol.md index e57a34ef89b5..aa9b43486d02 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -1,7 +1,6 @@ # protocol -The `protocol` module can register a custom protocol or intercept an existing -protocol. +> Register a custom protocol and intercept existing protocol requests. An example of implementing a protocol that has the same effect as the `file://` protocol: diff --git a/docs/api/remote.md b/docs/api/remote.md index 7bdbe0362f60..454758c9139f 100644 --- a/docs/api/remote.md +++ b/docs/api/remote.md @@ -1,5 +1,7 @@ # remote +> Use main process modules from the renderer process. + The `remote` module provides a simple way to do inter-process communication (IPC) between the renderer process (web page) and the main process. diff --git a/docs/api/screen.md b/docs/api/screen.md index 61210c9e119b..00061e969fbc 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -1,8 +1,9 @@ # screen -The `screen` module retrieves information about screen size, displays, cursor -position, etc. You should not use this module until the `ready` event of the -`app` module is emitted. +> Retrieve information about screen size, displays, cursor position, etc. + +You cannot not use this module until the `ready` event of the `app` module is +emitted (by invoking or requiring it). `screen` is an [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). @@ -62,8 +63,8 @@ a remote, virtual display. * `display` object * `id` Integer - Unique identifier associated with the display. - * `rotation` Integer - Can be 0, 1, 2, 3, each represents screen rotation in - clock-wise degrees of 0, 90, 180, 270. + * `rotation` Integer - Can be 0, 90, 180, 270, represents screen rotation in + clock-wise degrees. * `scaleFactor` Number - Output device's pixel scale factor. * `touchSupport` String - Can be `available`, `unavailable`, `unknown`. * `bounds` Object diff --git a/docs/api/session.md b/docs/api/session.md index 9cccefcbdb66..5bc819fb2bde 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -1,5 +1,7 @@ # session +> Manage browser sessions, cookies, cache, proxy settings, etc. + The `session` module can be used to create new `Session` objects. You can also access the `session` of existing pages by using the `session` @@ -295,8 +297,9 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c * `handler` Function * `webContents` Object - [WebContents](web-contents.md) requesting the permission. - * `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex', 'pointerLock', 'fullscreen'. - * `callback` Function - Allow or deny the permission. + * `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex', + 'pointerLock', 'fullscreen', 'openExternal'. + * `callback` Function - Allow or deny the permission. Sets the handler which can be used to respond to permission requests for the `session`. Calling `callback(true)` will allow the permission and `callback(false)` will reject it. @@ -421,7 +424,7 @@ response are visible by the time this listener is fired. * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter,] listener)` +#### `ses.webRequest.onHeadersReceived([filter,]listener)` * `filter` Object * `listener` Function @@ -446,8 +449,9 @@ The `callback` has to be called with an `response` object: * `cancel` Boolean * `responseHeaders` Object (optional) - When provided, the server is assumed to have responded with these headers. - * `statusLine` String (optional) - Should be provided when overriding `responseHeaders` - to change header status otherwise original response header's status will be used. + * `statusLine` String (optional) - Should be provided when overriding + `responseHeaders` to change header status otherwise original response + header's status will be used. #### `ses.webRequest.onResponseStarted([filter, ]listener)` @@ -465,7 +469,7 @@ and response headers are available. * `resourceType` String * `timestamp` Double * `responseHeaders` Object - * `fromCache` Boolean - Indicates whether the response was fetched from disk + * `fromCache` Boolean - Indicates whether the response was fetched from disk cache. * `statusCode` Integer * `statusLine` String diff --git a/docs/api/shell.md b/docs/api/shell.md index 823dc481bb07..8ede115f7c63 100644 --- a/docs/api/shell.md +++ b/docs/api/shell.md @@ -1,5 +1,7 @@ # shell +> Manage files and URLs using their default applications. + The `shell` module provides functions related to desktop integration. An example of opening a URL in the user's default browser: diff --git a/docs/api/synopsis.md b/docs/api/synopsis.md index 015674c2ef6f..a2aa58f57f17 100644 --- a/docs/api/synopsis.md +++ b/docs/api/synopsis.md @@ -1,5 +1,7 @@ # Synopsis +> How to use Node.js and Electron APIs. + All of [Node.js's built-in modules](http://nodejs.org/api/) are available in Electron and third-party node modules also fully supported as well (including the [native modules](../tutorial/using-native-node-modules.md)). @@ -11,8 +13,8 @@ both processes. The basic rule is: if a module is [GUI][gui] or low-level system related, then it should be only available in the main process. You need to be familiar with -the concept of [main process vs. renderer process](../tutorial/quick-start.md#the-main-process) scripts to be -able to use those modules. +the concept of [main process vs. renderer process](../tutorial/quick-start.md#the-main-process) +scripts to be able to use those modules. The main process script is just like a normal Node.js script: diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md new file mode 100644 index 000000000000..8fdd2b54cb0b --- /dev/null +++ b/docs/api/system-preferences.md @@ -0,0 +1,79 @@ +# systemPreferences + +> Get system preferences. + +## Methods + +### `systemPreferences.isDarkMode()` _OS X_ + +This method returns `true` if the system is in Dark Mode, and `false` otherwise. + +### `systemPreferences.subscribeNotification(event, callback)` _OS X_ + +* `event` String +* `callback` Function + +Subscribes to native notifications of OS X, `callback` will be called when the +corresponding `event` happens. The `id` of the subscriber is returned, which can +be used to unsubscribe the `event`. + +Under the hood this API subscribes to `NSDistributedNotificationCenter`, +possible values of `event` are: + +* `AppleInterfaceThemeChangedNotification` +* `AppleAquaColorVariantChanged` +* `AppleColorPreferencesChangedNotification` +* `AppleShowScrollBarsSettingChanged` + +### `systemPreferences.unsubscribeNotification(id)` _OS X_ + +* `id` Integer + +Removes the subscriber with `id`. + +### `systemPreferences.getUserDefault(key, type)` _OS X_ + +* `key` String +* `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`, + `url`. + +Get the value of `key` in system preferences. + +This API reads from `NSUserDefaults` on OS X, some popular `key` and `type`s +are: + +* `AppleInterfaceStyle: string` +* `AppleAquaColorVariant: integer` +* `AppleHighlightColor: string` +* `AppleShowScrollBars: string` + +### `systemPreferences.isAeroGlassEnabled()` _Windows_ + +This method returns `true` if [DWM composition][dwm-composition] (Aero Glass) is +enabled, and `false` otherwise. + +An example of using it to determine if you should create a transparent window or +not (transparent windows won't work correctly when DWM composition is disabled): + +```javascript +let browserOptions = {width: 1000, height: 800}; + +// Make the window transparent only if the platform supports it. +if (process.platform !== 'win32' || app.isAeroGlassEnabled()) { + browserOptions.transparent = true; + browserOptions.frame = false; +} + +// Create the window. +let win = new BrowserWindow(browserOptions); + +// Navigate. +if (browserOptions.transparent) { + win.loadURL('file://' + __dirname + '/index.html'); +} else { + // No transparency, so we load a fallback that uses basic styles. + win.loadURL('file://' + __dirname + '/fallback.html'); +} +``` + +[dwm-composition]:https://msdn.microsoft.com/en-us/library/windows/desktop/aa969540.aspx diff --git a/docs/api/tray.md b/docs/api/tray.md index aa972c57cd92..1ea0859f14a4 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -1,7 +1,6 @@ # Tray -A `Tray` represents an icon in an operating system's notification area, it is -usually attached with a context menu. +> Add icons and context menus to the system's notification area. ```javascript const electron = require('electron'); diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 8fc7a959f69d..64e7cea60e79 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -1,8 +1,9 @@ # webContents +> Render and control web pages. + `webContents` is an [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). - It is responsible for rendering and controlling a web page and is a property of the [`BrowserWindow`](browser-window.md) object. An example of accessing the `webContents` object: @@ -278,7 +279,8 @@ Emitted when media is paused or done playing. ### Event: 'did-change-theme-color' -Emitted when a page's theme color changes. This is usually due to encountering a meta tag: +Emitted when a page's theme color changes. This is usually due to encountering +a meta tag: ```html @@ -353,6 +355,11 @@ Returns the title of the current web page. Returns whether web page is still loading resources. +### `webContents.isLoadingMainFrame()` + +Returns whether the main frame (and not just iframes or frames within it) is +still loading. + ### `webContents.isWaitingForResponse()` Returns whether the web page is waiting for a first-response from the main @@ -521,9 +528,10 @@ Inserts `text` to the focused element. uppercase letter followed by a lowercase or non-letter. Accepts several other intra-word matches, defaults to `false`. -Starts a request to find all matches for the `text` in the web page and returns an `Integer` -representing the request id used for the request. The result of the request can be -obtained by subscribing to [`found-in-page`](web-contents.md#event-found-in-page) event. +Starts a request to find all matches for the `text` in the web page and returns +an `Integer` representing the request id used for the request. The result of +the request can be obtained by subscribing to +[`found-in-page`](web-contents.md#event-found-in-page) event. ### `webContents.stopFindInPage(action)` @@ -647,7 +655,8 @@ Removes the specified path from DevTools workspace. ### `webContents.openDevTools([options])` * `options` Object (optional) - * `detach` Boolean - opens DevTools in a new window + * `mode` String - Opens the devtools with specified dock state, can be one of + "right", "bottom", "undocked", "detach". Defaults to last used dock state. Opens the devtools. diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index d9e02ac097e7..4b7d04b6f0ff 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -1,7 +1,6 @@ # webFrame -The `web-frame` module allows you to customize the rendering of the current -web page. +> Customize the rendering of the current web page. An example of zooming current page to 200%. @@ -13,7 +12,7 @@ webFrame.setZoomFactor(2); ## Methods -The `web-frame` module has the following methods: +The `webFrame` module has the following methods: ### `webFrame.setZoomFactor(factor)` diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index cee06dfa35db..5f4a422a13a9 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -1,5 +1,7 @@ # The `` tag +> Display external web content in an isolated frame and process. + Use the `webview` tag to embed 'guest' content (such as web pages) in your Electron app. The guest content is contained within the `webview` container. An embedded page within your app controls how the guest content is laid out and @@ -47,17 +49,17 @@ and displays a "loading..." message during the load time: ## CSS Styling Notes -Please note that the `webview` tag's style uses `display:flex;` internally to -ensure the child `object` element fills the full height and width of its `webview` -container when used with traditional and flexbox layouts (since v0.36.11). Please -do not overwrite the default `display:flex;` CSS property, unless specifying +Please note that the `webview` tag's style uses `display:flex;` internally to +ensure the child `object` element fills the full height and width of its `webview` +container when used with traditional and flexbox layouts (since v0.36.11). Please +do not overwrite the default `display:flex;` CSS property, unless specifying `display:inline-flex;` for inline layout. -`webview` has issues being hidden using the `hidden` attribute or using `display: none;`. -It can cause unusual rendering behaviour within its child `browserplugin` object -and the web page is reloaded, when the `webview` is un-hidden, as opposed to just -becoming visible again. The recommended approach is to hide the `webview` using -CSS by zeroing the `width` & `height` and allowing the element to shrink to the 0px +`webview` has issues being hidden using the `hidden` attribute or using `display: none;`. +It can cause unusual rendering behaviour within its child `browserplugin` object +and the web page is reloaded, when the `webview` is un-hidden, as opposed to just +becoming visible again. The recommended approach is to hide the `webview` using +CSS by zeroing the `width` & `height` and allowing the element to shrink to the 0px dimensions via `flex`. ```html @@ -70,7 +72,7 @@ dimensions via `flex`. webview.hide { flex: 0 1; width: 0px; - height: 0px; + height: 0px; } ``` @@ -504,6 +506,7 @@ Returns: * `errorCode` Integer * `errorDescription` String * `validatedURL` String +* `isMainFrame` Boolean This event is like `did-finish-load`, but fired when the load failed or was cancelled, e.g. `window.stop()` is invoked. @@ -762,6 +765,10 @@ Emitted when media is paused or done playing. ### Event: 'did-change-theme-color' +Returns: + +* `themeColor` String + Emitted when a page's theme color changes. This is usually due to encountering a meta tag: ```html diff --git a/docs/api/window-open.md b/docs/api/window-open.md index 46e74327741f..abb0760f121e 100644 --- a/docs/api/window-open.md +++ b/docs/api/window-open.md @@ -1,5 +1,7 @@ # The `window.open` function +> Open a new window and load a URL. + When `window.open` is called to create a new window in a web page, a new instance of `BrowserWindow` will be created for the `url` and a proxy will be returned to `window.open` to let the page have limited control over it. diff --git a/docs/development/build-instructions-osx.md b/docs/development/build-instructions-osx.md index 02bdb72e2279..b703a75897d9 100644 --- a/docs/development/build-instructions-osx.md +++ b/docs/development/build-instructions-osx.md @@ -22,8 +22,8 @@ $ git clone https://github.com/electron/electron.git ## Bootstrapping The bootstrap script will download all necessary build dependencies and create -the build project files. Notice that we're using [ninja](https://ninja-build.org/) to build Electron so -there is no Xcode project generated. +the build project files. Notice that we're using [ninja](https://ninja-build.org/) +to build Electron so there is no Xcode project generated. ```bash $ cd electron diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 73526b362584..3f94a18393cf 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -11,7 +11,8 @@ Follow the guidelines below for building Electron on Windows. * [Node.js](http://nodejs.org/download/) * [Git](http://git-scm.com) -If you don't currently have a Windows installation, [modern.ie](https://www.modern.ie/en-us/virtualization-tools#downloads) +If you don't currently have a Windows installation, +[modern.ie](https://www.modern.ie/en-us/virtualization-tools#downloads) has timebombed versions of Windows that you can use to build Electron. Building Electron is done entirely with command-line scripts and cannot be done diff --git a/docs/development/build-system-overview.md b/docs/development/build-system-overview.md index 61b88e14078c..bc27c1932fa6 100644 --- a/docs/development/build-system-overview.md +++ b/docs/development/build-system-overview.md @@ -1,7 +1,8 @@ # Build System Overview -Electron uses [gyp](https://gyp.gsrc.io/) for project generation and [ninja](https://ninja-build.org/) for building. Project -configurations can be found in the `.gyp` and `.gypi` files. +Electron uses [gyp](https://gyp.gsrc.io/) for project generation and +[ninja](https://ninja-build.org/) for building. Project configurations can +be found in the `.gyp` and `.gypi` files. ## Gyp Files diff --git a/docs/development/coding-style.md b/docs/development/coding-style.md index baf2a2cce7de..1840700f5237 100644 --- a/docs/development/coding-style.md +++ b/docs/development/coding-style.md @@ -45,8 +45,10 @@ Electron APIs uses the same capitalization scheme as Node.js: - When the module itself is a class like `BrowserWindow`, use `CamelCase`. - When the module is a set of APIs, like `globalShortcut`, use `mixedCase`. -- When the API is a property of object, and it is complex enough to be in a separate chapter like `win.webContents`, use `mixedCase`. -- For other non-module APIs, use natural titles, like ` Tag` or `Process Object`. +- When the API is a property of object, and it is complex enough to be in a + separate chapter like `win.webContents`, use `mixedCase`. +- For other non-module APIs, use natural titles, like ` Tag` or + `Process Object`. When creating a new API, it is preferred to use getters and setters instead of jQuery's one-function style. For example, `.getText()` and `.setText(text)` diff --git a/docs/development/debug-instructions-windows.md b/docs/development/debug-instructions-windows.md new file mode 100644 index 000000000000..3a1a05e11030 --- /dev/null +++ b/docs/development/debug-instructions-windows.md @@ -0,0 +1,93 @@ +# Debugging Electron in Windows + +If you experience crashes or issues in Electron that you believe are not caused +by your JavaScript application, but instead by Electron itself, debugging can +be a little bit tricky, especially for developers not used to native/C++ +debugging. However, using Visual Studio, GitHub's hosted Electron Symbol Server, +and the Electron source code, it is fairly easy to enable step-through debugging +with breakpoints inside Electron's source code. + +## Requirements + +* **A debug build of Electron**: The easiest way is usually building it + yourself, using the tools and prerequisites listed in the + [build instructions for Windows](build-instructions-osx.md). While you can + easily attach to and debug Electron as you can download it directly, you will + find that it is heavily optimized, making debugging substantially more + difficult: The debugger will not be able to show you the content of all + variables and the execution path can seem strange because of inlining, + tail calls, and other compiler optimizations. + +* **Visual Studio with C++ Tools**: The free community editions of Visual + Studio 2013 and Visual Studio 2015 both work. Once installed, + [configure Visual Studio to use GitHub's Electron Symbol server](setting-up-symbol-server.md). + It will enable Visual Studio to gain a better understanding of what happens + inside Electron, making it easier to present variables in a human-readable + format. + +* **ProcMon**: The [free SysInternals tool][sys-internals] allows you to inspect + a processes parameters, file handles, and registry operations. + +## Attaching to and Debugging Electron + +To start a debugging session, open up PowerShell/CMD and execute your debug +build of Electron, using the application to open as a parameter. + +```powershell +$ ./out/D/electron.exe ~/my-electron-app/ +``` + +### Setting Breakpoints + +Then, open up Visual Studio. Electron is not built with Visual Studio and hence +does not contain a project file - you can however open up the source code files +"As File", meaning that Visual Studio will open them up by themselves. You can +still set breakpoints - Visual Studio will automatically figure out that the +source code matches the code running in the attached process and break +accordingly. + +Relevant code files can be found in `./atom/` as well as in Brightray, found in +`./vendor/brightray/browser` and `./vendor/brightray/common`. If you're hardcore, +you can also debug Chromium directly, which is obviously found in `chromium_src`. + +### Attaching + +You can attach the Visual Studio debugger to a running process on a local or +remote computer. After the process is running, click Debug / Attach to Process +(or press `CTRL+ALT+P`) to open the "Attach to Process" dialog box. You can use +this capability to debug apps that are running on a local or remote computer, +debug multiple processes simultaneously. + +If Electron is running under a different user account, select the +`Show processes from all users` check box. Notice that depending on how many +BrowserWindows your app opened, you will see multiple processes. A typical +one-window app will result in Visual Studio presenting you with two +`Electron.exe` entries - one for the main process and one for the renderer +process. Since the list only gives you names, there's currently no reliable +way of figuring out which is which. + +### Which Process Should I Attach to? + +Code executed within the main process (that is, code found in or eventually run +by your main JavaScript file) as well as code called using the remote +(`require('electron').remote`) will run inside the main process, while other +code will execute inside its respective renderer process. + +You can be attached to multiple programs when you are debugging, but only one +program is active in the debugger at any time. You can set the active program +in the `Debug Location` toolbar or the `Processes window`. + +## Using ProcMon to Observe a Process + +While Visual Studio is fantastic for inspecting specific code paths, ProcMon's +strength is really in observing everything your application is doing with the +operating system - it captures File, Registry, Network, Process, and Profiling +details of processes. It attempts to log **all** events occurring and can be +quite overwhelming, but if you seek to understand what and how your application +is doing to the operating system, it can be a valuable resource. + +For an introduction to ProcMon's basic and advanced debugging features, go check +out [this video tutorial][procmon-instructions] provided by Microsoft. + +[sys-internals]: https://technet.microsoft.com/en-us/sysinternals/processmonitor.aspx +[procmon-instructions]: https://channel9.msdn.com/shows/defrag-tools/defrag-tools-4-process-monitor diff --git a/docs/development/source-code-directory-structure.md b/docs/development/source-code-directory-structure.md index 998280a37054..27cf36b9d4e7 100644 --- a/docs/development/source-code-directory-structure.md +++ b/docs/development/source-code-directory-structure.md @@ -34,7 +34,7 @@ Electron ├── default_app - The default page to show when Electron is started without | providing an app. ├── docs - Documentations. -├── lib - JavaScript source code. +├── lib - JavaScript source code. | ├── browser - Javascript main process initialization code. | | └── api - Javascript API implementation. | ├── common - JavaScript used by both the main and renderer processes @@ -62,3 +62,30 @@ Electron when creating a distribution. * **external_binaries** - Downloaded binaries of third-party frameworks which do not support building with `gyp`. + +## Keeping Git Submodules Up to Date + +The Electron repository has a few vendored dependencies, found in the +[/vendor](/vendor) directory. Occasionally you might see a message like this +when running `git status`: + +```sh +$ git status + + modified: vendor/brightray (new commits) + modified: vendor/node (new commits) +``` + +To update these vendored dependencies, run the following command: + +```sh +git submodule update --init --recursive +``` + +If you find yourself running this command often, you can create an alias for it +in your `~/.gitconfig` file: + +``` +[alias] + su = submodule update --init --recursive +``` diff --git a/docs/faq/electron-faq.md b/docs/faq/electron-faq.md index 41b301d71981..02d72744e657 100644 --- a/docs/faq/electron-faq.md +++ b/docs/faq/electron-faq.md @@ -77,8 +77,8 @@ app.on('ready', function() { ## I can not use jQuery/RequireJS/Meteor/AngularJS in Electron. Due to the Node.js integration of Electron, there are some extra symbols -inserted into the DOM like `module`, `exports`, `require`. This causes problems for -some libraries since they want to insert the symbols with the same names. +inserted into the DOM like `module`, `exports`, `require`. This causes problems +for some libraries since they want to insert the symbols with the same names. To solve this, you can turn off node integration in Electron: diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index ee7fc4c5fa47..714b7100cf47 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -68,9 +68,10 @@ $ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin ### 7. Load the debugger UI -Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome browser. You may have to click pause if starting with debug-brk to see the entry line. +Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome +browser. You may have to click pause if starting with debug-brk to see the +entry line. [node-inspector]: https://github.com/node-inspector/node-inspector [node-gyp-required-tools]: https://github.com/nodejs/node-gyp#installation [how-to-install-native-modules]: using-native-node-modules.md#how-to-install-native-modules - diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index 1186918799b9..110d2917ab47 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -53,8 +53,7 @@ GNOME, KDE. ### OS X Notifications are straight-forward on OS X, you should however be aware of -[Apple's Human Interface guidelines regarding -notifications](https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/NotificationCenter.html). +[Apple's Human Interface guidelines regarding notifications](https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/NotificationCenter.html). Note that notifications are limited to 256 bytes in size - and will be truncated if you exceed that limit. diff --git a/docs/tutorial/mac-app-store-submission-guide.md b/docs/tutorial/mac-app-store-submission-guide.md index ec8d8653ca08..15b6142b57a9 100644 --- a/docs/tutorial/mac-app-store-submission-guide.md +++ b/docs/tutorial/mac-app-store-submission-guide.md @@ -4,10 +4,6 @@ Since v0.34.0, Electron allows submitting packaged apps to the Mac App Store (MAS). This guide provides information on: how to submit your app and the limitations of the MAS build. -**Note:** From v0.36.0 there was a bug preventing GPU process to start after -the app being sandboxed, so it is recommended to use v0.35.x before this bug -gets fixed. You can find more about this in [issue #3871][issue-3871]. - **Note:** Submitting an app to Mac App Store requires enrolling [Apple Developer Program][developer-program], which costs money. @@ -56,6 +52,8 @@ First, you need to prepare two entitlements files. com.apple.security.app-sandbox + com.apple.security.temporary-exception.sbpl + (allow mach-lookup (global-name-regex #"^org.chromium.Chromium.rohitfork.[0-9]+$")) ``` @@ -67,8 +65,8 @@ And then sign your app with the following script: # Name of your app. APP="YourApp" -# The path of you app to sign. -APP_PATH="/path/to/YouApp.app" +# The path of your app to sign. +APP_PATH="/path/to/YourApp.app" # The path to the location you want to put the signed package. RESULT_PATH="~/Desktop/$APP.pkg" # The name of certificates you requested. @@ -77,17 +75,18 @@ INSTALLER_KEY="3rd Party Mac Developer Installer: Company Name (APPIDENTITY)" FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/" -if [ -d "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A" ]; then - # Signing a non-MAS build. - codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Mantle.framework/Versions/A" - codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/ReactiveCocoa.framework/Versions/A" - codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A" -fi -codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libnode.dylib" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/Contents/MacOS/$APP Helper EH" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/Contents/MacOS/$APP Helper NP" +codesign -s "$APP_KEY" -f --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/" +codesign -s "$APP_KEY" -f --entitlements child.plist "$APP_PATH/Contents/MacOS/$APP" +codesign -s "$APP_KEY" -f --entitlements parent.plist "$APP_PATH" productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH" ``` @@ -96,11 +95,32 @@ If you are new to app sandboxing under OS X, you should also read through Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then add keys for the permissions needed by your app to the entitlements files. -### Upload Your App and Submit for Review +### Upload Your App After signing your app, you can use Application Loader to upload it to iTunes Connect for processing, making sure you have [created a record][create-record] -before uploading. Then you can [submit your app for review][submit-for-review]. +before uploading. + +### Explain the Usages of `temporary-exception` + +When sandboxing your app there was a `temporary-exception` entry added to the +entitlements, according to the [App Sandbox Temporary Exception +Entitlements][temporary-exception] documentation, you have to explain why this +entry is needed: + +> Note: If you request a temporary-exception entitlement, be sure to follow the +guidance regarding entitlements provided on the iTunes Connect website. In +particular, identify the entitlement and corresponding issue number in the App +Sandbox Entitlement Usage Information section in iTunes Connect and explain why +your app needs the exception. + +You may explain that your app is built upon Chromium browser, which uses Mach +port for its multi-process architecture. But there is still probability that +your app failed the review because of this. + +### Submit Your App for Review + +After these steps, you can [submit your app for review][submit-for-review]. ## Limitations of MAS Build @@ -163,5 +183,5 @@ ERN)][ern-tutorial]. [create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html [submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html [app-sandboxing]: https://developer.apple.com/app-sandboxing/ -[issue-3871]: https://github.com/electron/electron/issues/3871 [ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ +[temporary-exception]: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 2a41d6356a89..573e296d4d17 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -80,45 +80,60 @@ The `main.js` should create windows and handle system events, a typical example being: ```javascript -'use strict'; - -const electron = require('electron'); -const app = electron.app; // Module to control application life. -const BrowserWindow = electron.BrowserWindow; // Module to create native browser window. +const electron = require('electron') +// Module to control application life. +const app = electron.app +// Module to create native browser window. +const BrowserWindow = electron.BrowserWindow // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. -var mainWindow = null; +let mainWindow -// Quit when all windows are closed. -app.on('window-all-closed', function() { - // On OS X it is common for applications and their menu bar - // to stay active until the user quits explicitly with Cmd + Q - if (process.platform != 'darwin') { - app.quit(); - } -}); - -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -app.on('ready', function() { +function createWindow () { // Create the browser window. - mainWindow = new BrowserWindow({width: 800, height: 600}); + mainWindow = new BrowserWindow({width: 800, height: 600}) // and load the index.html of the app. - mainWindow.loadURL('file://' + __dirname + '/index.html'); + mainWindow.loadURL('file://' + __dirname + '/index.html') // Open the DevTools. - mainWindow.webContents.openDevTools(); + mainWindow.webContents.openDevTools() // Emitted when the window is closed. - mainWindow.on('closed', function() { + mainWindow.on('closed', function () { // Dereference the window object, usually you would store windows // in an array if your app supports multi windows, this is the time // when you should delete the corresponding element. - mainWindow = null; - }); -}); + mainWindow = null + }) +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.on('ready', createWindow) + +// Quit when all windows are closed. +app.on('window-all-closed', function () { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', function () { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (mainWindow === null) { + createWindow() + } +}) + +// In this file you can include the rest of your app's specific main process +// code. You can also put them in separate files and require them here. + ``` Finally the `index.html` is the web page you want to show: @@ -147,8 +162,11 @@ working as expected. ### electron-prebuilt -If you've installed `electron-prebuilt` globally with `npm`, then you will only need -to run the following in your app's source directory: +[`electron-prebuilt`](https://github.com/electron-userland/electron-prebuilt) is +an `npm` module that contains pre-compiled versions of Electron. + +If you've installed it globally with `npm`, then you will only need to run the +following in your app's source directory: ```bash electron . diff --git a/docs/tutorial/testing-on-headless-ci.md b/docs/tutorial/testing-on-headless-ci.md index ec1f4635c906..f9090a6cbd79 100644 --- a/docs/tutorial/testing-on-headless-ci.md +++ b/docs/tutorial/testing-on-headless-ci.md @@ -18,9 +18,9 @@ Then, create a virtual xvfb screen and export an environment variable called DISPLAY that points to it. Chromium in Electron will automatically look for `$DISPLAY`, so no further configuration of your app is required. This step can be automated with Paul Betts's -[xfvb-maybe](https://github.com/paulcbetts/xvfb-maybe): Prepend your test -commands with `xfvb-maybe` and the little tool will automatically configure -xfvb, if required by the current system. On Windows or Mac OS X, it will simply +[xvfb-maybe](https://github.com/paulcbetts/xvfb-maybe): Prepend your test +commands with `xvfb-maybe` and the little tool will automatically configure +xvfb, if required by the current system. On Windows or Mac OS X, it will simply do nothing. ``` @@ -47,7 +47,7 @@ install: ### Jenkins -For Jenkins, a [Xfvb plugin is available](https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin). +For Jenkins, a [Xvfb plugin is available](https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin). ### Circle CI diff --git a/docs/tutorial/using-pepper-flash-plugin.md b/docs/tutorial/using-pepper-flash-plugin.md index a9918b220ac0..de7c7001cf36 100644 --- a/docs/tutorial/using-pepper-flash-plugin.md +++ b/docs/tutorial/using-pepper-flash-plugin.md @@ -1,6 +1,6 @@ # Using Pepper Flash Plugin -Electron now supports the Pepper Flash plugin. To use the Pepper Flash plugin in +Electron supports the Pepper Flash plugin. To use the Pepper Flash plugin in Electron, you should manually specify the location of the Pepper Flash plugin and then enable it in your application. @@ -15,25 +15,25 @@ location. You can directly add `--ppapi-flash-path` and `ppapi-flash-version` to the Electron command line or by using the `app.commandLine.appendSwitch` method -before the app ready event. Also, add the `plugins` switch of `browser-window`. +before the app ready event. Also, turn on `plugins` option of `BrowserWindow`. For example: ```javascript // Specify flash path. -// On Windows, it might be /path/to/pepflashplayer.dll +// On Windows, it might be /path/to/pepflashplayer.dll or just pepflashplayer.dll if it resides main.js // On OS X, /path/to/PepperFlashPlayer.plugin // On Linux, /path/to/libpepflashplayer.so app.commandLine.appendSwitch('ppapi-flash-path', '/path/to/libpepflashplayer.so'); -// Specify flash version, for example, v17.0.0.169 +// Optional: Specify flash version, for example, v17.0.0.169 app.commandLine.appendSwitch('ppapi-flash-version', '17.0.0.169'); app.on('ready', function() { mainWindow = new BrowserWindow({ - 'width': 800, - 'height': 600, - 'web-preferences': { - 'plugins': true + width: 800, + height: 600, + webPreferences: { + plugins: true } }); mainWindow.loadURL('file://' + __dirname + '/index.html'); @@ -48,3 +48,13 @@ Add `plugins` attribute to `` tag. ```html ``` + +## Troubleshooting + +You can check if Pepper Flash plugin was loaded by inspecting +`navigator.plugins` in the console of devtools (although you can't know if the +plugin's path is correct). + +The architecture of Pepper Flash plugin has to match Electron's one. On Windows, +a common error is to use 32bit version of Flash plugin against 64bit version of +Electron. diff --git a/electron.gyp b/electron.gyp index 7a431028f198..66ff2fd5baf8 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.37.6', + 'version%': '0.37.8', }, 'includes': [ 'filenames.gypi', @@ -310,6 +310,9 @@ ], }], # OS=="mac" and mas_build==1 ['OS=="linux"', { + 'sources': [ + '<@(lib_sources_nss)', + ], 'link_settings': { 'ldflags': [ # Make binary search for libraries under current directory, so we diff --git a/filenames.gypi b/filenames.gypi index dd9edb409f59..1c2139497567 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -28,6 +28,7 @@ 'lib/browser/api/protocol.js', 'lib/browser/api/session.js', 'lib/browser/api/screen.js', + 'lib/browser/api/system-preferences.js', 'lib/browser/api/tray.js', 'lib/browser/api/web-contents.js', 'lib/browser/chrome-extension.js', @@ -115,6 +116,9 @@ 'atom/browser/api/atom_api_screen.h', 'atom/browser/api/atom_api_session.cc', 'atom/browser/api/atom_api_session.h', + 'atom/browser/api/atom_api_system_preferences.cc', + 'atom/browser/api/atom_api_system_preferences.h', + 'atom/browser/api/atom_api_system_preferences_mac.mm', 'atom/browser/api/atom_api_tray.cc', 'atom/browser/api/atom_api_tray.h', 'atom/browser/api/atom_api_web_contents.cc', @@ -299,6 +303,10 @@ 'atom/common/api/locker.h', 'atom/common/api/object_life_monitor.cc', 'atom/common/api/object_life_monitor.h', + 'atom/common/api/remote_callback_freer.cc', + 'atom/common/api/remote_callback_freer.h', + 'atom/common/api/remote_object_freer.cc', + 'atom/common/api/remote_object_freer.h', 'atom/common/asar/archive.cc', 'atom/common/asar/archive.h', 'atom/common/asar/asar_util.cc', @@ -517,6 +525,10 @@ '<@(native_mate_files)', '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', ], + 'lib_sources_nss': [ + 'chromium_src/chrome/browser/certificate_manager_model.cc', + 'chromium_src/chrome/browser/certificate_manager_model.h', + ], 'lib_sources_win': [ 'chromium_src/chrome/browser/ui/views/color_chooser_dialog.cc', 'chromium_src/chrome/browser/ui/views/color_chooser_dialog.h', diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 48a27cfc648d..0487270ce7b3 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -1,8 +1,7 @@ 'use strict' -const deprecate = require('electron').deprecate -const session = require('electron').session -const Menu = require('electron').Menu +const electron = require('electron') +const {deprecate, session, Menu} = electron const EventEmitter = require('events').EventEmitter const bindings = process.atomBinding('app') @@ -65,39 +64,49 @@ for (i = 0, len = ref1.length; i < len; i++) { } // Deprecated. - app.getHomeDir = deprecate('app.getHomeDir', 'app.getPath', function () { return this.getPath('home') }) - app.getDataPath = deprecate('app.getDataPath', 'app.getPath', function () { return this.getPath('userData') }) - app.setDataPath = deprecate('app.setDataPath', 'app.setPath', function (path) { return this.setPath('userData', path) }) - app.resolveProxy = deprecate('app.resolveProxy', 'session.defaultSession.resolveProxy', function (url, callback) { return session.defaultSession.resolveProxy(url, callback) }) - deprecate.rename(app, 'terminate', 'quit') - deprecate.event(app, 'finish-launching', 'ready', function () { // give default app a chance to setup default menu. setImmediate(() => { this.emit('finish-launching') }) }) - deprecate.event(app, 'activate-with-no-open-windows', 'activate', function (event, hasVisibleWindows) { if (!hasVisibleWindows) { return this.emit('activate-with-no-open-windows', event) } }) - deprecate.event(app, 'select-certificate', 'select-client-certificate') +if (process.platform === 'win32') { + app.isAeroGlassEnabled = deprecate('app.isAeroGlassEnabled', 'systemPreferences.isAeroGlassEnabled', function () { + return electron.systemPreferences.isAeroGlassEnabled() + }) +} else if (process.platform === 'darwin') { + app.isDarkMode = deprecate('app.isDarkMode', 'systemPreferences.isDarkMode', function () { + return electron.systemPreferences.isDarkMode() + }) + app.on = app.addListener = function (event, listener) { + if (event === 'platform-theme-changed') { + deprecate.warn('platform-theme-changed event', "systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', callback)") + electron.systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', function () { + app.emit('platform-theme-changed') + }) + } + EventEmitter.prototype.addListener.call(app, event, listener) + } +} // Wrappers for native classes. var wrapDownloadItem = function (downloadItem) { diff --git a/lib/browser/api/auto-updater/auto-updater-win.js b/lib/browser/api/auto-updater/auto-updater-win.js index 896b9ffce9d2..5b1127027e9d 100644 --- a/lib/browser/api/auto-updater/auto-updater-win.js +++ b/lib/browser/api/auto-updater/auto-updater-win.js @@ -12,6 +12,9 @@ function AutoUpdater () { util.inherits(AutoUpdater, EventEmitter) AutoUpdater.prototype.quitAndInstall = function () { + if (!this.updateAvailable) { + return this.emitError('No update available, can\'t quit and install') + } squirrelUpdate.processStart() return app.quit() } @@ -33,8 +36,10 @@ AutoUpdater.prototype.checkForUpdates = function () { return this.emitError(error) } if (update == null) { + this.updateAvailable = false return this.emit('update-not-available') } + this.updateAvailable = true this.emit('update-available') squirrelUpdate.update(this.updateURL, (error) => { var date, releaseNotes, version diff --git a/lib/browser/api/exports/electron.js b/lib/browser/api/exports/electron.js index bd8285401c79..9d873663029c 100644 --- a/lib/browser/api/exports/electron.js +++ b/lib/browser/api/exports/electron.js @@ -89,6 +89,12 @@ Object.defineProperties(exports, { return require('../session') } }, + systemPreferences: { + enumerable: true, + get: function () { + return require('../system-preferences') + } + }, Tray: { enumerable: true, get: function () { diff --git a/lib/browser/api/system-preferences.js b/lib/browser/api/system-preferences.js new file mode 100644 index 000000000000..6ba1750507c0 --- /dev/null +++ b/lib/browser/api/system-preferences.js @@ -0,0 +1,6 @@ +const {EventEmitter} = require('events') +const {systemPreferences} = process.atomBinding('system_preferences') + +Object.setPrototypeOf(systemPreferences, EventEmitter.prototype) + +module.exports = systemPreferences diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index 40efa77cda02..823351ebe116 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -116,10 +116,12 @@ let wrapWebContents = function (webContents) { callback = hasUserGesture hasUserGesture = false } - if (this.getURL() && !this.isLoading()) { - return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) + if (this.getURL() && !this.isLoadingMainFrame()) { + asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) } else { - return this.once('did-finish-load', asyncWebFrameMethods.bind(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)) + this.once('did-finish-load', () => { + asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) + }) } } diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index ad79fe699a40..58775669c2f4 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -182,6 +182,11 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params webSecurity: !params.disablewebsecurity, blinkFeatures: params.blinkfeatures } + + if (embedder.getWebPreferences().nodeIntegration === false) { + webPreferences.nodeIntegration = false + } + if (params.preload) { webPreferences.preloadURL = params.preload } diff --git a/lib/browser/objects-registry.js b/lib/browser/objects-registry.js index b8aa480a6dba..adbf6835554c 100644 --- a/lib/browser/objects-registry.js +++ b/lib/browser/objects-registry.js @@ -50,7 +50,10 @@ class ObjectsRegistry { this.dereference(id) // Also remove the reference in owner. - this.owners[webContentsId].delete(id) + let owner = this.owners[webContentsId] + if (owner) { + owner.delete(id) + } } // Clear all references to objects refrenced by the WebContents. diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index 4603ba332c26..1dff5fdb8ade 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -12,8 +12,19 @@ const FUNCTION_PROPERTIES = [ ] // The remote functions in renderer processes. -// (webContentsId) => {id: Function} -let rendererFunctions = {} +// id => Function +let rendererFunctions = new IDWeakMap() + +// Merge two IDs together. +let mergeIds = function (webContentsId, metaId) { + const PADDING_BITS = 20 + if ((webContentsId << PADDING_BITS) < 0) { + throw new Error(`webContents ID is too large: ${webContentsId}`) + } else if (metaId > (1 << PADDING_BITS)) { + throw new Error(`Object ID is too large: ${metaId}`) + } + return (webContentsId << PADDING_BITS) + metaId +} // Return the description of object's members: let getObjectMembers = function (object) { @@ -50,50 +61,37 @@ let getObjectPrototype = function (object) { } // Convert a real value into meta data. -var valueToMeta = function (sender, value, optimizeSimpleObject) { - var el, i, len, meta - if (optimizeSimpleObject == null) { - optimizeSimpleObject = false - } - meta = { - type: typeof value - } - if (Buffer.isBuffer(value)) { - meta.type = 'buffer' - } - if (value === null) { - meta.type = 'value' - } - if (Array.isArray(value)) { - meta.type = 'array' - } - if (value instanceof Error) { - meta.type = 'error' - } - if (value instanceof Date) { - meta.type = 'date' - } - if ((value != null ? value.constructor.name : void 0) === 'Promise') { - meta.type = 'promise' - } - - // Treat simple objects as value. - if (optimizeSimpleObject && meta.type === 'object' && v8Util.getHiddenValue(value, 'simple')) { - meta.type = 'value' - } - - // Treat the arguments object as array. - if (meta.type === 'object' && (value.hasOwnProperty('callee')) && (value.length != null)) { - meta.type = 'array' - } - if (meta.type === 'array') { - meta.members = [] - for (i = 0, len = value.length; i < len; i++) { - el = value[i] - meta.members.push(valueToMeta(sender, el)) +let valueToMeta = function (sender, value, optimizeSimpleObject = false) { + // Determine the type of value. + let meta = { type: typeof value } + if (meta.type === 'object') { + // Recognize certain types of objects. + if (value === null) { + meta.type = 'value' + } else if (Buffer.isBuffer(value)) { + meta.type = 'buffer' + } else if (Array.isArray(value)) { + meta.type = 'array' + } else if (value instanceof Error) { + meta.type = 'error' + } else if (value instanceof Date) { + meta.type = 'date' + } else if (value.constructor != null && value.constructor.name === 'Promise') { + meta.type = 'promise' + } else if (value.hasOwnProperty('callee') && value.length != null) { + // Treat the arguments object as array. + meta.type = 'array' + } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) { + // Treat simple objects as value. + meta.type = 'value' } + } + + // Fill the meta object according to value's type. + if (meta.type === 'array') { + meta.members = value.map((el) => valueToMeta(sender, el)) } else if (meta.type === 'object' || meta.type === 'function') { - meta.name = value.constructor.name + meta.name = value.constructor ? value.constructor.name : '' // Reference the original value if it's an object, because when it's // passed to renderer we would assume the renderer keeps a reference of @@ -178,32 +176,26 @@ var unwrapArgs = function (sender, args) { return returnValue } case 'function': { + // Merge webContentsId and meta.id, since meta.id can be the same in + // different webContents. + const webContentsId = sender.getId() + const objectId = mergeIds(webContentsId, meta.id) + // Cache the callbacks in renderer. - let webContentsId = sender.getId() - let callbacks = rendererFunctions[webContentsId] - if (!callbacks) { - callbacks = rendererFunctions[webContentsId] = new IDWeakMap() - sender.once('render-view-deleted', function (event, id) { - callbacks.clear() - delete rendererFunctions[id] - }) + if (rendererFunctions.has(objectId)) { + return rendererFunctions.get(objectId) } - if (callbacks.has(meta.id)) return callbacks.get(meta.id) - let callIntoRenderer = function (...args) { - if ((webContentsId in rendererFunctions) && !sender.isDestroyed()) { + if (!sender.isDestroyed() && webContentsId === sender.getId()) { sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args)) } else { throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`) } } - v8Util.setDestructor(callIntoRenderer, function () { - if ((webContentsId in rendererFunctions) && !sender.isDestroyed()) { - sender.send('ELECTRON_RENDERER_RELEASE_CALLBACK', meta.id) - } - }) - callbacks.set(meta.id, callIntoRenderer) + + v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender) + rendererFunctions.set(objectId, callIntoRenderer) return callIntoRenderer } default: diff --git a/lib/common/init.js b/lib/common/init.js index 221febb0c37a..11c098d3ce5c 100644 --- a/lib/common/init.js +++ b/lib/common/init.js @@ -44,3 +44,12 @@ if (process.type === 'browser') { global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout) global.setInterval = wrapWithActivateUvLoop(timers.setInterval) } + +// If we're running as a Windows Store app, __dirname will be set +// to C:/Program Files/WindowsApps. +// +// Nobody else get's to install there, changing the path is forbidden +// We can therefore say that we're running as appx +if (process.platform === 'win32' && __dirname.indexOf('\\Program Files\\WindowsApps\\') === 2) { + process.windowsStore = true +} diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 8865c33c2772..6631ea22575a 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -45,17 +45,19 @@ var wrapArgs = function (args, visited) { type: 'date', value: value.getTime() } - } else if ((value != null ? value.constructor.name : void 0) === 'Promise') { - return { - type: 'promise', - then: valueToMeta(function (v) { value.then(v) }) - } - } else if ((value != null) && typeof value === 'object' && v8Util.getHiddenValue(value, 'atomId')) { - return { - type: 'remote-object', - id: v8Util.getHiddenValue(value, 'atomId') - } } else if ((value != null) && typeof value === 'object') { + if (value.constructor != null && value.constructor.name === 'Promise') { + return { + type: 'promise', + then: valueToMeta(function (v) { value.then(v) }) + } + } else if (v8Util.getHiddenValue(value, 'atomId')) { + return { + type: 'remote-object', + id: v8Util.getHiddenValue(value, 'atomId') + } + } + ret = { type: 'object', name: value.constructor.name, @@ -208,9 +210,7 @@ let metaToValue = function (meta) { // Track delegate object's life time, and tell the browser to clean up // when the object is GCed. - v8Util.setDestructor(ret, function () { - ipcRenderer.send('ELECTRON_BROWSER_DEREFERENCE', meta.id) - }) + v8Util.setRemoteObjectFreer(ret, meta.id) // Remember object's id. v8Util.setHiddenValue(ret, 'atomId', meta.id) diff --git a/lib/renderer/web-view/web-view.js b/lib/renderer/web-view/web-view.js index 974d5c6608d6..20f5f07465b5 100644 --- a/lib/renderer/web-view/web-view.js +++ b/lib/renderer/web-view/web-view.js @@ -335,6 +335,7 @@ var registerWebViewElement = function () { 'loadURL', 'getTitle', 'isLoading', + 'isLoadingMainFrame', 'isWaitingForResponse', 'stop', 'reload', diff --git a/package.json b/package.json index d6306b8406ee..7d40098f099a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "0.37.6", + "version": "0.37.8", "devDependencies": { "asar": "^0.11.0", "request": "*", diff --git a/script/cibuild b/script/cibuild index fda5817b6b84..7d025b4e82cd 100755 --- a/script/cibuild +++ b/script/cibuild @@ -53,8 +53,6 @@ def main(): if PLATFORM == 'linux': os.environ['DISPLAY'] = ':99.0' - run_script('clean.py') - # CI's npm is not reliable. npm = 'npm.cmd' if PLATFORM == 'win32' else 'npm' execute([npm, 'install', 'npm@2.12.1']) @@ -77,12 +75,12 @@ def main(): run_script('create-dist.py') run_script('upload.py') else: + if PLATFORM == 'win32': + os.environ['OUTPUT_TO_FILE'] = 'output.log' run_script('build.py', ['-c', 'D']) - if PLATFORM != 'win32' and target_arch == 'x64': + if PLATFORM == 'win32' or target_arch == 'x64': run_script('test.py', ['--ci']) - run_script('clean.py') - def run_script(script, args=[]): sys.stderr.write('\nRunning ' + script +'\n') diff --git a/script/test.py b/script/test.py index 28aeac9dc1ff..5adfd4eed6c5 100755 --- a/script/test.py +++ b/script/test.py @@ -4,7 +4,7 @@ import os import subprocess import sys -from lib.util import atom_gyp +from lib.util import atom_gyp, rm_rf SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) @@ -30,7 +30,20 @@ def main(): else: atom_shell = os.path.join(SOURCE_ROOT, 'out', config, PROJECT_NAME) - subprocess.check_call([atom_shell, 'spec'] + sys.argv[1:]) + returncode = 0 + try: + subprocess.check_call([atom_shell, 'spec'] + sys.argv[1:]) + except subprocess.CalledProcessError as e: + returncode = e.returncode + + if os.environ.has_key('OUTPUT_TO_FILE'): + output_to_file = os.environ['OUTPUT_TO_FILE'] + with open(output_to_file, 'r') as f: + print f.read() + rm_rf(output_to_file) + + + return returncode if __name__ == '__main__': diff --git a/script/upload.py b/script/upload.py index fd795172a679..80d9e5e9e090 100755 --- a/script/upload.py +++ b/script/upload.py @@ -101,6 +101,10 @@ def main(): upload_atom_shell(github, release, os.path.join(DIST_DIR, mksnapshot)) if PLATFORM == 'win32' and not tag_exists: + # Upload PDBs to Windows symbol server. + execute([sys.executable, + os.path.join(SOURCE_ROOT, 'script', 'upload-windows-pdb.py')]) + # Upload node headers. execute([sys.executable, os.path.join(SOURCE_ROOT, 'script', 'upload-node-headers.py'), diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index c237ef17238e..1c20ef8e4523 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -1,5 +1,7 @@ const assert = require('assert') const ChildProcess = require('child_process') +const https = require('https') +const fs = require('fs') const path = require('path') const remote = require('electron').remote @@ -87,6 +89,70 @@ describe('app module', function () { }) }) + describe('app.importCertificate', function () { + if (process.platform !== 'linux') + return + + this.timeout(5000) + + var w = null + var certPath = path.join(__dirname, 'fixtures', 'certificates') + var options = { + key: fs.readFileSync(path.join(certPath, 'server.key')), + cert: fs.readFileSync(path.join(certPath, 'server.pem')), + ca: [ + fs.readFileSync(path.join(certPath, 'rootCA.pem')), + fs.readFileSync(path.join(certPath, 'intermediateCA.pem')) + ], + requestCert: true, + rejectUnauthorized: false + } + + var server = https.createServer(options, function (req, res) { + if (req.client.authorized) { + res.writeHead(200); + res.end('authorized'); + } + }) + + afterEach(function () { + if (w != null) { + w.destroy() + } + w = null + }) + + it('can import certificate into platform cert store', function (done) { + let options = { + certificate: path.join(certPath, 'client.p12'), + password: 'electron' + } + + w = new BrowserWindow({ + show: false + }) + + w.webContents.on('did-finish-load', function () { + server.close() + done() + }) + + app.on('select-client-certificate', function (event, webContents, url, list, callback) { + assert.equal(list.length, 1) + assert.equal(list[0].issuerName, 'Intermediate CA') + callback(list[0]) + }) + + app.importCertificate(options, function (result) { + assert(!result) + server.listen(0, '127.0.0.1', function () { + var port = server.address().port + w.loadURL(`https://127.0.0.1:${port}`) + }) + }) + }) + }) + describe('BrowserWindow events', function () { var w = null diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 2a8d1295c7fd..e76821020bdc 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -4,6 +4,7 @@ const assert = require('assert') const fs = require('fs') const path = require('path') const os = require('os') +const http = require('http') const remote = require('electron').remote const screen = require('electron').screen @@ -18,6 +19,23 @@ const isCI = remote.getGlobal('isCi') describe('browser-window module', function () { var fixtures = path.resolve(__dirname, 'fixtures') var w = null + var server + + before(function (done) { + server = http.createServer(function (req, res) { + function respond() { res.end(''); } + setTimeout(respond, req.url.includes('slow') ? 200 : 0) + }); + server.listen(0, '127.0.0.1', function () { + server.url = 'http://127.0.0.1:' + server.address().port + done() + }) + }) + + after(function () { + server.close() + server = null + }) beforeEach(function () { if (w != null) { @@ -155,11 +173,12 @@ describe('browser-window module', function () { }) it('does not crash in did-fail-provisional-load handler', function (done) { + this.timeout(10000) w.webContents.once('did-fail-provisional-load', function () { - w.loadURL('http://localhost:11111') + w.loadURL('http://127.0.0.1:11111') done() }) - w.loadURL('http://localhost:11111') + w.loadURL('http://127.0.0.1:11111') }) }) @@ -634,6 +653,44 @@ describe('browser-window module', function () { assert.equal(w.isResizable(), true) }) }) + + describe('loading main frame state', function () { + it('is true when the main frame is loading', function (done) { + w.webContents.on('did-start-loading', function() { + assert.equal(w.webContents.isLoadingMainFrame(), true) + done() + }) + w.webContents.loadURL(server.url) + }) + + it('is false when only a subframe is loading', function (done) { + w.webContents.once('did-finish-load', function() { + assert.equal(w.webContents.isLoadingMainFrame(), false) + w.webContents.on('did-start-loading', function() { + assert.equal(w.webContents.isLoadingMainFrame(), false) + done() + }) + w.webContents.executeJavaScript(` + var iframe = document.createElement('iframe') + iframe.src = '${server.url}/page2' + document.body.appendChild(iframe) + `) + }) + w.webContents.loadURL(server.url) + }) + + it('is true when navigating to pages from the same origin', function (done) { + w.webContents.once('did-finish-load', function() { + assert.equal(w.webContents.isLoadingMainFrame(), false) + w.webContents.on('did-start-loading', function() { + assert.equal(w.webContents.isLoadingMainFrame(), true) + done() + }) + w.webContents.loadURL(`${server.url}/page2`) + }) + w.webContents.loadURL(server.url) + }) + }) }) describe('window states (excluding Linux)', function () { @@ -798,6 +855,30 @@ describe('browser-window module', function () { done() }) }) + + it('works after page load and during subframe load', function (done) { + w.webContents.once('did-finish-load', function() { + // initiate a sub-frame load, then try and execute script during it + w.webContents.executeJavaScript(` + var iframe = document.createElement('iframe') + iframe.src = '${server.url}/slow' + document.body.appendChild(iframe) + `, function() { + w.webContents.executeJavaScript(`console.log('hello')`, function() { + done() + }) + }) + }) + w.loadURL(server.url) + }) + + it('executes after page load', function (done) { + w.webContents.executeJavaScript(code, function(result) { + assert.equal(result, expected) + done() + }) + w.loadURL(server.url) + }) }) describe('deprecated options', function () { diff --git a/spec/api-crash-reporter-spec.js b/spec/api-crash-reporter-spec.js index 2e49fdc36c5c..e5a3223eed8f 100644 --- a/spec/api-crash-reporter-spec.js +++ b/spec/api-crash-reporter-spec.js @@ -44,7 +44,7 @@ describe('crash-reporter module', function () { if (called) return called = true assert.equal(fields['prod'], 'Electron') - assert.equal(fields['ver'], process.versions['electron']) + assert.equal(fields['ver'], process.versions.electron) assert.equal(fields['process_type'], 'renderer') assert.equal(fields['platform'], process.platform) assert.equal(fields['extra1'], 'extra1') diff --git a/spec/api-desktop-capturer-spec.js b/spec/api-desktop-capturer-spec.js index 9e85a48fbcc5..35b7248ed5ee 100644 --- a/spec/api-desktop-capturer-spec.js +++ b/spec/api-desktop-capturer-spec.js @@ -1,7 +1,13 @@ const assert = require('assert') const desktopCapturer = require('electron').desktopCapturer +const isCI = require('electron').remote.getGlobal('isCi') + describe('desktopCapturer', function () { + if (isCI && process.platform === 'win32') { + return + } + it('should return a non-empty array of sources', function (done) { desktopCapturer.getSources({ types: ['window', 'screen'] @@ -24,4 +30,16 @@ describe('desktopCapturer', function () { desktopCapturer.getSources({types: ['window', 'screen']}, callback) desktopCapturer.getSources({types: ['window', 'screen']}, callback) }) + + it('responds to subsequest calls of different options', function (done) { + var callCount = 0 + var callback = function (error, sources) { + callCount++ + assert.equal(error, null) + if (callCount === 2) done() + } + + desktopCapturer.getSources({types: ['window']}, callback) + desktopCapturer.getSources({types: ['screen']}, callback) + }) }) diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 1d37d0fe051d..577f020128ad 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -62,6 +62,10 @@ describe('chromium feature', function () { w.loadURL(url) }) + if (isCI && process.platform === 'win32') { + return + } + it('is set correctly when window is inactive', function (done) { w = new BrowserWindow({ show: false @@ -97,6 +101,9 @@ describe('chromium feature', function () { if (isCI && process.platform === 'linux') { return } + if (isCI && process.platform === 'win32') { + return + } it('can return labels of enumerated devices', function (done) { navigator.mediaDevices.enumerateDevices().then((devices) => { @@ -327,6 +334,10 @@ describe('chromium feature', function () { }) describe('webgl', function () { + if (isCI && process.platform === 'win32') { + return + } + it('can be get as context in canvas', function () { if (process.platform === 'linux') return diff --git a/spec/fixtures/certificates/certs.cnf b/spec/fixtures/certificates/certs.cnf new file mode 100644 index 000000000000..76ef8d073f9d --- /dev/null +++ b/spec/fixtures/certificates/certs.cnf @@ -0,0 +1,68 @@ +ID=1 +CA_DIR=out + +[ca] +default_ca = ca_settings + +[ca_settings] +dir = ${ENV::CA_DIR} +database = $dir/${ENV::ID}-index.txt +new_certs_dir = $dir +serial = $dir/${ENV::ID}-serial +certificate = $dir/${ENV::ID}.pem +private_key = $dir/${ENV::ID}.key +RANDFILE = $dir/rand +default_md = sha256 +default_days = 3650 +policy = policy_anything +preserve = no + +[policy_anything] +# Default signing policy +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[req] +default_bits = 2048 +default_md = sha256 +string_mask = utf8only +distinguished_name = req_env_dn +prompt = no + +[user_cert] +basicConstraints = CA:FALSE +nsCertType = client +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection + +[server_cert] +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth + +[ca_cert] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ca_intermediate_cert] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true, pathlen:0 +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[req_env_dn] +commonName = ${ENV::COMMON_NAME} diff --git a/spec/fixtures/certificates/client.p12 b/spec/fixtures/certificates/client.p12 new file mode 100644 index 000000000000..7543c7d38904 Binary files /dev/null and b/spec/fixtures/certificates/client.p12 differ diff --git a/spec/fixtures/certificates/generate_certs.sh b/spec/fixtures/certificates/generate_certs.sh new file mode 100755 index 000000000000..c8e6217e66fd --- /dev/null +++ b/spec/fixtures/certificates/generate_certs.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# This script generates certificates that can be used to test SSL client +# authentication. +# +# 1. A (end-entity) -> B -> C (self-signed root) +# 2. D (end-entity) -> B -> C (self-signed root) + +try () { + echo "$@" + "$@" || exit 1 +} + +try mkdir out + +echo Create the serial number files and indices. +serial=1000 +for i in B C +do + try /bin/sh -c "echo $serial > out/$i-serial" + serial=$(expr $serial + 1) + touch out/$i-index.txt + touch out/$i-index.txt.attr +done + +echo Generate the keys. +for i in A B C D +do + try openssl genrsa -out out/$i.key 2048 +done + +echo Generate the C CSR +COMMON_NAME="Root CA" \ + CA_DIR=out \ + ID=C \ + try openssl req \ + -new \ + -key out/C.key \ + -out out/C.csr \ + -config certs.cnf + +echo C signs itself. +COMMON_NAME="Root CA" \ + CA_DIR=out \ + ID=C \ + try openssl x509 \ + -req -days 3650 \ + -in out/C.csr \ + -extensions ca_cert \ + -extfile certs.cnf \ + -signkey out/C.key \ + -out out/C.pem + +echo Generate the intermediates +COMMON_NAME="Intermediate CA" \ + CA_DIR=out \ + ID=B \ + try openssl req \ + -new \ + -key out/B.key \ + -out out/B.csr \ + -config certs.cnf + +COMMON_NAME="Root CA" \ + CA_DIR=out \ + ID=C \ + try openssl ca \ + -batch \ + -extensions ca_intermediate_cert \ + -in out/B.csr \ + -out out/B.pem \ + -config certs.cnf + +echo Generate the leaf certs +COMMON_NAME="Client Cert" \ + ID=A \ + try openssl req \ + -new \ + -key out/A.key \ + -out out/A.csr \ + -config certs.cnf + +echo B signs A +COMMON_NAME="Intermediate CA" \ + CA_DIR=out \ + ID=B \ + try openssl ca \ + -batch \ + -extensions user_cert \ + -in out/A.csr \ + -out out/A.pem \ + -config certs.cnf + +COMMON_NAME="localhost" \ + ID=D \ + try openssl req \ + -new \ + -key out/D.key \ + -out out/D.csr \ + -config certs.cnf + +echo B signs D +COMMON_NAME="Intermediate CA" \ + CA_DIR=out \ + ID=B \ + try openssl ca \ + -batch \ + -extensions server_cert \ + -in out/D.csr \ + -out out/D.pem \ + -config certs.cnf + +echo Package the client cert and private key into PKCS12 file +try /bin/sh -c "cat out/A.pem out/A.key out/B.pem out/C.pem > out/A-chain.pem" + +try openssl pkcs12 \ + -in out/A-chain.pem \ + -out client.p12 \ + -export \ + -passout pass:electron + +echo Package the certs +try cp out/C.pem rootCA.pem +try cp out/B.pem intermediateCA.pem +try cp out/D.key server.key +try cp out/D.pem server.pem + +try rm -rf out diff --git a/spec/fixtures/certificates/intermediateCA.pem b/spec/fixtures/certificates/intermediateCA.pem new file mode 100644 index 000000000000..58293f76da0b --- /dev/null +++ b/spec/fixtures/certificates/intermediateCA.pem @@ -0,0 +1,78 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4097 (0x1001) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root CA + Validity + Not Before: Apr 18 16:14:29 2016 GMT + Not After : Apr 16 16:14:29 2026 GMT + Subject: CN=Intermediate CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b6:42:02:13:25:40:13:a6:05:99:69:da:0c:c9: + a8:bf:86:3b:fc:c6:51:ba:64:65:7e:33:11:31:d5: + 03:45:30:4c:ca:49:d2:96:42:52:2f:f9:e6:6c:9a: + 50:1c:fe:fa:e2:e8:63:36:14:47:f7:49:9f:78:28: + 5e:1f:0b:9d:9e:f8:d3:33:77:06:4d:6d:14:c0:57: + 01:83:2b:ef:99:06:48:21:ec:c1:d7:05:48:2c:ea: + 83:06:6a:20:df:73:ce:8a:a5:e4:81:00:41:84:cf: + 89:81:78:2e:3a:bd:1b:fd:3e:96:08:8d:44:1b:00: + c8:d6:4e:7a:6a:75:c0:9b:3c:e0:fa:aa:3a:82:5b: + 3c:39:32:ca:4a:ba:82:bc:60:47:6f:e4:4a:fd:dc: + a0:72:8a:1b:fe:cd:2e:10:f4:27:4c:08:4e:d1:ed: + dc:08:b0:f8:1f:e4:fc:45:72:43:58:6e:dd:05:37: + 8c:04:a1:fb:64:f4:3f:90:bb:85:f2:4c:97:46:fd: + 1f:29:e5:19:d0:0f:24:fd:d1:00:c5:b6:be:da:84: + 62:77:be:db:67:f6:ec:98:5d:97:f5:df:0a:bd:b8: + 07:7f:0a:d5:92:29:1f:c4:b0:97:4f:e4:87:d7:a9: + 00:c9:61:d5:6c:cd:6a:fc:56:c3:f3:b7:ca:53:70: + 02:3f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + A9:75:99:CF:9C:92:54:A4:4B:65:CD:3D:FC:93:98:8D:9E:09:1F:47 + X509v3 Authority Key Identifier: + keyid:E3:51:87:E3:CD:7A:B3:26:9F:8F:EC:62:D1:0E:15:0C:39:36:47:4F + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Signature Algorithm: sha256WithRSAEncryption + 55:69:d6:1d:33:ad:ab:40:46:fd:34:02:c1:43:50:7b:90:ea: + f3:5f:4f:b6:2c:28:aa:72:e0:4b:36:2e:8f:44:93:15:52:14: + f6:61:b3:50:e0:ba:43:91:ba:a9:5d:ac:43:b7:52:ca:91:a3: + d7:0e:ac:a7:9e:ee:28:7f:2d:0f:93:b5:d9:23:35:68:54:29: + 2a:e7:3a:4c:41:24:d0:5e:2d:f3:1e:b9:52:f1:3e:16:76:93: + 89:6d:a1:4c:63:f5:4a:cc:08:36:61:29:0a:29:5f:f4:5a:55: + 98:10:b3:de:b3:90:f9:03:e5:bd:1b:61:01:a7:22:03:ae:0f: + 77:c4:a8:bf:31:b4:af:c8:c7:e3:25:a1:2b:b9:43:37:3b:08: + ea:c4:46:60:b8:5f:ee:2a:0d:ce:18:75:63:ba:32:28:84:f4: + 56:95:1b:c5:f9:46:7e:14:2e:83:5e:a9:ff:b2:80:ca:25:fd: + 22:90:b5:de:bd:e6:f1:0c:ee:7e:09:71:0d:82:6a:ca:2f:9c: + 96:45:73:3a:65:bc:d8:9d:e0:61:01:5d:a8:de:de:61:8c:82: + 52:0c:ef:97:39:b3:13:c6:7d:d0:c0:f5:6d:c8:70:5b:96:e8: + 99:31:d8:75:3a:21:58:ab:01:21:9e:38:8e:53:ff:f8:48:a7: + af:01:9a:93 +-----BEGIN CERTIFICATE----- +MIIDDjCCAfagAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0xNjA0MTgxNjE0MjlaFw0yNjA0MTYxNjE0MjlaMBoxGDAWBgNVBAMM +D0ludGVybWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALZCAhMlQBOmBZlp2gzJqL+GO/zGUbpkZX4zETHVA0UwTMpJ0pZCUi/55myaUBz+ ++uLoYzYUR/dJn3goXh8LnZ740zN3Bk1tFMBXAYMr75kGSCHswdcFSCzqgwZqIN9z +zoql5IEAQYTPiYF4Ljq9G/0+lgiNRBsAyNZOemp1wJs84PqqOoJbPDkyykq6grxg +R2/kSv3coHKKG/7NLhD0J0wITtHt3Aiw+B/k/EVyQ1hu3QU3jASh+2T0P5C7hfJM +l0b9HynlGdAPJP3RAMW2vtqEYne+22f27Jhdl/XfCr24B38K1ZIpH8Swl0/kh9ep +AMlh1WzNavxWw/O3ylNwAj8CAwEAAaNmMGQwHQYDVR0OBBYEFKl1mc+cklSkS2XN +PfyTmI2eCR9HMB8GA1UdIwQYMBaAFONRh+PNerMmn4/sYtEOFQw5NkdPMBIGA1Ud +EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB +AQBVadYdM62rQEb9NALBQ1B7kOrzX0+2LCiqcuBLNi6PRJMVUhT2YbNQ4LpDkbqp +XaxDt1LKkaPXDqynnu4ofy0Pk7XZIzVoVCkq5zpMQSTQXi3zHrlS8T4WdpOJbaFM +Y/VKzAg2YSkKKV/0WlWYELPes5D5A+W9G2EBpyIDrg93xKi/MbSvyMfjJaEruUM3 +OwjqxEZguF/uKg3OGHVjujIohPRWlRvF+UZ+FC6DXqn/soDKJf0ikLXevebxDO5+ +CXENgmrKL5yWRXM6ZbzYneBhAV2o3t5hjIJSDO+XObMTxn3QwPVtyHBbluiZMdh1 +OiFYqwEhnjiOU//4SKevAZqT +-----END CERTIFICATE----- diff --git a/spec/fixtures/certificates/rootCA.pem b/spec/fixtures/certificates/rootCA.pem new file mode 100644 index 000000000000..5c77fe61cd0f --- /dev/null +++ b/spec/fixtures/certificates/rootCA.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgIJAOcWbv0WHll0MA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV +BAMMB1Jvb3QgQ0EwHhcNMTYwNDE4MTYxNDI5WhcNMjYwNDE2MTYxNDI5WjASMRAw +DgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +0iZvelv6MjxqdmQsAmKqIfc5gvQjYB3CqWlEH3g5czPFBMMtmOI9czlk+0jc1VEf +t1SKst7zwe1rpxFArgudV45NBHQH3ZlzkLeO7Ol2kPzlyMHNJ70vT3CBitKnLl4B +bg7xf6kDQQlC3/QeWxvbR5cvp131uwcpXKdJ9k4dwpfS2BKiRb5Uk46DgX5kGaka +q/tQ2F7b6AlAoTq608tZBuOInkg2tTbGe9PDWSL8oMZRwCSbF543SAR45zjWBa0k +ymY31VvlYbEd/3lfE5Mrn/JwZQpTKOfcOI//kUkcClJVpSMObh4eiy1oNjqcJ4KR +/4hkY7oTQCA7zWD34jQpkQIDAQABo2MwYTAdBgNVHQ4EFgQU41GH4816syafj+xi +0Q4VDDk2R08wHwYDVR0jBBgwFoAU41GH4816syafj+xi0Q4VDDk2R08wDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBADrt +1bSbOYxDmB1x9KaHNFVSzs0XsipsbZW/XTqtNMPMFXh+I57NVptVanCwcfU5zmOF +AjL+R8v0NkPU9+6nSM2PYqWxEavf0f6pkxIj+ThFwtVwXEKocPG9RFUvZNZv+rSH +yAnzuAzFI71EsT9VgJGHI0pgPjrGbSlNfb0OJFOlwtbGWGofmg+N6hHcx5nVKlgL +ZWLtYT+/mT2pSGuIpJtdnuUv0vcrRa4mxAa8NPF4+Qpi6yErkfogE+T4RYf2L4rp +CaRIFicLoNUmwK0nCerJaPFLwGkiNGNX81CHnw3+xLisSPvxze2ZRA0DhUWUGInq +grjWDMO9P1hPWu5jmbo= +-----END CERTIFICATE----- diff --git a/spec/fixtures/certificates/server.key b/spec/fixtures/certificates/server.key new file mode 100644 index 000000000000..719bfc8797df --- /dev/null +++ b/spec/fixtures/certificates/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuByk7Ib6anz0xOFRqyogGsTTEAAGduquIckVFajDHrk7vdq+ +TflkDp+UhbISqu1rj0OuaqODxiPSSw5fHClqNKpjhHS1ry86knS847BGwrByY0oa +zBdN0UgtWiXOK+iCuSI9p0KSIORJ3Y70pUZmm12EHRwb0tANv4ogYxjAxwnyqYgn +PnrDsoyCh/Cb4Fu8XucrnepYbInXi6yOdwTuivTx9qy7tlQzvYJ2LLEUIJdBtCUZ +dZkxky9CJUbfu5rm6PFvbLII5YCSlpXLxg9bumZCR1z9IXE6rLYcJIp3HIquR2cN +tAs9M8OHuR5V6vhUG51bP3aTkg3asJVdUe10dwIDAQABAoIBAQCJGINSwZv86blW +VbX7r+2iIUhNVMd7i3tJGzQBId7RpPswf49P/tIb9YaiG5y8/PgoAS0CqWn5hDkW +vMfj747vUqWyPzn/DjseTaFOJrg6RyuWddsIeJ3wpj9nLlmc5pFZDH8+alrn9TZv +rgDMhWTocjVre7/YNibWpyNAx3DdhG5DzNVLnu1R68d5k3JutQVqm01xCAV9ne9n +xE1RB5Z1xLvpQfW2qLYT0yFB7Xxw8awGyzVesPhGW1aa5F4urQjdCt2baa06Xolu +T3wXJ6wA9BuF2KOCi8DxELDaXoB//+82HafgWbOWIhJFOzEZaMNqZkfS/GbCgpEr +mE2r8zGBAoGBAOHNcUPgnIIeGdgFZZvL3Ge3Hp5mi2Vd2KBkAPNCjVSXeCu57yRC +SetlYuZlIhd7o+wdxUmWtg73DU19eDXJsOjXgNAoJfT9Zsyi4RClmJ0FRcSzvFU/ +m/TKrBbnFFAI+1pKwDnQ7envuRiTECFSsvKqdr8hddx0cPCgDtbe+75BAoGBANC7 +4ozkgsUTtdojz0DYBYBwUjN1gUETIhl91tt+48hmmEedROWDQDCT9gJfpAVFe1I6 +RyKKJnBcgNDJ7mqPUB1f5xb5rtaZS1owPNYTi3GrdVVg3lAf0j5ch8XoRJn/plnL +M0Sj5lLMviHJjyk8CPHbnE2k2vERAW4/SgzfA3S3AoGAHx55Jamm6CfN1/+maTpH +PeP2zE3FmEq+uBwQJXZek/HsFdqiIpUgKtjmMGpvsFzR0pCnx+SFYrqZkrxf/Mm3 +H9/TWNyvnnvt1vX7npez2LAJVXqP0g/aJnpoDR/7pKwYN/FlXJJ2t27aS5C5AF6t +WtQzWVP7Mk654e+tG9/PQgECgYEAiTCT7EpccK9NvLwAgfv5UbuBK3U1qNGsfdip +mMZDa/mSaK9DEx462DLHZDP8F8LdFORc0KTAMuV5fMDbxInA/C2GMyGT+lPypKpD +sehSpDku+xiZxUvE4VvrmPXZ8OWILkhRv/GBdjY/WPGi+FUPA/d1Ocr6Y6rrp8xN +HTyOhu0CgYBKxTSH6RCQsm8Q8uqmcP7cwe6fciTC0c2CRRqlzuXeseG72MBRDk/8 +P1jtOIlIsax/8NwNm5ReAiLgVn/h6/YgN4fpMkV1XIaH4j7HiGf5CWgOTWxS9jWA +cV09H22BaNkT0fZ71IlXQI11cVRodX0g4cJXeuyTxY9OkMd6cGs8+A== +-----END RSA PRIVATE KEY----- diff --git a/spec/fixtures/certificates/server.pem b/spec/fixtures/certificates/server.pem new file mode 100644 index 000000000000..117be807e528 --- /dev/null +++ b/spec/fixtures/certificates/server.pem @@ -0,0 +1,88 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4097 (0x1001) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Intermediate CA + Validity + Not Before: Apr 18 16:14:29 2016 GMT + Not After : Apr 16 16:14:29 2026 GMT + Subject: CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b8:1c:a4:ec:86:fa:6a:7c:f4:c4:e1:51:ab:2a: + 20:1a:c4:d3:10:00:06:76:ea:ae:21:c9:15:15:a8: + c3:1e:b9:3b:bd:da:be:4d:f9:64:0e:9f:94:85:b2: + 12:aa:ed:6b:8f:43:ae:6a:a3:83:c6:23:d2:4b:0e: + 5f:1c:29:6a:34:aa:63:84:74:b5:af:2f:3a:92:74: + bc:e3:b0:46:c2:b0:72:63:4a:1a:cc:17:4d:d1:48: + 2d:5a:25:ce:2b:e8:82:b9:22:3d:a7:42:92:20:e4: + 49:dd:8e:f4:a5:46:66:9b:5d:84:1d:1c:1b:d2:d0: + 0d:bf:8a:20:63:18:c0:c7:09:f2:a9:88:27:3e:7a: + c3:b2:8c:82:87:f0:9b:e0:5b:bc:5e:e7:2b:9d:ea: + 58:6c:89:d7:8b:ac:8e:77:04:ee:8a:f4:f1:f6:ac: + bb:b6:54:33:bd:82:76:2c:b1:14:20:97:41:b4:25: + 19:75:99:31:93:2f:42:25:46:df:bb:9a:e6:e8:f1: + 6f:6c:b2:08:e5:80:92:96:95:cb:c6:0f:5b:ba:66: + 42:47:5c:fd:21:71:3a:ac:b6:1c:24:8a:77:1c:8a: + ae:47:67:0d:b4:0b:3d:33:c3:87:b9:1e:55:ea:f8: + 54:1b:9d:5b:3f:76:93:92:0d:da:b0:95:5d:51:ed: + 74:77 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Cert Type: + SSL Server + Netscape Comment: + OpenSSL Generated Server Certificate + X509v3 Subject Key Identifier: + 1D:60:82:FA:3A:EC:27:91:BA:8D:F5:ED:B2:E3:85:0B:22:5A:8E:38 + X509v3 Authority Key Identifier: + keyid:A9:75:99:CF:9C:92:54:A4:4B:65:CD:3D:FC:93:98:8D:9E:09:1F:47 + DirName:/CN=Root CA + serial:10:01 + + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + Signature Algorithm: sha256WithRSAEncryption + 89:90:3d:2c:b8:0d:36:63:68:9a:cd:f9:14:56:94:d9:18:11: + b5:08:35:af:f9:34:cd:70:db:7d:66:06:e3:57:9b:06:8f:11: + d6:ea:ac:a6:07:db:ae:a2:c0:66:69:84:d8:2d:3c:cc:d7:4d: + 3c:75:60:4f:98:fc:56:df:30:39:c6:55:2c:73:92:9e:0c:b5: + 7c:75:40:5d:21:aa:01:c1:8a:03:86:eb:d7:02:7d:f5:7b:12: + cc:18:90:23:ad:8f:d7:05:18:6d:f0:11:a8:6b:27:fd:4c:07: + 07:53:f5:7f:f7:a2:e5:18:1e:4e:90:1b:10:5f:f3:5c:cb:c7: + 37:63:d0:d5:1d:3a:65:66:24:ee:0e:ce:7f:b1:fb:ee:17:d0: + b5:4d:64:2f:5a:9c:bc:7a:1c:c0:b4:0f:32:c9:a9:5c:cb:57: + 26:fd:49:39:8d:f2:89:54:c4:92:b5:35:ec:fe:cf:87:07:a6: + 84:01:98:00:e4:2a:44:26:b7:48:00:11:d3:e4:5a:c1:ad:46: + 36:53:f9:28:b7:e4:c5:bb:66:88:ab:8e:cc:30:d0:96:aa:3e: + c1:12:6a:8f:fa:6d:19:15:f4:90:66:54:62:84:97:06:2d:5c: + b9:18:71:90:f4:ca:4c:8c:a5:8b:32:14:93:89:f1:93:f4:00: + bd:1d:42:4f +-----BEGIN CERTIFICATE----- +MIIDgjCCAmqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPSW50 +ZXJtZWRpYXRlIENBMB4XDTE2MDQxODE2MTQyOVoXDTI2MDQxNjE2MTQyOVowFDES +MBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAuByk7Ib6anz0xOFRqyogGsTTEAAGduquIckVFajDHrk7vdq+TflkDp+UhbIS +qu1rj0OuaqODxiPSSw5fHClqNKpjhHS1ry86knS847BGwrByY0oazBdN0UgtWiXO +K+iCuSI9p0KSIORJ3Y70pUZmm12EHRwb0tANv4ogYxjAxwnyqYgnPnrDsoyCh/Cb +4Fu8XucrnepYbInXi6yOdwTuivTx9qy7tlQzvYJ2LLEUIJdBtCUZdZkxky9CJUbf +u5rm6PFvbLII5YCSlpXLxg9bumZCR1z9IXE6rLYcJIp3HIquR2cNtAs9M8OHuR5V +6vhUG51bP3aTkg3asJVdUe10dwIDAQABo4HXMIHUMAkGA1UdEwQCMAAwEQYJYIZI +AYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBT +ZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFB1ggvo67CeRuo317bLjhQsiWo44 +MDsGA1UdIwQ0MDKAFKl1mc+cklSkS2XNPfyTmI2eCR9HoRakFDASMRAwDgYDVQQD +DAdSb290IENBggIQATAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH +AwEwDQYJKoZIhvcNAQELBQADggEBAImQPSy4DTZjaJrN+RRWlNkYEbUINa/5NM1w +231mBuNXmwaPEdbqrKYH266iwGZphNgtPMzXTTx1YE+Y/FbfMDnGVSxzkp4MtXx1 +QF0hqgHBigOG69cCffV7EswYkCOtj9cFGG3wEahrJ/1MBwdT9X/3ouUYHk6QGxBf +81zLxzdj0NUdOmVmJO4Ozn+x++4X0LVNZC9anLx6HMC0DzLJqVzLVyb9STmN8olU +xJK1Nez+z4cHpoQBmADkKkQmt0gAEdPkWsGtRjZT+Si35MW7Zoirjsww0JaqPsES +ao/6bRkV9JBmVGKElwYtXLkYcZD0ykyMpYsyFJOJ8ZP0AL0dQk8= +-----END CERTIFICATE----- diff --git a/spec/fixtures/module/answer.js b/spec/fixtures/module/answer.js new file mode 100644 index 000000000000..d592d6cda214 --- /dev/null +++ b/spec/fixtures/module/answer.js @@ -0,0 +1,4 @@ +var ipcRenderer = require('electron').ipcRenderer +window.answer = function (answer) { + ipcRenderer.send('answer', answer) +} diff --git a/spec/fixtures/pages/web-view-log-process.html b/spec/fixtures/pages/web-view-log-process.html new file mode 100644 index 000000000000..9e75edb393e2 --- /dev/null +++ b/spec/fixtures/pages/web-view-log-process.html @@ -0,0 +1,13 @@ + + + + + test + + + + test? + + diff --git a/spec/fixtures/pages/webview-no-node-integration-on-window.html b/spec/fixtures/pages/webview-no-node-integration-on-window.html new file mode 100644 index 000000000000..0af03cf1c497 --- /dev/null +++ b/spec/fixtures/pages/webview-no-node-integration-on-window.html @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/spec/static/main.js b/spec/static/main.js index 84e9ba3da55d..025ff394bc66 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -7,8 +7,10 @@ const ipcMain = electron.ipcMain const dialog = electron.dialog const BrowserWindow = electron.BrowserWindow +const fs = require('fs') const path = require('path') const url = require('url') +const util = require('util') var argv = require('yargs') .boolean('ci') @@ -35,13 +37,18 @@ ipcMain.on('message', function (event, arg) { event.sender.send('message', arg) }) -ipcMain.on('console.log', function (event, args) { - console.error.apply(console, args) -}) - -ipcMain.on('console.error', function (event, args) { - console.error.apply(console, args) -}) +// Write output to file if OUTPUT_TO_FILE is defined. +const outputToFile = process.env.OUTPUT_TO_FILE +const print = function (_, args) { + let output = util.format.apply(null, args) + if (outputToFile) { + fs.appendFileSync(outputToFile, output + '\n') + } else { + console.error(output) + } +} +ipcMain.on('console.log', print) +ipcMain.on('console.error', print) ipcMain.on('process.exit', function (event, code) { process.exit(code) diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 10758a0a4365..d2c37c857df1 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -84,6 +84,39 @@ describe(' tag', function () { document.body.appendChild(webview) }) + it('disables node integration when disabled on the parent BrowserWindow', function (done) { + var b = undefined + + ipcMain.once('answer', function (event, typeofProcess) { + try { + assert.equal(typeofProcess, 'undefined') + done() + } finally { + b.close() + } + }) + + var windowUrl = require('url').format({ + pathname: `${fixtures}/pages/webview-no-node-integration-on-window.html`, + protocol: 'file', + query: { + p: `${fixtures}/pages/web-view-log-process.html` + }, + slashes: true + }) + var preload = path.join(fixtures, 'module', 'answer.js') + + b = new BrowserWindow({ + height: 400, + width: 400, + show: false, + webPreferences: { + preload: preload, + nodeIntegration: false, + } + }) + b.loadURL(windowUrl) + }) it('disables node integration on child windows when it is disabled on the webview', function (done) { app.once('browser-window-created', function (event, window) { @@ -722,11 +755,13 @@ describe(' tag', function () { }) describe('permission-request event', function () { - function setUpRequestHandler (webview, requested_permission) { + function setUpRequestHandler (webview, requested_permission, completed) { var listener = function (webContents, permission, callback) { if (webContents.getId() === webview.getId()) { assert.equal(permission, requested_permission) callback(false) + if (completed) + completed() } } session.fromPartition(webview.partition).setPermissionRequestHandler(listener) @@ -770,6 +805,13 @@ describe(' tag', function () { setUpRequestHandler(webview, 'midiSysex') document.body.appendChild(webview) }) + + it('emits when accessing external protocol', function (done) { + webview.src = 'magnet:test' + webview.partition = 'permissionTest' + setUpRequestHandler(webview, 'openExternal', done) + document.body.appendChild(webview) + }) }) describe('.getWebContents', function () { diff --git a/vendor/brightray b/vendor/brightray index 79b80e83f4a6..8dbaeed37b9c 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 79b80e83f4a64790e51ad2fc44b0a56bdb3d7ef3 +Subproject commit 8dbaeed37b9c4fb8ae985670b142f659bb265fb4 diff --git a/vendor/native_mate b/vendor/native_mate index 553326b00696..0df2d882ea22 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit 553326b00696fcda106a8866872a8f2ad6caff0d +Subproject commit 0df2d882ea2286e6335f206b7002037fce66c4a5