diff --git a/README.md b/README.md index 83ba1f5a618f..cf8d97a56098 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ forums - [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)* - [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)* - [`electron-jp`](https://electron-jp-slackin.herokuapp.com/) *(Japanese)* +- [`electron-tr`](http://www.meetup.com/Electron-JS-Istanbul/) *(Turkish)* Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index bc29b988eeea..a39f9809ebf0 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -463,10 +463,11 @@ void App::DisableHardwareAcceleration(mate::Arguments* args) { void App::ImportCertificate( const base::DictionaryValue& options, const net::CompletionCallback& callback) { - auto browser_context = AtomBrowserMainParts::Get()->browser_context(); + auto browser_context = brightray::BrowserContext::From("", false); if (!certificate_manager_model_) { std::unique_ptr copy = options.CreateDeepCopy(); - CertificateManagerModel::Create(browser_context, + CertificateManagerModel::Create( + browser_context.get(), base::Bind(&App::OnCertificateManagerModelCreated, base::Unretained(this), base::Passed(©), @@ -519,6 +520,8 @@ void App::BuildPrototype( base::Bind(&Browser::SetAsDefaultProtocolClient, browser)) .SetMethod("removeAsDefaultProtocolClient", base::Bind(&Browser::RemoveAsDefaultProtocolClient, browser)) + .SetMethod("setBadgeCount", base::Bind(&Browser::SetBadgeCount, browser)) + .SetMethod("getBadgeCount", base::Bind(&Browser::GetBadgeCount, browser)) #if defined(OS_MACOSX) .SetMethod("hide", base::Bind(&Browser::Hide, browser)) .SetMethod("show", base::Bind(&Browser::Show, browser)) @@ -528,8 +531,11 @@ void App::BuildPrototype( base::Bind(&Browser::GetCurrentActivityType, browser)) #endif #if defined(OS_WIN) - .SetMethod("setUserTasks", - base::Bind(&Browser::SetUserTasks, browser)) + .SetMethod("setUserTasks", base::Bind(&Browser::SetUserTasks, browser)) +#endif +#if defined(OS_LINUX) + .SetMethod("isUnityRunning", + base::Bind(&Browser::IsUnityRunning, browser)) #endif .SetMethod("setPath", &App::SetPath) .SetMethod("getPath", &App::GetPath) diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index 6ee15a6cba90..919a4bff6fe0 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -30,7 +30,7 @@ struct Converter { if (val == atom::api::Cookies::SUCCESS) return v8::Null(isolate); else - return v8::Exception::Error(StringToV8(isolate, "failed")); + return v8::Exception::Error(StringToV8(isolate, "Setting cookie failed")); } }; diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 996c71739bc4..c9cd37522b82 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -61,8 +61,10 @@ bool Menu::GetAcceleratorForCommandId(int command_id, return mate::ConvertFromV8(isolate(), val, accelerator); } -void Menu::ExecuteCommand(int command_id, int event_flags) { - execute_command_.Run(command_id); +void Menu::ExecuteCommand(int command_id, int flags) { + execute_command_.Run( + mate::internal::CreateEventFromFlags(isolate(), flags), + command_id); } void Menu::MenuWillShow(ui::SimpleMenuModel* source) { diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 9ba4d7a754c4..53c6bdaf4ec9 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -90,7 +90,7 @@ class Menu : public mate::TrackableObject, base::Callback is_enabled_; base::Callback is_visible_; base::Callback(int)> get_accelerator_; - base::Callback execute_command_; + base::Callback, int)> execute_command_; base::Callback menu_will_show_; DISALLOW_COPY_AND_ASSIGN(Menu); diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 602729b9e382..4a4ffba0c84a 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -41,8 +41,10 @@ #include "net/http/http_auth_preferences.h" #include "net/proxy/proxy_service.h" #include "net/proxy/proxy_config_service_fixed.h" +#include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#include "ui/base/l10n/l10n_util.h" using content::BrowserThread; using content::StoragePartition; @@ -93,6 +95,15 @@ uint32_t GetQuotaMask(const std::vector& quota_types) { return quota_mask; } +void SetUserAgentInIO(scoped_refptr getter, + const std::string& accept_lang, + const std::string& user_agent) { + getter->GetURLRequestContext()->set_http_user_agent_settings( + new net::StaticHttpUserAgentSettings( + net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang), + user_agent)); +} + } // namespace namespace mate { @@ -455,6 +466,23 @@ void Session::AllowNTLMCredentialsForDomains(const std::string& domains) { domains)); } +void Session::SetUserAgent(const std::string& user_agent, + mate::Arguments* args) { + browser_context_->SetUserAgent(user_agent); + + std::string accept_lang = l10n_util::GetApplicationLocale(""); + args->GetNext(&accept_lang); + + auto getter = browser_context_->GetRequestContext(); + getter->GetNetworkTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent)); +} + +std::string Session::GetUserAgent() { + return browser_context_->GetUserAgent(); +} + v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { auto handle = atom::api::Cookies::Create(isolate, browser_context()); @@ -520,6 +548,8 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache) .SetMethod("allowNTLMCredentialsForDomains", &Session::AllowNTLMCredentialsForDomains) + .SetMethod("setUserAgent", &Session::SetUserAgent) + .SetMethod("getUserAgent", &Session::GetUserAgent) .SetProperty("cookies", &Session::Cookies) .SetProperty("protocol", &Session::Protocol) .SetProperty("webRequest", &Session::WebRequest); diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index bb67fa106de1..0868cbe18bee 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -57,15 +57,7 @@ class Session: public mate::TrackableObject, static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); - protected: - Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); - ~Session(); - - // content::DownloadManager::Observer: - void OnDownloadCreated(content::DownloadManager* manager, - content::DownloadItem* item) override; - - private: + // Methods. void ResolveProxy(const GURL& url, ResolveProxyCallback callback); template void DoCacheAction(const net::CompletionCallback& callback); @@ -80,10 +72,21 @@ class Session: public mate::TrackableObject, mate::Arguments* args); void ClearHostResolverCache(mate::Arguments* args); void AllowNTLMCredentialsForDomains(const std::string& domains); + void SetUserAgent(const std::string& user_agent, mate::Arguments* args); + std::string GetUserAgent(); v8::Local Cookies(v8::Isolate* isolate); v8::Local Protocol(v8::Isolate* isolate); v8::Local WebRequest(v8::Isolate* isolate); + protected: + Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); + ~Session(); + + // content::DownloadManager::Observer: + void OnDownloadCreated(content::DownloadManager* manager, + content::DownloadItem* item) override; + + private: // Cached object. v8::Global cookies_; v8::Global protocol_; diff --git a/atom/browser/api/atom_api_system_preferences.cc b/atom/browser/api/atom_api_system_preferences.cc index 2b11aad25278..c3421365f0c8 100644 --- a/atom/browser/api/atom_api_system_preferences.cc +++ b/atom/browser/api/atom_api_system_preferences.cc @@ -53,6 +53,10 @@ void SystemPreferences::BuildPrototype( &SystemPreferences::SubscribeNotification) .SetMethod("unsubscribeNotification", &SystemPreferences::UnsubscribeNotification) + .SetMethod("subscribeLocalNotification", + &SystemPreferences::SubscribeLocalNotification) + .SetMethod("unsubscribeLocalNotification", + &SystemPreferences::UnsubscribeLocalNotification) .SetMethod("getUserDefault", &SystemPreferences::GetUserDefault) #endif .SetMethod("isDarkMode", &SystemPreferences::IsDarkMode); diff --git a/atom/browser/api/atom_api_system_preferences.h b/atom/browser/api/atom_api_system_preferences.h index 7779ce007816..9a5cf990357a 100644 --- a/atom/browser/api/atom_api_system_preferences.h +++ b/atom/browser/api/atom_api_system_preferences.h @@ -26,17 +26,18 @@ class SystemPreferences : public mate::EventEmitter { static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); -#if defined(OS_MACOSX) - using NotificationCallback = base::Callback< - void(const std::string&, const base::DictionaryValue&)>; -#endif - #if defined(OS_WIN) bool IsAeroGlassEnabled(); #elif defined(OS_MACOSX) + using NotificationCallback = base::Callback< + void(const std::string&, const base::DictionaryValue&)>; + int SubscribeNotification(const std::string& name, const NotificationCallback& callback); void UnsubscribeNotification(int id); + int SubscribeLocalNotification(const std::string& name, + const NotificationCallback& callback); + void UnsubscribeLocalNotification(int request_id); v8::Local GetUserDefault(const std::string& name, const std::string& type); #endif @@ -46,6 +47,13 @@ class SystemPreferences : public mate::EventEmitter { explicit SystemPreferences(v8::Isolate* isolate); ~SystemPreferences() override; +#if defined(OS_MACOSX) + int DoSubscribeNotification(const std::string& name, + const NotificationCallback& callback, + bool is_local); + void DoUnsubscribeNotification(int request_id, bool is_local); +#endif + private: DISALLOW_COPY_AND_ASSIGN(SystemPreferences); }; diff --git a/atom/browser/api/atom_api_system_preferences_mac.mm b/atom/browser/api/atom_api_system_preferences_mac.mm index 72011500bd04..6f7055ab9135 100644 --- a/atom/browser/api/atom_api_system_preferences_mac.mm +++ b/atom/browser/api/atom_api_system_preferences_mac.mm @@ -30,34 +30,59 @@ std::map g_id_map; int SystemPreferences::SubscribeNotification( const std::string& name, const NotificationCallback& callback) { + return DoSubscribeNotification(name, callback, false); +} + +void SystemPreferences::UnsubscribeNotification(int request_id) { + DoUnsubscribeNotification(request_id, false); +} + +int SystemPreferences::SubscribeLocalNotification( + const std::string& name, const NotificationCallback& callback) { + return DoSubscribeNotification(name, callback, true); +} + +void SystemPreferences::UnsubscribeLocalNotification(int request_id) { + DoUnsubscribeNotification(request_id, true); +} + +int SystemPreferences::DoSubscribeNotification(const std::string& name, + const NotificationCallback& callback, bool is_local) { int request_id = g_next_id++; __block NotificationCallback copied_callback = callback; - g_id_map[request_id] = [[NSDistributedNotificationCenter defaultCenter] - addObserverForName:base::SysUTF8ToNSString(name) - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - std::unique_ptr user_info = - NSDictionaryToDictionaryValue(notification.userInfo); - if (user_info) { - copied_callback.Run( - base::SysNSStringToUTF8(notification.name), - *user_info); - } else { - copied_callback.Run( - base::SysNSStringToUTF8(notification.name), - base::DictionaryValue()); - } + NSNotificationCenter* center = is_local ? + [NSNotificationCenter defaultCenter] : + [NSDistributedNotificationCenter defaultCenter]; + + g_id_map[request_id] = [center + addObserverForName:base::SysUTF8ToNSString(name) + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + std::unique_ptr user_info = + NSDictionaryToDictionaryValue(notification.userInfo); + if (user_info) { + copied_callback.Run( + base::SysNSStringToUTF8(notification.name), + *user_info); + } else { + copied_callback.Run( + base::SysNSStringToUTF8(notification.name), + base::DictionaryValue()); } + } ]; return request_id; } -void SystemPreferences::UnsubscribeNotification(int request_id) { +void SystemPreferences::DoUnsubscribeNotification(int request_id, bool is_local) { auto iter = g_id_map.find(request_id); if (iter != g_id_map.end()) { id observer = iter->second; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:observer]; + NSNotificationCenter* center = is_local ? + [NSNotificationCenter defaultCenter] : + [NSDistributedNotificationCenter defaultCenter]; + [center removeObserver:observer]; g_id_map.erase(iter); } } diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index c84e8a1d66b6..6bbad738b32d 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -16,7 +16,6 @@ #include "atom/common/node_includes.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" -#include "ui/events/event_constants.h" #include "ui/gfx/image/image.h" namespace atom { @@ -44,24 +43,15 @@ mate::WrappableBase* Tray::New(v8::Isolate* isolate, } void Tray::OnClicked(const gfx::Rect& bounds, int modifiers) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - EmitCustomEvent("click", - ModifiersToObject(isolate(), modifiers), bounds); + EmitWithFlags("click", modifiers, bounds); } void Tray::OnDoubleClicked(const gfx::Rect& bounds, int modifiers) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - EmitCustomEvent("double-click", - ModifiersToObject(isolate(), modifiers), bounds); + EmitWithFlags("double-click", modifiers, bounds); } void Tray::OnRightClicked(const gfx::Rect& bounds, int modifiers) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - EmitCustomEvent("right-click", - ModifiersToObject(isolate(), modifiers), bounds); + EmitWithFlags("right-click", modifiers, bounds); } void Tray::OnBalloonShow() { @@ -159,14 +149,8 @@ void Tray::SetContextMenu(v8::Isolate* isolate, mate::Handle menu) { tray_icon_->SetContextMenu(menu->model()); } -v8::Local Tray::ModifiersToObject(v8::Isolate* isolate, - int modifiers) { - mate::Dictionary obj(isolate, v8::Object::New(isolate)); - obj.Set("shiftKey", static_cast(modifiers & ui::EF_SHIFT_DOWN)); - obj.Set("ctrlKey", static_cast(modifiers & ui::EF_CONTROL_DOWN)); - obj.Set("altKey", static_cast(modifiers & ui::EF_ALT_DOWN)); - obj.Set("metaKey", static_cast(modifiers & ui::EF_COMMAND_DOWN)); - return obj.GetHandle(); +gfx::Rect Tray::GetBounds() { + return tray_icon_->GetBounds(); } // static @@ -181,7 +165,8 @@ void Tray::BuildPrototype(v8::Isolate* isolate, .SetMethod("setHighlightMode", &Tray::SetHighlightMode) .SetMethod("displayBalloon", &Tray::DisplayBalloon) .SetMethod("popUpContextMenu", &Tray::PopUpContextMenu) - .SetMethod("setContextMenu", &Tray::SetContextMenu); + .SetMethod("setContextMenu", &Tray::SetContextMenu) + .SetMethod("getBounds", &Tray::GetBounds); } } // namespace api diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index a6c329567ca3..4dd79c467f09 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -65,10 +65,9 @@ class Tray : public mate::TrackableObject, void DisplayBalloon(mate::Arguments* args, const mate::Dictionary& options); void PopUpContextMenu(mate::Arguments* args); void SetContextMenu(v8::Isolate* isolate, mate::Handle menu); + gfx::Rect GetBounds(); private: - v8::Local ModifiersToObject(v8::Isolate* isolate, int modifiers); - v8::Global menu_; std::unique_ptr tray_icon_; diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index bfedddce8a34..99cb2e007482 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -17,6 +17,7 @@ #include "atom/browser/lib/bluetooth_chooser.h" #include "atom/browser/native_window.h" #include "atom/browser/net/atom_network_delegate.h" +#include "atom/browser/ui/drag_util.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" #include "atom/browser/web_view_guest_delegate.h" @@ -61,11 +62,9 @@ #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" #include "net/http/http_response_headers.h" -#include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request_context.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebFindOptions.h" -#include "ui/base/l10n/l10n_util.h" #include "atom/common/node_includes.h" @@ -76,15 +75,6 @@ struct PrintSettings { bool print_background; }; -void SetUserAgentInIO(scoped_refptr getter, - std::string accept_lang, - std::string user_agent) { - getter->GetURLRequestContext()->set_http_user_agent_settings( - new net::StaticHttpUserAgentSettings( - net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang), - user_agent)); -} - } // namespace namespace mate { @@ -618,7 +608,10 @@ void WebContents::DidFailProvisionalLoad( bool was_ignored_by_handler) { bool is_main_frame = !render_frame_host->GetParent(); Emit("did-fail-provisional-load", code, description, url, is_main_frame); - Emit("did-fail-load", code, description, url, is_main_frame); + + // Do not emit "did-fail-load" for canceled requests. + if (code != net::ERR_ABORTED) + Emit("did-fail-load", code, description, url, is_main_frame); } void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host, @@ -811,7 +804,7 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { std::string user_agent; if (options.Get("userAgent", &user_agent)) - SetUserAgent(user_agent); + web_contents()->SetUserAgentOverride(user_agent); std::string extra_headers; if (options.Get("extraHeaders", &extra_headers)) @@ -898,14 +891,9 @@ bool WebContents::IsCrashed() const { return web_contents()->IsCrashed(); } -void WebContents::SetUserAgent(const std::string& user_agent) { +void WebContents::SetUserAgent(const std::string& user_agent, + mate::Arguments* args) { web_contents()->SetUserAgentOverride(user_agent); - scoped_refptr getter = - web_contents()->GetBrowserContext()->GetRequestContext(); - - auto accept_lang = l10n_util::GetApplicationLocale(""); - getter->GetNetworkTaskRunner()->PostTask(FROM_HERE, - base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent)); } std::string WebContents::GetUserAgent() { @@ -1194,15 +1182,14 @@ void WebContents::SendInputEvent(v8::Isolate* isolate, isolate, "Invalid event object"))); } -void WebContents::BeginFrameSubscription( - mate::Arguments* args) { - FrameSubscriber::FrameCaptureCallback callback; +void WebContents::BeginFrameSubscription(mate::Arguments* args) { bool only_dirty = false; + FrameSubscriber::FrameCaptureCallback callback; + args->GetNext(&only_dirty); if (!args->GetNext(&callback)) { - args->GetNext(&only_dirty); - if (!args->GetNext(&callback)) - args->ThrowTypeError("'callback' must be defined"); + args->ThrowError(); + return; } const auto view = web_contents()->GetRenderWidgetHostView(); @@ -1219,6 +1206,35 @@ void WebContents::EndFrameSubscription() { view->EndFrameSubscription(); } +void WebContents::StartDrag(const mate::Dictionary& item, + mate::Arguments* args) { + base::FilePath file; + std::vector files; + if (!item.Get("files", &files) && item.Get("file", &file)) { + files.push_back(file); + } + + mate::Handle icon; + if (!item.Get("icon", &icon) && !file.empty()) { + // TODO(zcbenz): Set default icon from file. + } + + // Error checking. + if (icon.IsEmpty()) { + args->ThrowError("icon must be set"); + return; + } + + // Start dragging. + if (!files.empty()) { + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + DragFileItems(files, icon->image(), web_contents()->GetNativeView()); + } else { + args->ThrowError("There is nothing to drag"); + } +} + void WebContents::OnCursorChange(const content::WebCursor& cursor) { content::WebCursor::CursorInfo info; cursor.GetCursorInfo(&info); @@ -1338,6 +1354,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("beginFrameSubscription", &WebContents::BeginFrameSubscription) .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) + .SetMethod("startDrag", &WebContents::StartDrag) .SetMethod("setSize", &WebContents::SetSize) .SetMethod("isGuest", &WebContents::IsGuest) .SetMethod("getType", &WebContents::GetType) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index dd91360c5fea..2917aa860a81 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -81,7 +81,7 @@ class WebContents : public mate::TrackableObject, void GoForward(); void GoToOffset(int offset); bool IsCrashed() const; - void SetUserAgent(const std::string& user_agent); + void SetUserAgent(const std::string& user_agent, mate::Arguments* args); std::string GetUserAgent(); void InsertCSS(const std::string& css); bool SavePage(const base::FilePath& full_file_path, @@ -142,6 +142,9 @@ class WebContents : public mate::TrackableObject, void BeginFrameSubscription(mate::Arguments* args); void EndFrameSubscription(); + // Dragging native items. + void StartDrag(const mate::Dictionary& item, mate::Arguments* args); + // Methods for creating . void SetSize(const SetSizeParams& params); bool IsGuest() const; diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index cc3749bd0143..ac5fb87c7535 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -106,9 +106,6 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { options, parent.IsEmpty() ? nullptr : parent->window_.get())); web_contents->SetOwnerWindow(window_.get()); - window_->InitFromOptions(options); - window_->AddObserver(this); - AttachAsUserData(window_.get()); #if defined(TOOLKIT_VIEWS) // Sets the window icon. @@ -116,6 +113,10 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { if (options.Get(options::kIcon, &icon)) SetIcon(icon); #endif + + window_->InitFromOptions(options); + window_->AddObserver(this); + AttachAsUserData(window_.get()); } Window::~Window() { @@ -572,6 +573,10 @@ void Window::SetIgnoreMouseEvents(bool ignore) { return window_->SetIgnoreMouseEvents(ignore); } +void Window::SetContentProtection(bool enable) { + return window_->SetContentProtection(enable); +} + void Window::SetFocusable(bool focusable) { return window_->SetFocusable(focusable); } @@ -833,6 +838,7 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("setDocumentEdited", &Window::SetDocumentEdited) .SetMethod("isDocumentEdited", &Window::IsDocumentEdited) .SetMethod("setIgnoreMouseEvents", &Window::SetIgnoreMouseEvents) + .SetMethod("setContentProtection", &Window::SetContentProtection) .SetMethod("setFocusable", &Window::SetFocusable) .SetMethod("focusOnWebView", &Window::FocusOnWebView) .SetMethod("blurWebView", &Window::BlurWebView) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 21ecca4c7292..a204b70e296c 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -153,6 +153,7 @@ class Window : public mate::TrackableObject, void SetDocumentEdited(bool edited); bool IsDocumentEdited(); void SetIgnoreMouseEvents(bool ignore); + void SetContentProtection(bool enable); void SetFocusable(bool focusable); void CapturePage(mate::Arguments* args); void SetProgressBar(double progress); diff --git a/atom/browser/api/event_emitter.cc b/atom/browser/api/event_emitter.cc index 7e392fddee34..e98008b85d49 100644 --- a/atom/browser/api/event_emitter.cc +++ b/atom/browser/api/event_emitter.cc @@ -8,6 +8,7 @@ #include "native_mate/arguments.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" +#include "ui/events/event_constants.h" namespace mate { @@ -65,6 +66,15 @@ v8::Local CreateCustomEvent( return event; } +v8::Local CreateEventFromFlags(v8::Isolate* isolate, int flags) { + mate::Dictionary obj = mate::Dictionary::CreateEmpty(isolate); + obj.Set("shiftKey", static_cast(flags & ui::EF_SHIFT_DOWN)); + obj.Set("ctrlKey", static_cast(flags & ui::EF_CONTROL_DOWN)); + obj.Set("altKey", static_cast(flags & ui::EF_ALT_DOWN)); + obj.Set("metaKey", static_cast(flags & ui::EF_COMMAND_DOWN)); + return obj.GetHandle(); +} + } // namespace internal } // namespace mate diff --git a/atom/browser/api/event_emitter.h b/atom/browser/api/event_emitter.h index 99f6ed46e48f..ead3beddaac5 100644 --- a/atom/browser/api/event_emitter.h +++ b/atom/browser/api/event_emitter.h @@ -30,6 +30,7 @@ v8::Local CreateCustomEvent( v8::Isolate* isolate, v8::Local object, v8::Local event); +v8::Local CreateEventFromFlags(v8::Isolate* isolate, int flags); } // namespace internal @@ -54,6 +55,16 @@ class EventEmitter : public Wrappable { internal::CreateCustomEvent(isolate(), GetWrapper(), event), args...); } + // this.emit(name, new Event(flags), args...); + template + bool EmitWithFlags(const base::StringPiece& name, + int flags, + const Args&... args) { + return EmitCustomEvent( + name, + internal::CreateEventFromFlags(isolate(), flags), args...); + } + // this.emit(name, new Event(), args...); template bool Emit(const base::StringPiece& name, const Args&... args) { diff --git a/atom/browser/api/frame_subscriber.cc b/atom/browser/api/frame_subscriber.cc index 7d87c6f0fcbb..456a535d608b 100644 --- a/atom/browser/api/frame_subscriber.cc +++ b/atom/browser/api/frame_subscriber.cc @@ -5,8 +5,8 @@ #include "atom/browser/api/frame_subscriber.h" #include "base/bind.h" -#include "atom/common/node_includes.h" #include "atom/common/native_mate_converters/gfx_converter.h" +#include "atom/common/node_includes.h" #include "content/public/browser/render_widget_host.h" #include @@ -31,8 +31,11 @@ FrameSubscriber::FrameSubscriber(v8::Isolate* isolate, content::RenderWidgetHostView* view, const FrameCaptureCallback& callback, bool only_dirty) - : isolate_(isolate), view_(view), callback_(callback), - only_dirty_(only_dirty), weak_factory_(this) { + : isolate_(isolate), + view_(view), + callback_(callback), + only_dirty_(only_dirty), + weak_factory_(this) { } bool FrameSubscriber::ShouldCaptureFrame( @@ -87,8 +90,9 @@ void FrameSubscriber::ReadbackResultAsBitmap( } void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, - const gfx::Rect& damage_rect, const SkBitmap& bitmap, - content::ReadbackResponse response) { + const gfx::Rect& damage_rect, + const SkBitmap& bitmap, + content::ReadbackResponse response) { if (response != content::ReadbackResponse::READBACK_SUCCESS) return; @@ -106,7 +110,7 @@ void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, rgb_arr_size); v8::Local damage = - mate::Converter::ToV8(isolate_, damage_rect); + mate::Converter::ToV8(isolate_, damage_rect); callback_.Run(buffer.ToLocalChecked(), damage); } diff --git a/atom/browser/api/frame_subscriber.h b/atom/browser/api/frame_subscriber.h index 6f2f7fc6f40a..6639f2efe70f 100644 --- a/atom/browser/api/frame_subscriber.h +++ b/atom/browser/api/frame_subscriber.h @@ -35,7 +35,7 @@ class FrameSubscriberRenderWidgetHostView class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { public: using FrameCaptureCallback = - base::Callback, v8::Local)>; + base::Callback, v8::Local)>; FrameSubscriber(v8::Isolate* isolate, content::RenderWidgetHostView* view, @@ -52,8 +52,9 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { std::unique_ptr result); void OnFrameDelivered(const FrameCaptureCallback& callback, - const gfx::Rect& damage_rect, const SkBitmap& bitmap, - content::ReadbackResponse response); + const gfx::Rect& damage_rect, + const SkBitmap& bitmap, + content::ReadbackResponse response); v8::Isolate* isolate_; content::RenderWidgetHostView* view_; diff --git a/atom/browser/atom_access_token_store.cc b/atom/browser/atom_access_token_store.cc index ed87ea979920..9f57148120c8 100644 --- a/atom/browser/atom_access_token_store.cc +++ b/atom/browser/atom_access_token_store.cc @@ -7,8 +7,8 @@ #include #include "atom/browser/atom_browser_context.h" -#include "atom/browser/atom_browser_main_parts.h" #include "atom/common/google_api_key.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/geolocation_provider.h" namespace atom { @@ -25,6 +25,7 @@ const char* kGeolocationProviderURL = } // namespace AtomAccessTokenStore::AtomAccessTokenStore() { + LOG(ERROR) << "AtomAccessTokenStore"; content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices(); } @@ -33,21 +34,35 @@ AtomAccessTokenStore::~AtomAccessTokenStore() { void AtomAccessTokenStore::LoadAccessTokens( const LoadAccessTokensCallback& callback) { - AccessTokenMap access_token_map; - - // Equivelent to access_token_map[kGeolocationProviderURL]. - // Somehow base::string16 is causing compilation errors when used in a pair - // of std::map on Linux, this can work around it. - std::pair token_pair; - token_pair.first = GURL(kGeolocationProviderURL); - access_token_map.insert(token_pair); - - auto browser_context = AtomBrowserMainParts::Get()->browser_context(); - callback.Run(access_token_map, browser_context->url_request_context_getter()); + content::BrowserThread::PostTaskAndReply( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&AtomAccessTokenStore::GetRequestContextOnUIThread, this), + base::Bind(&AtomAccessTokenStore::RespondOnOriginatingThread, + this, callback)); } void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url, const base::string16& access_token) { } +void AtomAccessTokenStore::GetRequestContextOnUIThread() { + auto browser_context = brightray::BrowserContext::From("", false); + request_context_getter_ = browser_context->GetRequestContext(); +} + +void AtomAccessTokenStore::RespondOnOriginatingThread( + const LoadAccessTokensCallback& callback) { + // Equivelent to access_token_map[kGeolocationProviderURL]. + // Somehow base::string16 is causing compilation errors when used in a pair + // of std::map on Linux, this can work around it. + AccessTokenMap access_token_map; + std::pair token_pair; + token_pair.first = GURL(kGeolocationProviderURL); + access_token_map.insert(token_pair); + + callback.Run(access_token_map, request_context_getter_.get()); + request_context_getter_ = nullptr; +} + } // namespace atom diff --git a/atom/browser/atom_access_token_store.h b/atom/browser/atom_access_token_store.h index 54bc2cee3c29..27c1911a65fa 100644 --- a/atom/browser/atom_access_token_store.h +++ b/atom/browser/atom_access_token_store.h @@ -9,12 +9,10 @@ namespace atom { -class AtomBrowserContext; - class AtomAccessTokenStore : public content::AccessTokenStore { public: AtomAccessTokenStore(); - virtual ~AtomAccessTokenStore(); + ~AtomAccessTokenStore(); // content::AccessTokenStore: void LoadAccessTokens( @@ -23,6 +21,11 @@ class AtomAccessTokenStore : public content::AccessTokenStore { const base::string16& access_token) override; private: + void GetRequestContextOnUIThread(); + void RespondOnOriginatingThread(const LoadAccessTokensCallback& callback); + + scoped_refptr request_context_getter_; + DISALLOW_COPY_AND_ASSIGN(AtomAccessTokenStore); }; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index c1b50459c680..add3154642dc 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -287,20 +287,21 @@ brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( void AtomBrowserClient::WebNotificationAllowed( int render_process_id, - const base::Callback& callback) { + const base::Callback& callback) { content::WebContents* web_contents = WebContentsPreferences::GetWebContentsFromProcessID(render_process_id); if (!web_contents) { - callback.Run(false); + callback.Run(false, false); return; } auto permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); if (!permission_helper) { - callback.Run(false); + callback.Run(false, false); return; } - permission_helper->RequestWebNotificationPermission(callback); + permission_helper->RequestWebNotificationPermission( + base::Bind(callback, web_contents->IsAudioMuted())); } void AtomBrowserClient::RenderProcessHostDestroyed( diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index cf1a4cc438b6..a61706534aa8 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -100,7 +100,7 @@ class AtomBrowserClient : public brightray::BrowserClient, const content::MainFunctionParams&) override; void WebNotificationAllowed( int render_process_id, - const base::Callback& callback) override; + const base::Callback& callback) override; // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 8268a7778193..7b682543a09c 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -68,16 +68,7 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, : brightray::BrowserContext(partition, in_memory), cert_verifier_(new AtomCertVerifier), network_delegate_(new AtomNetworkDelegate) { -} - -AtomBrowserContext::~AtomBrowserContext() { -} - -net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { - return network_delegate_; -} - -std::string AtomBrowserContext::GetUserAgent() { + // Construct user agent string. Browser* browser = Browser::Get(); std::string name = RemoveWhitespace(browser->GetName()); std::string user_agent; @@ -91,7 +82,22 @@ std::string AtomBrowserContext::GetUserAgent() { browser->GetVersion().c_str(), CHROME_VERSION_STRING); } - return content::BuildUserAgentFromProduct(user_agent); + user_agent_ = content::BuildUserAgentFromProduct(user_agent); +} + +AtomBrowserContext::~AtomBrowserContext() { +} + +void AtomBrowserContext::SetUserAgent(const std::string& user_agent) { + user_agent_ = user_agent; +} + +net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { + return network_delegate_; +} + +std::string AtomBrowserContext::GetUserAgent() { + return user_agent_; } std::unique_ptr diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index c9b1de947fba..f793bc9870d6 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -22,6 +22,8 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomBrowserContext(const std::string& partition, bool in_memory); ~AtomBrowserContext() override; + void SetUserAgent(const std::string& user_agent); + // brightray::URLRequestContextGetter::Delegate: net::NetworkDelegate* CreateNetworkDelegate() override; std::string GetUserAgent() override; @@ -47,6 +49,7 @@ class AtomBrowserContext : public brightray::BrowserContext { std::unique_ptr download_manager_delegate_; std::unique_ptr guest_manager_; std::unique_ptr permission_manager_; + std::string user_agent_; // Managed by brightray::BrowserContext. AtomCertVerifier* cert_verifier_; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 75f71d42314f..b933d1073bb6 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -124,6 +124,8 @@ void AtomBrowserMainParts::PostEarlyInitialization() { } void AtomBrowserMainParts::PreMainMessageLoopRun() { + js_env_->OnMessageLoopCreated(); + // Run user's main script before most things get initialized, so we can have // a chance to setup everything. node_bindings_->PrepareMessageLoop(); @@ -169,6 +171,8 @@ void AtomBrowserMainParts::PostMainMessageLoopStart() { void AtomBrowserMainParts::PostMainMessageLoopRun() { brightray::BrowserMainParts::PostMainMessageLoopRun(); + js_env_->OnMessageLoopDestroying(); + #if defined(OS_MACOSX) FreeAppDelegate(); #endif diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 8c00c1287376..b7d5626a82e3 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -118,6 +118,10 @@ void Browser::SetName(const std::string& name) { name_override_ = name; } +int Browser::GetBadgeCount() { + return badge_count_; +} + bool Browser::OpenFile(const std::string& file_path) { bool prevent_default = false; FOR_EACH_OBSERVER(BrowserObserver, diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 2c1678083034..37d2e11e6799 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -87,6 +87,10 @@ class Browser : public WindowListObserver { // Query the current state of default handler for a protocol. bool IsDefaultProtocolClient(const std::string& protocol); + // Set/Get the badge count. + bool SetBadgeCount(int count); + int GetBadgeCount(); + #if defined(OS_MACOSX) // Hide the application. void Hide(); @@ -149,7 +153,12 @@ class Browser : public WindowListObserver { // one from app's name. // The returned string managed by Browser, and should not be modified. PCWSTR GetAppUserModelID(); -#endif +#endif // defined(OS_WIN) + +#if defined(OS_LINUX) + // Whether Unity launcher is running. + bool IsUnityRunning(); +#endif // defined(OS_LINUX) // Tell the application to open a file. bool OpenFile(const std::string& file_path); @@ -216,6 +225,8 @@ class Browser : public WindowListObserver { std::string version_override_; std::string name_override_; + int badge_count_ = 0; + #if defined(OS_WIN) base::string16 app_user_model_id_; #endif diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index d994bb4109bb..fe1a0af9ba3a 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -10,6 +10,7 @@ #include "atom/browser/window_list.h" #include "atom/common/atom_version.h" #include "brightray/common/application_info.h" +#include "chrome/browser/ui/libgtk2ui/unity_service.h" namespace atom { @@ -46,6 +47,16 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { return false; } +bool Browser::SetBadgeCount(int count) { + if (IsUnityRunning()) { + unity::SetDownloadCount(count); + badge_count_ = count; + return true; + } else { + return false; + } +} + std::string Browser::GetExecutableFileVersion() const { return brightray::GetApplicationVersion(); } @@ -54,4 +65,8 @@ std::string Browser::GetExecutableFileProductName() const { return brightray::GetApplicationName(); } +bool Browser::IsUnityRunning() { + return unity::IsRunning(); +} + } // namespace atom diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index a50de2a07f60..bb789365ffb2 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -11,6 +11,7 @@ #include "atom/browser/window_list.h" #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/sys_string_conversions.h" #include "brightray/common/application_info.h" #include "net/base/mac/url_conversions.h" @@ -114,6 +115,12 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { void Browser::SetAppUserModelID(const base::string16& name) { } +bool Browser::SetBadgeCount(int count) { + DockSetBadgeText(count != 0 ? base::IntToString(count) : ""); + badge_count_ = count; + return true; +} + void Browser::SetUserActivity(const std::string& type, const base::DictionaryValue& user_info, mate::Arguments* args) { diff --git a/atom/browser/browser_win.cc b/atom/browser/browser_win.cc index 345e2bcf67ce..5eb9275dcff6 100644 --- a/atom/browser/browser_win.cc +++ b/atom/browser/browser_win.cc @@ -269,6 +269,10 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { } } +bool Browser::SetBadgeCount(int count) { + return false; +} + PCWSTR Browser::GetAppUserModelID() { if (app_user_model_id_.empty()) { SetAppUserModelID(base::ReplaceStringPlaceholders( diff --git a/atom/browser/javascript_environment.cc b/atom/browser/javascript_environment.cc index 970132b47c15..0f87a2ca32fd 100644 --- a/atom/browser/javascript_environment.cc +++ b/atom/browser/javascript_environment.cc @@ -7,6 +7,7 @@ #include #include "base/command_line.h" +#include "base/message_loop/message_loop.h" #include "content/public/common/content_switches.h" #include "gin/array_buffer.h" #include "gin/v8_initializer.h" @@ -23,6 +24,14 @@ JavascriptEnvironment::JavascriptEnvironment() context_scope_(v8::Local::New(isolate_, context_)) { } +void JavascriptEnvironment::OnMessageLoopCreated() { + isolate_holder_.AddRunMicrotasksObserver(); +} + +void JavascriptEnvironment::OnMessageLoopDestroying() { + isolate_holder_.RemoveRunMicrotasksObserver(); +} + bool JavascriptEnvironment::Initialize() { auto cmd = base::CommandLine::ForCurrentProcess(); if (cmd->HasSwitch("debug-brk")) { diff --git a/atom/browser/javascript_environment.h b/atom/browser/javascript_environment.h index 07cd602cf00d..1f4d2f453478 100644 --- a/atom/browser/javascript_environment.h +++ b/atom/browser/javascript_environment.h @@ -14,6 +14,9 @@ class JavascriptEnvironment { public: JavascriptEnvironment(); + void OnMessageLoopCreated(); + void OnMessageLoopDestroying(); + v8::Isolate* isolate() const { return isolate_; } v8::Local context() const { return v8::Local::New(isolate_, context_); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index d3b6f8a2b0be..8656c02ca939 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -157,6 +157,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetDocumentEdited(bool edited); virtual bool IsDocumentEdited(); virtual void SetIgnoreMouseEvents(bool ignore) = 0; + virtual void SetContentProtection(bool enable) = 0; virtual void SetFocusable(bool focusable); virtual void SetMenu(ui::MenuModel* menu); virtual bool HasModalDialog(); diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index af99b3912e16..d07d586a8439 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -79,6 +79,7 @@ class NativeWindowMac : public NativeWindow { void SetDocumentEdited(bool edited) override; bool IsDocumentEdited() override; void SetIgnoreMouseEvents(bool ignore) override; + void SetContentProtection(bool enable) override; bool HasModalDialog() override; void SetParentWindow(NativeWindow* parent) override; gfx::NativeWindow GetNativeWindow() override; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 5f997ee9b6b9..1ef003ab2aa9 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -70,6 +70,7 @@ bool ScopedDisableResize::disable_resize_ = false; @interface AtomNSWindowDelegate : NSObject { @private atom::NativeWindowMac* shell_; + bool is_zooming_; } - (id)initWithShell:(atom::NativeWindowMac*)shell; @end @@ -79,6 +80,7 @@ bool ScopedDisableResize::disable_resize_ = false; - (id)initWithShell:(atom::NativeWindowMac*)shell { if ((self = [super init])) { shell_ = shell; + is_zooming_ = false; } return self; } @@ -172,16 +174,20 @@ bool ScopedDisableResize::disable_resize_ = false; } - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame { - // Cocoa doen't have concept of maximize/unmaximize, so wee need to emulate - // them by calculating size change when zooming. - if (newFrame.size.width < [window frame].size.width || - newFrame.size.height < [window frame].size.height) - shell_->NotifyWindowUnmaximize(); - else - shell_->NotifyWindowMaximize(); + is_zooming_ = true; return YES; } +- (void)windowDidEndLiveResize:(NSNotification*)notification { + if (is_zooming_) { + if (shell_->IsMaximized()) + shell_->NotifyWindowMaximize(); + else + shell_->NotifyWindowUnmaximize(); + is_zooming_ = false; + } +} + - (void)windowWillEnterFullScreen:(NSNotification*)notification { // Hide the native toolbar before entering fullscreen, so there is no visual // artifacts. @@ -199,6 +205,7 @@ bool ScopedDisableResize::disable_resize_ = false; // have to set one, because title bar is visible here. NSWindow* window = shell_->GetNativeWindow(); if ((shell_->transparent() || !shell_->has_frame()) && + base::mac::IsOSYosemiteOrLater() && // FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under // fullscreen mode. shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) { @@ -223,6 +230,7 @@ bool ScopedDisableResize::disable_resize_ = false; // Restore the titlebar visibility. NSWindow* window = shell_->GetNativeWindow(); if ((shell_->transparent() || !shell_->has_frame()) && + base::mac::IsOSYosemiteOrLater() && shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) { [window setTitleVisibility:NSWindowTitleHidden]; } @@ -526,8 +534,10 @@ NativeWindowMac::NativeWindowMac( [window_ setDisableKeyOrMainWindow:YES]; if (transparent() || !has_frame()) { - // Don't show title bar. - [window_ setTitleVisibility:NSWindowTitleHidden]; + if (base::mac::IsOSYosemiteOrLater()) { + // Don't show title bar. + [window_ setTitleVisibility:NSWindowTitleHidden]; + } // Remove non-transparent corners, see http://git.io/vfonD. [window_ setOpaque:NO]; } @@ -852,6 +862,11 @@ void NativeWindowMac::Center() { } void NativeWindowMac::SetTitle(const std::string& title) { + // For macOS <= 10.9, the setTitleVisibility API is not available, we have + // to avoid calling setTitle for frameless window. + if (!base::mac::IsOSYosemiteOrLater() && (transparent() || !has_frame())) + return; + [window_ setTitle:base::SysUTF8ToNSString(title)]; } @@ -935,6 +950,11 @@ void NativeWindowMac::SetIgnoreMouseEvents(bool ignore) { [window_ setIgnoresMouseEvents:ignore]; } +void NativeWindowMac::SetContentProtection(bool enable) { + [window_ setSharingType:enable ? NSWindowSharingNone + : NSWindowSharingReadOnly]; +} + bool NativeWindowMac::HasModalDialog() { return [window_ attachedSheet] != nil; } diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 568a21cb158c..2edee730d652 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -752,6 +752,13 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore) { #endif } +void NativeWindowViews::SetContentProtection(bool enable) { +#if defined(OS_WIN) + DWORD affinity = enable ? WDA_MONITOR : WDA_NONE; + ::SetWindowDisplayAffinity(GetAcceleratedWidget(), affinity); +#endif +} + void NativeWindowViews::SetFocusable(bool focusable) { #if defined(OS_WIN) LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 921cb1182436..a3bea9b97acb 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -97,6 +97,7 @@ class NativeWindowViews : public NativeWindow, void SetHasShadow(bool has_shadow) override; bool HasShadow() override; void SetIgnoreMouseEvents(bool ignore) override; + void SetContentProtection(bool enable) override; void SetFocusable(bool focusable) override; void SetMenu(ui::MenuModel* menu_model) override; void SetParentWindow(NativeWindow* parent) override; diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 46898637c510..2f346b685508 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.2.3 + 1.2.6 CFBundleShortVersionString - 1.2.3 + 1.2.6 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index b6df17ca1d41..50ed8a37d6d6 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 1,2,3,0 - PRODUCTVERSION 1,2,3,0 + FILEVERSION 1,2,6,0 + PRODUCTVERSION 1,2,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.2.3" + VALUE "FileVersion", "1.2.6" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "1.2.3" + VALUE "ProductVersion", "1.2.6" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/accelerator_util_mac.mm b/atom/browser/ui/accelerator_util_mac.mm index e2448833ab0d..cf1d3de4d212 100644 --- a/atom/browser/ui/accelerator_util_mac.mm +++ b/atom/browser/ui/accelerator_util_mac.mm @@ -29,6 +29,10 @@ void SetPlatformAccelerator(ui::Accelerator* accelerator) { modifiers ^= NSShiftKeyMask; } + if (character == NSDeleteFunctionKey) { + character = NSDeleteCharacter; + } + NSString* characters = [[[NSString alloc] initWithCharacters:&character length:1] autorelease]; diff --git a/atom/browser/ui/atom_menu_model.cc b/atom/browser/ui/atom_menu_model.cc index 9add7a22715e..369684dd3f21 100644 --- a/atom/browser/ui/atom_menu_model.cc +++ b/atom/browser/ui/atom_menu_model.cc @@ -17,12 +17,14 @@ AtomMenuModel::~AtomMenuModel() { } void AtomMenuModel::SetRole(int index, const base::string16& role) { - roles_[index] = role; + int command_id = GetCommandIdAt(index); + roles_[command_id] = role; } base::string16 AtomMenuModel::GetRoleAt(int index) { - if (ContainsKey(roles_, index)) - return roles_[index]; + int command_id = GetCommandIdAt(index); + if (ContainsKey(roles_, command_id)) + return roles_[command_id]; else return base::string16(); } diff --git a/atom/browser/ui/atom_menu_model.h b/atom/browser/ui/atom_menu_model.h index d091df9fb570..1112949e7efa 100644 --- a/atom/browser/ui/atom_menu_model.h +++ b/atom/browser/ui/atom_menu_model.h @@ -42,7 +42,7 @@ class AtomMenuModel : public ui::SimpleMenuModel { private: Delegate* delegate_; // weak ref. - std::map roles_; + std::map roles_; // command id -> role base::ObserverList observers_; DISALLOW_COPY_AND_ASSIGN(AtomMenuModel); diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index 642721e6bd35..536f48760589 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -38,6 +38,8 @@ Role kRolesMap[] = { { @selector(performMiniaturize:), "minimize" }, { @selector(performClose:), "close" }, { @selector(performZoom:), "zoom" }, + { @selector(terminate:), "quit" }, + { @selector(toggleFullScreen:), "togglefullscreen" }, }; } // namespace diff --git a/atom/browser/ui/drag_util.h b/atom/browser/ui/drag_util.h new file mode 100644 index 000000000000..df5303c65e45 --- /dev/null +++ b/atom/browser/ui/drag_util.h @@ -0,0 +1,24 @@ +// 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_UI_DRAG_UTIL_H_ +#define ATOM_BROWSER_UI_DRAG_UTIL_H_ + +#include + +#include "ui/gfx/image/image.h" + +namespace base { +class FilePath; +} + +namespace atom { + +void DragFileItems(const std::vector& files, + const gfx::Image& icon, + gfx::NativeView view); + +} // namespace atom + +#endif // ATOM_BROWSER_UI_DRAG_UTIL_H_ diff --git a/atom/browser/ui/drag_util_mac.mm b/atom/browser/ui/drag_util_mac.mm new file mode 100644 index 000000000000..7197e79af68c --- /dev/null +++ b/atom/browser/ui/drag_util_mac.mm @@ -0,0 +1,58 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#import + +#include "atom/browser/ui/drag_util.h" +#include "base/files/file_path.h" +#include "base/strings/sys_string_conversions.h" + +namespace atom { + +namespace { + +// Write information about the file being dragged to the pasteboard. +void AddFilesToPasteboard(NSPasteboard* pasteboard, + const std::vector& files) { + NSMutableArray* fileList = [NSMutableArray array]; + for (const base::FilePath& file : files) + [fileList addObject:base::SysUTF8ToNSString(file.value())]; + [pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] + owner:nil]; + [pasteboard setPropertyList:fileList forType:NSFilenamesPboardType]; +} + +} // namespace + +void DragFileItems(const std::vector& files, + const gfx::Image& icon, + gfx::NativeView view) { + NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + AddFilesToPasteboard(pasteboard, files); + + // Synthesize a drag event, since we don't have access to the actual event + // that initiated a drag (possibly consumed by the Web UI, for example). + NSPoint position = [[view window] mouseLocationOutsideOfEventStream]; + NSTimeInterval eventTime = [[NSApp currentEvent] timestamp]; + NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged + location:position + modifierFlags:NSLeftMouseDraggedMask + timestamp:eventTime + windowNumber:[[view window] windowNumber] + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; + + // Run the drag operation. + [[view window] dragImage:icon.ToNSImage() + at:position + offset:NSZeroSize + event:dragEvent + pasteboard:pasteboard + source:view + slideBack:YES]; +} + +} // namespace atom diff --git a/atom/browser/ui/drag_util_views.cc b/atom/browser/ui/drag_util_views.cc new file mode 100644 index 000000000000..c69e34326c94 --- /dev/null +++ b/atom/browser/ui/drag_util_views.cc @@ -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. + +#include "atom/browser/ui/drag_util.h" + +#include "ui/aura/window.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drag_utils.h" +#include "ui/base/dragdrop/file_info.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/screen.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/public/drag_drop_client.h" + +namespace atom { + +void DragFileItems(const std::vector& files, + const gfx::Image& icon, + gfx::NativeView view) { + // Set up our OLE machinery + ui::OSExchangeData data; + + drag_utils::CreateDragImageForFile(files[0], icon.AsImageSkia(), &data); + + std::vector file_infos; + for (const base::FilePath& file : files) { + file_infos.push_back(ui::FileInfo(file, base::FilePath())); + } + data.SetFilenames(file_infos); + + aura::Window* root_window = view->GetRootWindow(); + if (!root_window || !aura::client::GetDragDropClient(root_window)) + return; + + gfx::Point location = gfx::Screen::GetScreen()->GetCursorScreenPoint(); + // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below. + aura::client::GetDragDropClient(root_window)->StartDragAndDrop( + data, + root_window, + view, + location, + ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK, + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); +} + +} // namespace atom diff --git a/atom/browser/ui/file_dialog_gtk.cc b/atom/browser/ui/file_dialog_gtk.cc index 1ad7ff5f2d41..eba1813780ad 100644 --- a/atom/browser/ui/file_dialog_gtk.cc +++ b/atom/browser/ui/file_dialog_gtk.cc @@ -4,7 +4,7 @@ #include "atom/browser/ui/file_dialog.h" -#include "atom/browser/native_window.h" +#include "atom/browser/native_window_views.h" #include "base/callback.h" #include "base/files/file_util.h" #include "base/strings/string_util.h" @@ -40,7 +40,8 @@ class FileChooserDialog { const std::string& button_label, const base::FilePath& default_path, const Filters& filters) - : dialog_scope_(parent_window), + : parent_(static_cast(parent_window)), + dialog_scope_(parent_window), filters_(filters) { const char* confirm_text = GTK_STOCK_OK; @@ -58,9 +59,10 @@ class FileChooserDialog { GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, confirm_text, GTK_RESPONSE_ACCEPT, NULL); - if (parent_window) { - gfx::NativeWindow window = parent_window->GetNativeWindow(); - libgtk2ui::SetGtkTransientForAura(dialog_, window); + if (parent_) { + parent_->SetEnabled(false); + libgtk2ui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow()); + gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); } if (action == GTK_FILE_CHOOSER_ACTION_SAVE) @@ -69,8 +71,6 @@ class FileChooserDialog { if (action != GTK_FILE_CHOOSER_ACTION_OPEN) gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog_), TRUE); - gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); - if (!default_path.empty()) { if (base::DirectoryExists(default_path)) { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog_), @@ -89,6 +89,8 @@ class FileChooserDialog { virtual ~FileChooserDialog() { gtk_widget_destroy(dialog_); + if (parent_) + parent_->SetEnabled(true); } void RunAsynchronous() { @@ -143,6 +145,7 @@ class FileChooserDialog { void AddFilters(const Filters& filters); base::FilePath AddExtensionForFilename(const gchar* filename) const; + atom::NativeWindowViews* parent_; atom::NativeWindow::DialogScope dialog_scope_; GtkWidget* dialog_; @@ -208,7 +211,9 @@ base::FilePath FileChooserDialog::AddExtensionForFilename( const auto& extensions = filters_[i].second; for (const auto& extension : extensions) { - if (extension == "*" || path.MatchesExtension("." + extension)) + if (extension == "*" || + base::EndsWith(path.value(), "." + extension, + base::CompareCase::INSENSITIVE_ASCII)) return path; } diff --git a/atom/browser/ui/message_box_gtk.cc b/atom/browser/ui/message_box_gtk.cc index b09ccdd48279..d08171c6e426 100644 --- a/atom/browser/ui/message_box_gtk.cc +++ b/atom/browser/ui/message_box_gtk.cc @@ -5,7 +5,7 @@ #include "atom/browser/ui/message_box.h" #include "atom/browser/browser.h" -#include "atom/browser/native_window.h" +#include "atom/browser/native_window_views.h" #include "base/callback.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -36,7 +36,8 @@ class GtkMessageBox { const std::string& detail, const gfx::ImageSkia& icon) : dialog_scope_(parent_window), - cancel_id_(cancel_id) { + cancel_id_(cancel_id), + parent_(static_cast(parent_window)) { // Create dialog. dialog_ = gtk_message_dialog_new( nullptr, // parent @@ -75,14 +76,17 @@ class GtkMessageBox { } // Parent window. - if (parent_window) { - gfx::NativeWindow window = parent_window->GetNativeWindow(); - libgtk2ui::SetGtkTransientForAura(dialog_, window); + if (parent_) { + parent_->SetEnabled(false); + libgtk2ui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow()); + gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); } } ~GtkMessageBox() { gtk_widget_destroy(dialog_); + if (parent_) + parent_->SetEnabled(true); } GtkMessageType GetMessageType(MessageBoxType type) { @@ -123,7 +127,6 @@ class GtkMessageBox { } int RunSynchronous() { - gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); Show(); int response = gtk_dialog_run(GTK_DIALOG(dialog_)); if (response < 0) @@ -149,6 +152,7 @@ class GtkMessageBox { // The id to return when the dialog is closed without pressing buttons. int cancel_id_; + NativeWindowViews* parent_; GtkWidget* dialog_; MessageBoxCallback callback_; diff --git a/atom/browser/ui/tray_icon.cc b/atom/browser/ui/tray_icon.cc index dcdb90ac159c..fda68b09cd11 100644 --- a/atom/browser/ui/tray_icon.cc +++ b/atom/browser/ui/tray_icon.cc @@ -30,6 +30,10 @@ void TrayIcon::PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) { } +gfx::Rect TrayIcon::GetBounds() { + return gfx::Rect(); +} + void TrayIcon::NotifyClicked(const gfx::Rect& bounds, int modifiers) { FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnClicked(bounds, modifiers)); } diff --git a/atom/browser/ui/tray_icon.h b/atom/browser/ui/tray_icon.h index 1916c11b23af..2763e50941b6 100644 --- a/atom/browser/ui/tray_icon.h +++ b/atom/browser/ui/tray_icon.h @@ -60,8 +60,12 @@ class TrayIcon { // Set the context menu for this icon. virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) = 0; + // Returns the bounds of tray icon. + virtual gfx::Rect GetBounds(); + void AddObserver(TrayIconObserver* obs) { observers_.AddObserver(obs); } void RemoveObserver(TrayIconObserver* obs) { observers_.RemoveObserver(obs); } + void NotifyClicked(const gfx::Rect& = gfx::Rect(), int modifiers = 0); void NotifyDoubleClicked(const gfx::Rect& = gfx::Rect(), int modifiers = 0); void NotifyBalloonShow(); diff --git a/atom/browser/ui/tray_icon_cocoa.h b/atom/browser/ui/tray_icon_cocoa.h index 59e2241aa480..cb972d54a9af 100644 --- a/atom/browser/ui/tray_icon_cocoa.h +++ b/atom/browser/ui/tray_icon_cocoa.h @@ -32,6 +32,7 @@ class TrayIconCocoa : public TrayIcon, void PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) override; void SetContextMenu(ui::SimpleMenuModel* menu_model) override; + gfx::Rect GetBounds() override; protected: // AtomMenuModel::Observer: diff --git a/atom/browser/ui/tray_icon_cocoa.mm b/atom/browser/ui/tray_icon_cocoa.mm index 0dfd59132ae8..2510d3160cc6 100644 --- a/atom/browser/ui/tray_icon_cocoa.mm +++ b/atom/browser/ui/tray_icon_cocoa.mm @@ -8,6 +8,7 @@ #include "base/strings/sys_string_conversions.h" #include "ui/events/cocoa/cocoa_event_utils.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/mac/coordinate_conversion.h" #include "ui/gfx/screen.h" namespace { @@ -236,13 +237,13 @@ const CGFloat kVerticalTitleMargin = 2; // Single click event. if (event.clickCount == 1) trayIcon_->NotifyClicked( - [self getBoundsFromEvent:event], + gfx::ScreenRectFromNSRect(event.window.frame), ui::EventFlagsFromModifiers([event modifierFlags])); // Double click event. if (event.clickCount == 2) trayIcon_->NotifyDoubleClicked( - [self getBoundsFromEvent:event], + gfx::ScreenRectFromNSRect(event.window.frame), ui::EventFlagsFromModifiers([event modifierFlags])); [self setNeedsDisplay:YES]; @@ -262,7 +263,7 @@ const CGFloat kVerticalTitleMargin = 2; } if (menuController_ && ![menuController_ isMenuOpen]) { - // Redraw the dray icon to show highlight if it is enabled. + // Redraw the tray icon to show highlight if it is enabled. [self setNeedsDisplay:YES]; [statusItem_ popUpStatusItemMenu:[menuController_ menu]]; // The popUpStatusItemMenu returns only after the showing menu is closed. @@ -273,7 +274,7 @@ const CGFloat kVerticalTitleMargin = 2; - (void)rightMouseUp:(NSEvent*)event { trayIcon_->NotifyRightClicked( - [self getBoundsFromEvent:event], + gfx::ScreenRectFromNSRect(event.window.frame), ui::EventFlagsFromModifiers([event modifierFlags])); } @@ -324,13 +325,6 @@ const CGFloat kVerticalTitleMargin = 2; return isHighlightEnable_ && (inMouseEventSequence_ || isMenuOpen); } -- (gfx::Rect)getBoundsFromEvent:(NSEvent*)event { - NSRect frame = event.window.frame; - gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); - NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; - bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); - return bounds; -} @end namespace atom { @@ -386,6 +380,15 @@ void TrayIconCocoa::SetContextMenu(ui::SimpleMenuModel* menu_model) { [status_item_view_ setMenuController:menu_.get()]; } +gfx::Rect TrayIconCocoa::GetBounds() { + auto bounds = gfx::ScreenRectFromNSRect([status_item_view_ window].frame); + // Calling [window frame] immediately after the view gets created will have + // negative |y| sometimes. + if (bounds.y() < 0) + bounds.set_y(0); + return bounds; +} + void TrayIconCocoa::MenuClosed() { [status_item_view_ setNeedsDisplay:YES]; } diff --git a/atom/browser/ui/views/menu_bar.cc b/atom/browser/ui/views/menu_bar.cc index 872375ccbbd1..0538421bfe64 100644 --- a/atom/browser/ui/views/menu_bar.cc +++ b/atom/browser/ui/views/menu_bar.cc @@ -50,14 +50,7 @@ void GetMenuBarColor(SkColor* enabled, SkColor* disabled, SkColor* highlight, MenuBar::MenuBar() : background_color_(kDefaultColor), menu_model_(NULL) { -#if defined(OS_WIN) - background_color_ = color_utils::GetSysSkColor(COLOR_MENUBAR); -#elif defined(USE_X11) - GetMenuBarColor(&enabled_color_, &disabled_color_, &highlight_color_, - &hover_color_, &background_color_); -#endif - - set_background(views::Background::CreateSolidBackground(background_color_)); + UpdateMenuBarColor(); SetLayoutManager(new views::BoxLayout( views::BoxLayout::kHorizontal, 0, 0, 0)); } @@ -152,11 +145,27 @@ void MenuBar::OnMenuButtonClicked(views::MenuButton* source, int id = source->tag(); ui::MenuModel::ItemType type = menu_model_->GetTypeAt(id); - if (type != ui::MenuModel::TYPE_SUBMENU) + if (type != ui::MenuModel::TYPE_SUBMENU) { + menu_model_->ActivatedAt(id, 0); return; + } MenuDelegate menu_delegate(this); menu_delegate.RunMenu(menu_model_->GetSubmenuModelAt(id), source); } +void MenuBar::OnNativeThemeChanged(const ui::NativeTheme* theme) { + UpdateMenuBarColor(); +} + +void MenuBar::UpdateMenuBarColor() { +#if defined(OS_WIN) + background_color_ = color_utils::GetSysSkColor(COLOR_MENUBAR); +#elif defined(USE_X11) + GetMenuBarColor(&enabled_color_, &disabled_color_, &highlight_color_, + &hover_color_, &background_color_); +#endif + set_background(views::Background::CreateSolidBackground(background_color_)); +} + } // namespace atom diff --git a/atom/browser/ui/views/menu_bar.h b/atom/browser/ui/views/menu_bar.h index 9f38a5981c7f..eb62784e71ca 100644 --- a/atom/browser/ui/views/menu_bar.h +++ b/atom/browser/ui/views/menu_bar.h @@ -60,9 +60,11 @@ class MenuBar : public views::View, void OnMenuButtonClicked(views::MenuButton* source, const gfx::Point& point, const ui::Event* event) override; - + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; private: + void UpdateMenuBarColor(); + SkColor background_color_; #if defined(USE_X11) diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index 1cc616216c6e..a82ac36c7acb 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -13,6 +13,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/screen.h" +#include "ui/gfx/win/dpi.h" #include "ui/views/controls/menu/menu_runner.h" namespace atom { @@ -48,26 +49,19 @@ NotifyIcon::~NotifyIcon() { void NotifyIcon::HandleClickEvent(int modifiers, bool left_mouse_click, bool double_button_click) { - NOTIFYICONIDENTIFIER icon_id; - memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER)); - icon_id.uID = icon_id_; - icon_id.hWnd = window_; - icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER); - - RECT rect = { 0 }; - Shell_NotifyIconGetRect(&icon_id, &rect); + gfx::Rect bounds = GetBounds(); if (left_mouse_click) { if (double_button_click) // double left click - NotifyDoubleClicked(gfx::Rect(rect), modifiers); + NotifyDoubleClicked(bounds, modifiers); else // single left click - NotifyClicked(gfx::Rect(rect), modifiers); + NotifyClicked(bounds, modifiers); return; } else if (!double_button_click) { // single right click if (menu_model_) PopUpContextMenu(gfx::Point(), menu_model_); else - NotifyRightClicked(gfx::Rect(rect), modifiers); + NotifyRightClicked(bounds, modifiers); } } @@ -140,8 +134,9 @@ void NotifyIcon::DisplayBalloon(HICON icon, void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) { // Returns if context menu isn't set. - if (!menu_model) + if (menu_model == nullptr && menu_model_ == nullptr) return; + // Set our window as the foreground window, so the context menu closes when // we click away from it. if (!SetForegroundWindow(window_)) @@ -153,7 +148,7 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, rect.set_origin(gfx::Screen::GetScreen()->GetCursorScreenPoint()); views::MenuRunner menu_runner( - menu_model, + menu_model != nullptr ? menu_model : menu_model_, views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS); ignore_result(menu_runner.RunMenuAt( NULL, NULL, rect, views::MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_MOUSE)); @@ -163,6 +158,18 @@ void NotifyIcon::SetContextMenu(ui::SimpleMenuModel* menu_model) { menu_model_ = menu_model; } +gfx::Rect NotifyIcon::GetBounds() { + NOTIFYICONIDENTIFIER icon_id; + memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER)); + icon_id.uID = icon_id_; + icon_id.hWnd = window_; + icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER); + + RECT rect = { 0 }; + Shell_NotifyIconGetRect(&icon_id, &rect); + return gfx::win::ScreenToDIPRect(gfx::Rect(rect)); +} + void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) { memset(icon_data, 0, sizeof(NOTIFYICONDATA)); icon_data->cbSize = sizeof(NOTIFYICONDATA); diff --git a/atom/browser/ui/win/notify_icon.h b/atom/browser/ui/win/notify_icon.h index 95e9945a17f8..1284ebadcf1c 100644 --- a/atom/browser/ui/win/notify_icon.h +++ b/atom/browser/ui/win/notify_icon.h @@ -54,6 +54,7 @@ class NotifyIcon : public TrayIcon { void PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) override; void SetContextMenu(ui::SimpleMenuModel* menu_model) override; + gfx::Rect GetBounds() override; private: void InitIconData(NOTIFYICONDATA* icon_data); diff --git a/atom/common/api/atom_api_clipboard.cc b/atom/common/api/atom_api_clipboard.cc index cb413800be77..01e670be9261 100644 --- a/atom/common/api/atom_api_clipboard.cc +++ b/atom/common/api/atom_api_clipboard.cc @@ -54,12 +54,16 @@ std::string Read(const std::string& format_string, void Write(const mate::Dictionary& data, mate::Arguments* args) { ui::ScopedClipboardWriter writer(GetClipboardType(args)); - base::string16 text, html; + base::string16 text, html, bookmark; gfx::Image image; - if (data.Get("text", &text)) + if (data.Get("text", &text)) { writer.WriteText(text); + if (data.Get("bookmark", &bookmark)) + writer.WriteBookmark(bookmark, base::UTF16ToUTF8(text)); + } + if (data.Get("rtf", &text)) { std::string rtf = base::UTF16ToUTF8(text); writer.WriteRTF(rtf); @@ -122,6 +126,23 @@ void WriteHtml(const base::string16& html, mate::Arguments* args) { writer.WriteHTML(html, std::string()); } +v8::Local ReadBookmark(mate::Arguments* args) { + base::string16 title; + std::string url; + mate::Dictionary dict = mate::Dictionary::CreateEmpty(args->isolate()); + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + clipboard->ReadBookmark(&title, &url); + dict.Set("title", title); + dict.Set("url", url); + return dict.GetHandle(); +} + +void WriteBookmark(const base::string16& title, const std::string& url, + mate::Arguments* args) { + ui::ScopedClipboardWriter writer(GetClipboardType(args)); + writer.WriteBookmark(title, url); +} + gfx::Image ReadImage(mate::Arguments* args) { ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); SkBitmap bitmap = clipboard->ReadImage(GetClipboardType(args)); @@ -150,6 +171,8 @@ void Initialize(v8::Local exports, v8::Local unused, dict.SetMethod("writeRTF", &WriteRtf); dict.SetMethod("readHTML", &ReadHtml); dict.SetMethod("writeHTML", &WriteHtml); + dict.SetMethod("readBookmark", &ReadBookmark); + dict.SetMethod("writeBookmark", &WriteBookmark); dict.SetMethod("readImage", &ReadImage); dict.SetMethod("writeImage", &WriteImage); dict.SetMethod("clear", &Clear); diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index c0b51ba2af28..14500496ebf3 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -160,10 +160,14 @@ base::win::ScopedHICON ReadICOFromPath(int size, const base::FilePath& path) { LR_LOADFROMFILE))); } -void ReadImageSkiaFromICO(gfx::ImageSkia* image, HICON icon) { +bool ReadImageSkiaFromICO(gfx::ImageSkia* image, HICON icon) { // Convert the icon from the Windows specific HICON to gfx::ImageSkia. std::unique_ptr bitmap(IconUtil::CreateSkBitmapFromHICON(icon)); + if (!bitmap) + return false; + image->AddRepresentation(gfx::ImageSkiaRep(*bitmap, 1.0f)); + return true; } #endif @@ -344,14 +348,17 @@ mate::Handle NativeImage::CreateFromDataURL( void NativeImage::BuildPrototype( v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("toPng", &NativeImage::ToPNG) - .SetMethod("toJpeg", &NativeImage::ToJPEG) + .SetMethod("toPNG", &NativeImage::ToPNG) + .SetMethod("toJPEG", &NativeImage::ToJPEG) .SetMethod("getNativeHandle", &NativeImage::GetNativeHandle) .SetMethod("toDataURL", &NativeImage::ToDataURL) .SetMethod("isEmpty", &NativeImage::IsEmpty) .SetMethod("getSize", &NativeImage::GetSize) .SetMethod("setTemplateImage", &NativeImage::SetTemplateImage) - .SetMethod("isTemplateImage", &NativeImage::IsTemplateImage); + .SetMethod("isTemplateImage", &NativeImage::IsTemplateImage) + // TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings + .SetMethod("toPng", &NativeImage::ToPNG) + .SetMethod("toJpeg", &NativeImage::ToJPEG); } } // namespace api diff --git a/atom/common/api/event_emitter_caller.cc b/atom/common/api/event_emitter_caller.cc index 271ea705e09c..ac6c9c213f7b 100644 --- a/atom/common/api/event_emitter_caller.cc +++ b/atom/common/api/event_emitter_caller.cc @@ -16,11 +16,8 @@ v8::Local CallEmitWithArgs(v8::Isolate* isolate, v8::Local obj, ValueVector* args) { // Perform microtask checkpoint after running JavaScript. - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope( + isolate, v8::MicrotasksScope::kRunMicrotasks); // Use node::MakeCallback to call the callback, and it will also run pending // tasks in Node.js. return node::MakeCallback( diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index d649af2a6edc..ff139fe9846f 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 1 #define ATOM_MINOR_VERSION 2 -#define ATOM_PATCH_VERSION 3 +#define ATOM_PATCH_VERSION 6 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h index 3c3b780b9ce0..66870f4d6c38 100644 --- a/atom/common/chrome_version.h +++ b/atom/common/chrome_version.h @@ -8,7 +8,7 @@ #ifndef ATOM_COMMON_CHROME_VERSION_H_ #define ATOM_COMMON_CHROME_VERSION_H_ -#define CHROME_VERSION_STRING "51.0.2704.103" +#define CHROME_VERSION_STRING "51.0.2704.106" #define CHROME_VERSION "v" CHROME_VERSION_STRING #endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/atom/common/crash_reporter/crash_reporter_win.cc b/atom/common/crash_reporter/crash_reporter_win.cc index 4264f6af9db4..7ce61d69719c 100644 --- a/atom/common/crash_reporter/crash_reporter_win.cc +++ b/atom/common/crash_reporter/crash_reporter_win.cc @@ -137,7 +137,8 @@ void UnregisterNonABICompliantCodeRange(void* start) { } // namespace -CrashReporterWin::CrashReporterWin() { +CrashReporterWin::CrashReporterWin() + : skip_system_crash_handler_(false) { } CrashReporterWin::~CrashReporterWin() { diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index 43a1baf25fa8..decc36eb5767 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -48,11 +48,8 @@ struct V8FunctionInvoker(ArgTypes...)> { v8::EscapableHandleScope handle_scope(isolate); if (!function.IsAlive()) return v8::Null(isolate); - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(isolate, + v8::MicrotasksScope::kRunMicrotasks); v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); @@ -71,11 +68,8 @@ struct V8FunctionInvoker { v8::HandleScope handle_scope(isolate); if (!function.IsAlive()) return; - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(isolate, + v8::MicrotasksScope::kRunMicrotasks); v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); @@ -94,11 +88,8 @@ struct V8FunctionInvoker { ReturnType ret = ReturnType(); if (!function.IsAlive()) return ret; - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(isolate, + v8::MicrotasksScope::kRunMicrotasks); v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index ed2ea01675a1..40fa6bdb6e28 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -226,10 +226,8 @@ void NodeBindings::UvRunOnce() { v8::Context::Scope context_scope(env->context()); // Perform microtask checkpoint after running JavaScript. - std::unique_ptr script_scope(is_browser_ ? - nullptr : - new v8::MicrotasksScope(env->isolate(), - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(env->isolate(), + v8::MicrotasksScope::kRunMicrotasks); // Deal with uv events. int r = uv_run(uv_loop_, UV_RUN_NOWAIT); diff --git a/default_app/main.js b/default_app/main.js index 4e5ea96b03eb..dd887e0def1e 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -52,45 +52,30 @@ app.once('ready', () => { label: 'Edit', submenu: [ { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, { - label: 'Cut', - accelerator: 'CmdOrCtrl+X', role: 'cut' }, { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', role: 'copy' }, { - label: 'Paste', - accelerator: 'CmdOrCtrl+V', role: 'paste' }, { - label: 'Paste and Match Style', - accelerator: 'Shift+Command+V', role: 'pasteandmatchstyle' }, { - label: 'Delete', role: 'delete' }, { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', role: 'selectall' } ] @@ -106,19 +91,11 @@ app.once('ready', () => { } }, { - label: 'Toggle Full Screen', - accelerator: (() => { - return (process.platform === 'darwin') ? 'Ctrl+Command+F' : 'F11' - })(), - click (item, focusedWindow) { - if (focusedWindow) focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) - } + role: 'togglefullscreen' }, { label: 'Toggle Developer Tools', - accelerator: (() => { - return (process.platform === 'darwin') ? 'Alt+Command+I' : 'Ctrl+Shift+I' - })(), + accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', click (item, focusedWindow) { if (focusedWindow) focusedWindow.toggleDevTools() } @@ -126,23 +103,17 @@ app.once('ready', () => { ] }, { - label: 'Window', role: 'window', submenu: [ { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' } ] }, { - label: 'Help', role: 'help', submenu: [ { @@ -180,14 +151,12 @@ app.once('ready', () => { label: 'Electron', submenu: [ { - label: 'About Electron', role: 'about' }, { type: 'separator' }, { - label: 'Services', role: 'services', submenu: [] }, @@ -195,54 +164,52 @@ app.once('ready', () => { type: 'separator' }, { - label: 'Hide Electron', - accelerator: 'Command+H', role: 'hide' }, { - label: 'Hide Others', - accelerator: 'Command+Alt+H', role: 'hideothers' }, { - label: 'Show All', role: 'unhide' }, { type: 'separator' }, { - label: 'Quit ' + app.getName(), - accelerator: 'Command+Q', - click () { app.quit() } + role: 'quit' } ] }) template[3].submenu = [ { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' }, { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Zoom', role: 'zoom' }, { type: 'separator' }, { - label: 'Bring All to Front', role: 'front' } ] } + if (process.platform === 'win32') { + template.unshift({ + label: 'File', + submenu: [ + { + role: 'quit' + } + ] + }) + } + const menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu) }) diff --git a/docs-translations/jp/api/native-image.md b/docs-translations/jp/api/native-image.md index a819123707c2..1c0c2ff9c6dc 100644 --- a/docs-translations/jp/api/native-image.md +++ b/docs-translations/jp/api/native-image.md @@ -112,11 +112,11 @@ const nativeImage = require('electron').nativeImage; var image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); ``` -### `image.toPng()` +### `image.toPNG()` `PNG`エンコードされた画像を含む[Buffer][buffer]を返します。 -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**required**) - Between 0 - 100. diff --git a/docs-translations/ko-KR/api/app.md b/docs-translations/ko-KR/api/app.md index b6695bdf21a6..d8d639c86698 100644 --- a/docs-translations/ko-KR/api/app.md +++ b/docs-translations/ko-KR/api/app.md @@ -647,6 +647,33 @@ dock 아이콘을 표시합니다. dock 아이콘의 `image`를 설정합니다. +### `app.launcher.setBadgeCount(count)` _Linux_ +* `count` Integer + +Unity 런처에서 앱 아이콘 옆에 표시될 숫자를 설정합니다. `0`으로 설정하면 뱃지를 +숨깁니다. + +**참고:** 이 기능은 현재 Ubuntu Unity에서만 사용할 수 있습니다. 다른 환경에서 이 +함수를 호출하는 것은 아무런 효과가 없습니다. + +**참고:** 이 기능을 사용하려면 `package.json`의 `desktopName` 필드에 `.desktop` 파일 이름을 설정해야 합니다. 기본적으로 패키징된 애플리케이션의 `app.getName().desktop`을 사용합니다. + +### `app.launcher.getBadgeCount()` _Linux_ + +런처 아이콘 옆 배지의 카운터에 표시된 현재 값을 반환합니다. + +**참고:** `setBadgeCount`가 Ubuntu Unity에서만 지원하기 때문에, 애플리케이션이 다른 +플랫폼에서 돌아가고 있다면 이 메서드는 `0`을 반환합니다. + +### `app.launcher.isUnityRunning()` _Linux_ + +현재 데스크톱 환경이 Unity인지 여부를 반환합니다. Unity가 맞다면 `true`를 반환합니다. + +### `app.launcher.isCounterBadgeAvailable()` _Linux_ + +현재 데스크톱 환경이 애플리케이션 아이콘 카운터 뱃지를 사용할 수 있는지 여부를 +반환합니다. 사용할 수 있다면 `true`를 반환합니다. + [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [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 diff --git a/docs-translations/ko-KR/api/browser-window.md b/docs-translations/ko-KR/api/browser-window.md index b65f50911fbb..d214a0270d0b 100644 --- a/docs-translations/ko-KR/api/browser-window.md +++ b/docs-translations/ko-KR/api/browser-window.md @@ -62,6 +62,38 @@ win.loadURL('https://github.com') 참고로 `ready-to-show` 이벤트를 사용하더라도 어플리케이션을 네이티브 느낌이 나도록 하기 위해 `backgroundColor`도 같이 설정하는 것을 권장합니다. +## 부모와 자식 윈도우 + +`parent` 옵션을 사용하면 자식 윈도우를 만들 수 있습니다: + +```javascript +let top = new BrowserWindow() +let child = new BrowserWindow({parent: top}) +``` + +`child` 윈도우는 언제나 `top` 윈도우의 상위에 표시됩니다. + +### 모달 윈도우 + +모달 윈도우는 부모 윈도우를 비활성화 시키는 자식 윈도우입니다. 모달 윈도우를 만드려면 `parent`, `modal` 옵션을 동시에 설정해야 합니다: + +```javascript +let child = new BrowserWindow({parent: top, modal: true, show: false}) +child.loadURL('https://github.com') +child.once('ready-to-show', () => { + child.show() +}) +``` + +### 플랫폼별 특이사항 + +* On macOS the child windows will keep the relative position to parent window + when parent window moves, while on Windows and Linux child windows will not + move. +* On Windows it is not supported to change parent window dynamically. +* On Linux the type of modal windows will be changed to `dialog`. +* On Linux many desktop environments do not support hiding a modal window. + ## Class: BrowserWindow `BrowserWindow`는 [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)를 @@ -125,6 +157,9 @@ On Windows it is * `show` Boolean - 윈도우가 생성되면 보여줄지 여부. 기본값은 `true`입니다. * `frame` Boolean - `false`로 지정하면 창을 [Frameless Window](frameless-window.md) 형태로 생성합니다. 기본값은 `true`입니다. +* `parent` BrowserWindow - 부모 윈도우를 설정합니다. 기본 값은 `null`입니다. +* `modal` Boolean - 이 윈도우가 모달 윈도우인지 여부를 설정합니다. 이 옵션은 자식 + 윈도우에서만 작동합니다. 기본값은 `false`입니다. * `acceptFirstMouse` Boolean - 윈도우가 비활성화 상태일 때 내부 콘텐츠 클릭 시 활성화 되는 동시에 단일 mouse-down 이벤트를 발생시킬지 여부. 기본값은 `false`입니다. * `disableAutoHideCursor` Boolean - 타이핑중 자동으로 커서를 숨길지 여부. 기본값은 @@ -528,6 +563,10 @@ let win = new BrowserWindow({width: 800, height: 600}); 윈도우가 사용자에게 표시되고 있는지 여부를 반환합니다. +### `win.isModal()` + +현재 윈도우가 모달 윈도우인지 여부를 반환합니다. + ### `win.maximize()` 윈도우를 최대화 시킵니다. @@ -894,7 +933,7 @@ Linux 플랫폼에선 Unity 데스크톱 환경만 지원합니다. 그리고 ### `win.setHasShadow(hasShadow)` _macOS_ -* `hasShadow` (Boolean) +* `hasShadow` Boolean 윈도우가 그림자를 가질지 여부를 지정합니다. Windows와 Linux에선 아무 일도 일어나지 않습니다. @@ -995,10 +1034,32 @@ Linux 플랫폼에선 Unity 데스크톱 환경만 지원합니다. 그리고 이 윈도우에서 일어나는 모든 마우스 이벤트가 이 윈도우 밑의 윈도우로 전달됩니다. 하지만 이 윈도우가 포커스되어 있다면, 여전히 키보드 이벤트는 받을 수 있습니다. +### `win.setContentProtection(enable)` _macOS_ _Windows_ + +윈도우 콘텐츠가 외부 어플리케이션에 의해 캡쳐되는 것을 막습니다. + +macOS에선 NSWindow의 sharingType을 NSWindowSharingNone로 설정합니다. +Windows에선 WDA_MONITOR와 함께 SetWindowDisplayAffinity를 호출합니다. + ### `win.setFocusable(focusable)` _Windows_ * `focusable` Boolean 윈도우가 포커스될 수 있는지 여부를 변경합니다. +### `win.setParentWindow(parent)` _Linux_ _macOS_ + +* `parent` BrowserWindow + +`parent` 인수를 현재 윈도우의 부모 윈도우로 설정합니다. `null`로 설정하면 +현재 윈도우를 상위 윈도우로 전환합니다. + +### `win.getParentWindow()` + +모든 부모 윈도우를 반환합니다. + +### `win.getChildWindows()` + +모든 자식 윈도우를 반환합니다. + [blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in diff --git a/docs-translations/ko-KR/api/clipboard.md b/docs-translations/ko-KR/api/clipboard.md index 48b1b21b57c6..6536bf922bca 100644 --- a/docs-translations/ko-KR/api/clipboard.md +++ b/docs-translations/ko-KR/api/clipboard.md @@ -76,6 +76,19 @@ console.log(clipboard.readText('selection')); 클립보드에 `text`를 RTF 형식으로 씁니다. +### `clipboard.readBookmark()` _macOS_ _Windows_ + +클립보드로부터 북마크 형식으로 표현된 `title`와 `url` 키를 담은 객체를 반환합니다. +`title`과 `url` 값들은 북마크를 사용할 수 없을 때 빈 문자열을 포함합니다. + +### `clipboard.writeBookmark(title, url[, type])` _macOS_ _Windows_ + +* `title` String +* `url` String +* `type` String (optional) + +`title`과 `url`을 클립보드에 북마크 형식으로 씁니다. + ### `clipboard.clear([type])` * `type` String (optional) @@ -110,6 +123,8 @@ console.log(clipboard.has('

selection

')); * `text` String * `html` String * `image` [NativeImage](native-image.md) + * `rtf` String + * `bookmark` String - `text`에 있는 URL의 텍스트. * `type` String (optional) ```javascript diff --git a/docs-translations/ko-KR/api/menu-item.md b/docs-translations/ko-KR/api/menu-item.md index 254fec147f1a..27ad7216c9f8 100644 --- a/docs-translations/ko-KR/api/menu-item.md +++ b/docs-translations/ko-KR/api/menu-item.md @@ -2,7 +2,7 @@ > 네이티브 애플리케이션 메뉴와 컨텍스트 메뉴에 아이템을 추가합니다. -[`menu`](menu.md)에서 예시를 확인할 수 있습니다. +[`Menu`](menu.md)에서 예시를 확인할 수 있습니다. ## Class: MenuItem @@ -11,9 +11,9 @@ ### new MenuItem(options) * `options` Object - * `click` Function - 메뉴 아이템이 클릭될 때 `click(menuItem, browserWindow)` - 형태로 호출 되는 콜백 함수 - * `role` String - 메뉴 아이템의 액션을 정의합니다; 이 속성을 지정하면 `click` + * `click` Function - 메뉴 아이템이 클릭될 때 `click(menuItem, browserWindow, + event)` 형태로 호출 되는 콜백 함수. + * `role` String - 메뉴 아이템의 액션을 정의합니다. 이 속성을 지정하면 `click` 속성이 무시됩니다. * `type` String - `MenuItem`의 타입 `normal`, `separator`, `submenu`, `checkbox` 또는 `radio`를 사용할 수 있습니다. 만약 값이 `Menu`가 아니면 @@ -39,6 +39,9 @@ 함수로 일일이 구현하려 시도하는 것 보다 더 좋을 수 있습니다. 빌트-인 `role` 동작은 더 좋은 네이티브 경험을 제공할 것입니다. +`role`을 사용하는 동안에는 `label`과 `accelerator`는 필수가 아니며 각 플랫폼에 대해 +적합한 값이 기본값으로 사용됩니다. + `role` 속성은 다음 값을 가질 수 있습니다: * `undo` @@ -51,6 +54,8 @@ * `delete` * `minimize` - 현재 윈도우를 최소화합니다 * `close` - 현재 윈도우를 닫습니다 +* `quit`- 애플리케이션을 닫습니다 +* `togglefullscreen` - 현재 윈도우에서 전체 화면 모드를 토글합니다 macOS에서의 `role`은 다음 값을 추가로 가질 수 있습니다: @@ -67,20 +72,28 @@ macOS에서의 `role`은 다음 값을 추가로 가질 수 있습니다: macOS에서는 `role`을 지정할 때, `label`과 `accelerator`만 MenuItem에 효과가 적용되도록 변경되며, 다른 옵션들은 모두 무시됩니다. -## Instance Properties +### Instance Properties -다음 속성들은 존재하는 `MenuItem`에서 계속 변경될 수 있습니다: +다음은 `MenuItem`의 인스턴스에서 사용할 수 있는 속성입니다: - * `enabled` Boolean - * `visible` Boolean - * `checked` Boolean +#### `menuItem.enabled` -이 속성들의 의미는 위 옵션에서 설명한 것과 같습니다. +아이템이 활성화되어있는지 여부를 표시하는 Boolean 값입니다. 이 속성은 동적으로 변경될 +수 있습니다. -`checkbox` 메뉴 아이템은 선택될 때 해당 아이템의 `checked` 속성을 통해 활성화 그리고 -비활성화 상태인지를 표시합니다. 또한 `click` 함수를 지정하여 추가적인 작업을 할 수도 -있습니다. +#### `menuItem.visible` -`radio` 메뉴 아이템은 클릭되었을 때 `checked` 속성을 활성화 합니다. 그리고 같은 -메뉴의 인접한 모든 다른 아이템은 비활성화됩니다. 또한 `click` 함수를 지정하여 추가적인 -작업을 할 수도 있습니다. +아이템이 보여지고있는지 여부를 표시하는 Boolean 값입니다. 이 속성은 동적으로 변경될 +수 있습니다. + +#### `menuItem.checked` + +아이템이 선택되어있는지 여부를 반환하는 Boolean 값입니다. 이 속성은 동적으로 변경될 +수 있습니다. + +`checkbox` 메뉴 아이템은 선택되면 `checked` 속성을 토글합니다. + +`radio` 메뉴 아이템은 클릭되었을 때 `checked` 속성을 활성화 합니다. 그리고 +같은 메뉴의 모든 인접한 아이템에 대한 속성이 꺼집니다. + +추가적인 작업을 위해 `click` 함수를 추가할 수도 있습니다. diff --git a/docs-translations/ko-KR/api/menu.md b/docs-translations/ko-KR/api/menu.md index eaee7e8d4f9d..c6ba3a6bb908 100644 --- a/docs-translations/ko-KR/api/menu.md +++ b/docs-translations/ko-KR/api/menu.md @@ -38,45 +38,30 @@ const template = [ label: 'Edit', submenu: [ { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, { - label: 'Cut', - accelerator: 'CmdOrCtrl+X', role: 'cut' }, { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', role: 'copy' }, { - label: 'Paste', - accelerator: 'CmdOrCtrl+V', role: 'paste' }, { - label: 'Paste and Match Style', - accelerator: 'Shift+Command+V', role: 'pasteandmatchstyle' }, { - label: 'Delete', role: 'delete' }, { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', role: 'selectall' }, ] @@ -92,12 +77,7 @@ const template = [ } }, { - label: 'Toggle Full Screen', - accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', - click(item, focusedWindow) { - if (focusedWindow) - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); - } + role: 'togglefullscreen' }, { label: 'Toggle Developer Tools', @@ -110,23 +90,17 @@ const template = [ ] }, { - label: 'Window', role: 'window', submenu: [ { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' }, ] }, { - label: 'Help', role: 'help', submenu: [ { @@ -143,14 +117,12 @@ if (process.platform === 'darwin') { label: name, submenu: [ { - label: 'About ' + name, role: 'about' }, { type: 'separator' }, { - label: 'Services', role: 'services', submenu: [] }, @@ -158,26 +130,19 @@ if (process.platform === 'darwin') { type: 'separator' }, { - label: 'Hide ' + name, - accelerator: 'Command+H', role: 'hide' }, { - label: 'Hide Others', - accelerator: 'Command+Alt+H', role: 'hideothers' }, { - label: 'Show All', role: 'unhide' }, { type: 'separator' }, { - label: 'Quit', - accelerator: 'Command+Q', - click() { app.quit(); } + role: 'quit' }, ] }); @@ -217,7 +182,7 @@ Menu.setApplicationMenu(menu); 새로운 메뉴를 생성합니다. -### Methods +## Methods `menu` 클래스는 다음과 같은 메서드를 가지고 있습니다: diff --git a/docs-translations/ko-KR/api/native-image.md b/docs-translations/ko-KR/api/native-image.md index d44f000a1c90..dfdc180b6c78 100644 --- a/docs-translations/ko-KR/api/native-image.md +++ b/docs-translations/ko-KR/api/native-image.md @@ -125,11 +125,11 @@ let image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); `nativeImage` 인스턴스 객체에서 사용할 수 있는 메서드입니다. -### `image.toPng()` +### `image.toPNG()` `PNG` 이미지를 인코딩한 데이터를 [Buffer][buffer]로 반환합니다. -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**required**) 0 - 100 사이의 값 diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index a2dc9b682a6b..626ca240db07 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -4,16 +4,15 @@ `session` 모듈은 새로운 `Session` 객체를 만드는데 사용할 수 있습니다. -또한 존재하는 [`BrowserWindow`](browser-window.md)의 -[`webContents`](web-contents.md)에서 `session` 속성으로 접근할 수도 있습니다. +또한 [`WebContents`](web-contents.md)의 `session` 속성이나 `session` 모듈을 통해 현재 존재하는 페이지의 `session`에 접근할 수 있습니다. ```javascript -const {BrowserWindow} = require('electron'); +const {session, BrowserWindow} = require('electron') -let win = new BrowserWindow({width: 800, height: 600}); -win.loadURL('http://github.com'); +let win = new BrowserWindow({width: 800, height: 600}) +win.loadURL('http://github.com') -let ses = win.webContents.session; +let ses = win.webContents.session ``` ## Methods @@ -75,92 +74,7 @@ session.defaultSession.on('will-download', (event, item, webContents) => { ### Instance Methods -`Session` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: - -#### `ses.cookies` - -`cookies` 속성은 쿠키를 조작하는 방법을 제공합니다. 예를 들어 다음과 같이 할 수 -있습니다: - -```javascript -// 모든 쿠키를 요청합니다. -session.defaultSession.cookies.get({}, (error, cookies) => { - console.log(cookies); -}); - -// url에 관련된 쿠키를 모두 가져옵니다. -session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { - console.log(cookies); -}); - -// 지정한 쿠키 데이터를 설정합니다. -// 동일한 쿠키가 있으면 해당 쿠키를 덮어씁니다. -const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}; -session.defaultSession.cookies.set(cookie, (error) => { - if (error) - console.error(error); -}); -``` - -#### `ses.cookies.get(filter, callback)` - -* `filter` Object - * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 - 생략하면 모든 url에서 찾습니다. - * `name` String (optional) - 쿠키의 이름입니다. - * `domain` String (optional) - 도메인 또는 서브 도메인에 일치하는 쿠키를 - 취득합니다. - * `path` String (optional) - `path`에 일치하는 쿠키를 취득합니다. - * `secure` Boolean (optional) - 보안 속성에 따라 쿠키를 필터링합니다. - * `session` Boolean (optional) - 세션 또는 지속성 쿠키를 필터링합니다. -* `callback` Function - -`details` 객체에서 묘사한 모든 쿠키를 요청합니다. 모든 작업이 끝나면 `callback`이 -`callback(error, cookies)` 형태로 호출됩니다. - -`cookies`는 `cookie` 객체의 배열입니다. - -* `cookie` Object - * `name` String - 쿠키의 이름. - * `value` String - 쿠키의 값. - * `domain` String - 쿠키의 도메인. - * `hostOnly` String - 쿠키가 호스트 전용인가에 대한 여부. - * `path` String - 쿠키의 경로. - * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부. - * `httpOnly` Boolean - 쿠키가 HTTP로만 표시되는지에 대한 여부. - * `session` Boolean - 쿠키가 세션 쿠키 또는 만료일이 있는 영구 쿠키인지에 대한 - 여부. - * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 - 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. - -#### `ses.cookies.set(details, callback)` - -* `details` Object - * `url` String - 쿠키에 대한 `url` 링크. - * `name` String - 쿠키의 이름입니다. 기본적으로 비워두면 생략됩니다. - * `value` String - 쿠키의 값입니다. 기본적으로 비워두면 생략됩니다. - * `domain` String - 쿠키의 도메인입니다. 기본적으로 비워두면 생략됩니다. - * `path` String - 쿠키의 경로입니다. 기본적으로 비워두면 생략됩니다. - * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부입니다. 기본값은 - false입니다. - * `session` Boolean - 쿠키가 Http 전용으로 표시되는지에 대한 여부입니다. 기본값은 - false입니다. - * `expirationDate` Double (optional) - UNIX 시간으로 표시되는 쿠키의 만료일에 - 대한 초 단위 시간입니다. 생략되면 쿠키가 세션 쿠기가 되며 세션 사이에 유지되지 - 않게 됩니다. -* `callback` Function - -`details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 -`callback(error)` 형태로 호출됩니다. - -#### `ses.cookies.remove(url, name, callback)` - -* `url` String - 쿠키와 관련된 URL입니다. -* `name` String - 지울 쿠키의 이름입니다. -* `callback` Function - -`url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 -`callback()` 형식으로 호출됩니다. +`Session` 객체는 다음과 같은 메서드를 가지고 있습니다: #### `ses.getCacheSize(callback)` @@ -232,7 +146,7 @@ proxyURL = ["://"][":"] * `http=foopy;socks=foopy2` - http:// URL에 `foopy` HTTP 프록시를 사용합니다. 그리고 `socks4://foopy2` 프록시를 다른 모든 URL에 사용합니다. -### `app.resolveProxy(url, callback)` +#### `ses.resolveProxy(url, callback)` * `url` URL * `callback` Function @@ -339,14 +253,156 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` +#### `ses.setUserAgent(userAgent[, acceptLanguages])` + +* `userAgent` String +* `acceptLanguages` String (optional) + +현재 세션에 대해 `userAgent`와 `acceptLanguages`를 덮어씁니다. + +`acceptLanguages`는 반드시 쉼표로 구분된 순서에 맞춘 언어 코드의 리스트여야 하며 +예를 들면 `"en-US,fr,de,ko,zh-CN,ja"` 입니다. + +이는 현재 존재하는 `WebContents`에 적용되지 않으며 각 `WebContents`는 +`webContents.setUserAgent`를 사용하여 세션 전체의 유저 에이전트를 덮어쓸 수 있습니다. + +#### `ses.getUserAgent()` + +현재 세션의 유저 에이전트를 표현하는 `String`을 반환합니다. + +### Instance Properties + +다음은 `Session` 인스턴스에서 사용할 수 있는 속성들입니다: + +#### `ses.cookies` + +현재 세션의 `Cookies` 클래스 인스턴스를 반환합니다. + #### `ses.webRequest` -`webRequest` API는 생명주기의 다양한 단계에 맞춰 요청 콘텐츠를 가로채거나 변경할 수 -있도록 합니다. +현재 세션의 `WebRequest` 클래스 인스턴스를 반환합니다. -각 API는 `filter`와 `listener`를 선택적으로 받을 수 있습니다. `listener`는 API의 -이벤트가 발생했을 때 `listener(details)` 형태로 호출되며 `defails`는 요청을 묘사하는 -객체입니다. `listener`에 `null`을 전달하면 이벤트 수신을 중지합니다. +#### `ses.protocol` + +현재 세션의 [protocol](protocol.md) 모듈 인스턴스를 반환합니다. + +```javascript +const {app, session} = require('electron') +const path = require('path') + +app.on('ready', function () { + const protocol = session.fromPartition(partitionName).protocol + protocol.registerFileProtocol('atom', function (request, callback) { + var url = request.url.substr(7) + callback({path: path.normalize(__dirname + '/' + url)}) + }, function (error) { + if (error) + console.error('Failed to register protocol') + }) +}) +``` + +## Class: Cookies + +`Cookies` 클래스는 쿠키를 탐색하고 조작하는 방법을 제공합니다. `Cookies` 클래스의 +인스턴스는 반드시 `Session` 클래스의 `cookies` 속성에서 접근해야 합니다. + +예를 들어: + +```javascript +// 모든 쿠키를 요청합니다. +session.defaultSession.cookies.get({}, (error, cookies) => { + console.log(cookies); +}); + +// url에 관련된 쿠키를 모두 가져옵니다. +session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { + console.log(cookies); +}); + +// 지정한 쿠키 데이터를 설정합니다. +// 동일한 쿠키가 있으면 해당 쿠키를 덮어씁니다. +const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}; +session.defaultSession.cookies.set(cookie, (error) => { + if (error) + console.error(error); +}); +``` + +### Instance Methods + +다음은 `Cookies` 객체에서 사용할 수 있는 메서드들입니다: + +#### `ses.cookies.get(filter, callback)` + +* `filter` Object + * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 + 생략하면 모든 url에서 찾습니다. + * `name` String (optional) - 쿠키의 이름입니다. + * `domain` String (optional) - 도메인 또는 서브 도메인에 일치하는 쿠키를 + 취득합니다. + * `path` String (optional) - `path`에 일치하는 쿠키를 취득합니다. + * `secure` Boolean (optional) - 보안 속성에 따라 쿠키를 필터링합니다. + * `session` Boolean (optional) - 세션 또는 지속성 쿠키를 필터링합니다. +* `callback` Function + +`details` 객체에서 묘사한 모든 쿠키를 요청합니다. 모든 작업이 끝나면 `callback`이 +`callback(error, cookies)` 형태로 호출됩니다. + +`cookies`는 `cookie` 객체의 배열입니다. + +* `cookie` Object + * `name` String - 쿠키의 이름. + * `value` String - 쿠키의 값. + * `domain` String - 쿠키의 도메인. + * `hostOnly` String - 쿠키가 호스트 전용인가에 대한 여부. + * `path` String - 쿠키의 경로. + * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부. + * `httpOnly` Boolean - 쿠키가 HTTP로만 표시되는지에 대한 여부. + * `session` Boolean - 쿠키가 세션 쿠키 또는 만료일이 있는 영구 쿠키인지에 대한 + 여부. + * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 + 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. + +#### `ses.cookies.set(details, callback)` + +* `details` Object + * `url` String - 쿠키에 대한 `url` 링크. + * `name` String - 쿠키의 이름입니다. 기본적으로 비워두면 생략됩니다. + * `value` String - 쿠키의 값입니다. 기본적으로 비워두면 생략됩니다. + * `domain` String - 쿠키의 도메인입니다. 기본적으로 비워두면 생략됩니다. + * `path` String - 쿠키의 경로입니다. 기본적으로 비워두면 생략됩니다. + * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부입니다. 기본값은 + false입니다. + * `httpOnly` Boolean - 쿠키가 Http 전용으로 표시되는지에 대한 여부입니다. 기본값은 + false입니다. + * `expirationDate` Double (optional) - UNIX 시간으로 표시되는 쿠키의 만료일에 + 대한 초 단위 시간입니다. 생략되면 쿠키가 세션 쿠기가 되며 세션 사이에 유지되지 + 않게 됩니다. +* `callback` Function + +`details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 +`callback(error)` 형태로 호출됩니다. + +#### `ses.cookies.remove(url, name, callback)` + +* `url` String - 쿠키와 관련된 URL입니다. +* `name` String - 지울 쿠키의 이름입니다. +* `callback` Function + +`url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 +`callback()` 형식으로 호출됩니다. + +## Class: WebRequest + +`WebRequest` 클래스는 생명 주기의 다양한 단계에서 요청의 콘텐츠를 조작하거나 가로채는 +방법을 제공합니다. `WebRequest` 클래스는 반드시 `Session` 클래스의 `webRequest` +속성에서 접근해야 합니다. + +`WebRequest`의 메서드는 선택적인 `filter`와 `listener` 속성을 허용하며 `listener`는 +API의 이벤트가 발생했을 때 `listener(details)` 형식으로 호출되고, `details`는 요청에 +관한 내용을 표현하는 객체입니다. `listener`에 `null`을 전달하면 이벤트의 구독을 +해제합니다. `filter`는 `urls` 속성을 가진 객체입니다. 이 속성은 URL 규칙의 배열이며 URL 규칙에 일치하지 않는 요청을 모두 거르는데 사용됩니다. 만약 `filter`가 생략되면 모든 요청을 @@ -355,19 +411,25 @@ session.defaultSession.allowNTLMCredentialsForDomains('*') 어떤 `listener`의 이벤트들은 `callback`을 같이 전달하는데, 이벤트 처리시 `listener`의 작업을 완료한 후 `response` 객체를 포함하여 호출해야 합니다. +다음은 요청에 `User-Agent` 헤더를 추가하는 예시입니다: + ```javascript // 다음 url에 대한 User Agent를 조작합니다. const filter = { urls: ['https://*.github.com/*', '*://electron.github.io'] -}; +} session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => { - details.requestHeaders['User-Agent'] = "MyAgent"; - callback({cancel: false, requestHeaders: details.requestHeaders}); -}); + details.requestHeaders['User-Agent'] = "MyAgent" + callback({cancel: false, requestHeaders: details.requestHeaders}) +}) ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### Instance Methods + +다음은 `WebRequest` 객체에서 사용할 수 있는 메서드들입니다: + +#### `webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -396,7 +458,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback * `redirectURL` String (optional) - 원래 요청은 전송과 완료가 방지되지만 이 속성을 지정하면 해당 URL로 리다이렉트됩니다. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -421,7 +483,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `requestHeaders` Object (optional) - 이 속성이 제공되면, 요청은 이 헤더로 만들어 집니다. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -438,7 +500,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter, ]listener)` +#### `webRequest.onHeadersReceived([filter, ]listener)` * `filter` Object * `listener` Function @@ -467,7 +529,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener 변경하기 위해 반드시 지정되어야 합니다. 그렇지 않은 경우, 기존의 응답 헤더의 상태가 사용됩니다. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -486,7 +548,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -506,7 +568,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +#### `webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -524,7 +586,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -539,23 +601,3 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `timestamp` Double * `fromCache` Boolean * `error` String - 에러 설명. - -#### `ses.protocol` - -현재 세션의 [protocol](protocol.md) 모듈 인스턴스를 반환합니다. - -```javascript -const {app, session} = require('electron') -const path = require('path') - -app.on('ready', function () { - const protocol = session.fromPartition(partitionName).protocol - protocol.registerFileProtocol('atom', function (request, callback) { - var url = request.url.substr(7) - callback({path: path.normalize(__dirname + '/' + url)}) - }, function (error) { - if (error) - console.error('Failed to register protocol') - }) -}) -``` diff --git a/docs-translations/ko-KR/api/system-preferences.md b/docs-translations/ko-KR/api/system-preferences.md index 254bd006874d..d8170b73cf3e 100644 --- a/docs-translations/ko-KR/api/system-preferences.md +++ b/docs-translations/ko-KR/api/system-preferences.md @@ -2,6 +2,11 @@ > 시스템 설정을 가져옵니다. +```javascript +const {systemPreferences} = require('electron'); +console.log(systemPreferences.isDarkMode()); +``` + ## Methods ### `systemPreferences.isDarkMode()` _macOS_ @@ -34,6 +39,17 @@ macOS의 네이티브 알림을 구독하며, 해당하는 `event`가 발생하 `id`와 함께 구독자를 제거합니다. +### `systemPreferences.subscribeLocalNotification(event, callback)` _macOS_ + +`subscribeNotification`와 같지만, 로컬 기본값으로 `NSNotificationCenter`를 +사용합니다. 다음과 같은 이벤트에 필수적입니다: + +* `NSUserDefaultsDidChangeNotification` + +### `systemPreferences.unsubscribeLocalNotification(id)` _macOS_ + +`unsubscribeNotification`와 같지만, `NSNotificationCenter`에서 구독자를 제거합니다. + ### `systemPreferences.getUserDefault(key, type)` _macOS_ * `key` String diff --git a/docs-translations/ko-KR/api/tray.md b/docs-translations/ko-KR/api/tray.md index 95fe59fd597b..46f790cd7651 100644 --- a/docs-translations/ko-KR/api/tray.md +++ b/docs-translations/ko-KR/api/tray.md @@ -3,20 +3,20 @@ > 아이콘과 컨텍스트 메뉴를 시스템 알림 영역에 추가합니다. ```javascript -const {app, Menu, Tray} = require('electron'); +const {app, Menu, Tray} = require('electron') -let appIcon = null; +let tray = null app.on('ready', () => { - appIcon = new Tray('/path/to/my/icon'); // 현재 애플리케이션 디렉터리를 기준으로 하려면 `__dirname + '/images/tray.png'` 형식으로 입력해야 합니다. + tray = new Tray('/path/to/my/icon') // 현재 애플리케이션 디렉터리를 기준으로 하려면 `__dirname + '/images/tray.png'` 형식으로 입력해야 합니다. const contextMenu = Menu.buildFromTemplate([ {label: 'Item1', type: 'radio'}, {label: 'Item2', type: 'radio'}, {label: 'Item3', type: 'radio', checked: true}, {label: 'Item4', type: 'radio'} ]); - appIcon.setToolTip('이것은 나의 애플리케이션 입니다!'); - appIcon.setContextMenu(contextMenu); -}); + tray.setToolTip('이것은 나의 애플리케이션 입니다!') + tray.setContextMenu(contextMenu) +}) ``` __플랫폼별 한계:__ @@ -33,8 +33,8 @@ __플랫폼별 한계:__ 호출해야 합니다. 예를 들면: ```javascript -contextMenu.items[2].checked = false; -appIcon.setContextMenu(contextMenu); +contextMenu.items[2].checked = false +appIcon.setContextMenu(contextMenu) ``` 이러한 이유로 Tray API가 모든 플랫폼에서 똑같이 작동하게 하고 싶다면 `click` 이벤트에 @@ -50,20 +50,18 @@ appIcon.setContextMenu(contextMenu); 전달된 `image`를 이용하여 트레이 아이콘을 만듭니다. -## Events +### Instance Events `Tray` 모듈은 다음과 같은 이벤트를 가지고 있습니다: -**참고:** 몇몇 이벤트는 특정한 플랫폼에서만 작동합니다. - -### Event: 'click' +#### Event: 'click' * `event` Event * `altKey` Boolean * `shiftKey` Boolean * `ctrlKey` Boolean * `metaKey` Boolean -* `bounds` Object - 트레이 아이콘의 범위 +* `bounds` Object _macOS_ _Windows_ - 트레이 아이콘의 범위 * `x` Integer * `y` Integer * `width` Integer @@ -71,9 +69,7 @@ appIcon.setContextMenu(contextMenu); 트레이 아이콘이 클릭될 때 발생하는 이벤트입니다. -**참고:** `bounds`는 macOS 와 Windows에서만 작동합니다. - -### Event: 'right-click' _macOS_ _Windows_ +#### Event: 'right-click' _macOS_ _Windows_ * `event` Event * `altKey` Boolean @@ -88,7 +84,7 @@ appIcon.setContextMenu(contextMenu); 트레이 아이콘을 오른쪽 클릭될 때 호출 됩니다. -### Event: 'double-click' _macOS_ _Windows_ +#### Event: 'double-click' _macOS_ _Windows_ * `event` Event * `altKey` Boolean @@ -103,83 +99,81 @@ appIcon.setContextMenu(contextMenu); 트레이 아이콘이 더블 클릭될 때 발생하는 이벤트입니다. -### Event: 'balloon-show' _Windows_ +#### Event: 'balloon-show' _Windows_ 풍선 팝업이 보여질 때 발생하는 이벤트입니다. -### Event: 'balloon-click' _Windows_ +#### Event: 'balloon-click' _Windows_ 풍선 팝업이 클릭될 때 발생하는 이벤트입니다. -### Event: 'balloon-closed' _Windows_ +#### Event: 'balloon-closed' _Windows_ 풍선 팝업이 시간이 지나 사라지거나 유저가 클릭하여 닫을 때 발생하는 이벤트입니다. -### Event: 'drop' _macOS_ +#### Event: 'drop' _macOS_ 드래그 가능한 아이템이 트레이 아이콘에 드롭되면 발생하는 이벤트입니다. -### Event: 'drop-files' _macOS_ +#### Event: 'drop-files' _macOS_ -* `event` +* `event` Event * `files` Array - 드롭된 파일의 경로 트레이 아이콘에 파일이 드롭되면 발생하는 이벤트입니다. -### Event: 'drag-enter' _macOS_ +#### Event: 'drag-enter' _macOS_ 트레이 아이콘에 드래그 작업이 시작될 때 발생하는 이벤트입니다. -### Event: 'drag-leave' _macOS_ +#### Event: 'drag-leave' _macOS_ 트레이 아이콘에 드래그 작업이 종료될 때 발생하는 이벤트입니다. -### Event: 'drag-end' _macOS_ +#### Event: 'drag-end' _macOS_ 트레이 아이콘에 드래그 작업이 종료되거나 다른 위치에서 종료될 때 발생하는 이벤트입니다. -## Methods +### Instance Methods -`Tray` 모듈은 다음과 같은 메서드를 가지고 있습니다: +`Tray` 클래스는 다음과 같은 메서드를 가지고 있습니다: -**참고:** 몇몇 메서드는 특정 플랫폼에서만 작동합니다. - -### `Tray.destroy()` +#### `tray.destroy()` 트레이 아이콘을 즉시 삭제시킵니다. -### `Tray.setImage(image)` +#### `tray.setImage(image)` * `image` [NativeImage](native-image.md) `image`를 사용하여 트레이 아이콘의 이미지를 설정합니다. -### `Tray.setPressedImage(image)` _macOS_ +#### `tray.setPressedImage(image)` _macOS_ * `image` [NativeImage](native-image.md) `image`를 사용하여 트레이 아이콘이 눌렸을 때의 이미지를 설정합니다. -### `Tray.setToolTip(toolTip)` +#### `tray.setToolTip(toolTip)` * `toolTip` String 트레이 아이콘의 툴팁 텍스트를 설정합니다. -### `Tray.setTitle(title)` _macOS_ +#### `tray.setTitle(title)` _macOS_ * `title` String 상태바에서 트레이 아이콘 옆에 표시되는 제목 텍스트를 설정합니다. -### `Tray.setHighlightMode(highlight)` _macOS_ +#### `tray.setHighlightMode(highlight)` _macOS_ * `highlight` Boolean 트레이 아이콘이 클릭됐을 때 아이콘의 배경이 파란색으로 하이라이트 될지 여부를 지정합니다. 기본값은 true입니다. -### `Tray.displayBalloon(options)` _Windows_ +#### `tray.displayBalloon(options)` _Windows_ * `options` Object * `icon` [NativeImage](native-image.md) @@ -188,7 +182,7 @@ appIcon.setContextMenu(contextMenu); 트레이에 풍선 팝업을 생성합니다. -### `Tray.popUpContextMenu([menu, position])` _macOS_ _Windows_ +#### `tray.popUpContextMenu([menu, position])` _macOS_ _Windows_ * `menu` Menu (optional) * `position` Object (optional) - 팝업 메뉴의 위치 @@ -200,10 +194,20 @@ appIcon.setContextMenu(contextMenu); `position`은 Windows에서만 사용할 수 있으며 기본값은 (0, 0)입니다. -### `Tray.setContextMenu(menu)` +### `tray.setContextMenu(menu)` * `menu` Menu 트레이에 컨텍스트 메뉴를 설정합니다. +### `tray.getBounds()` _macOS_ _Windows_ + +이 트레이 아이콘의 `bounds`를 `Object` 형식으로 반환합니다. + +* `bounds` Object + * `x` Integer + * `y` Integer + * `width` Integer + * `height` Integer + [event-emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter diff --git a/docs-translations/ko-KR/api/web-contents.md b/docs-translations/ko-KR/api/web-contents.md index 437b5dabbe7b..ba53f3dbd8ff 100644 --- a/docs-translations/ko-KR/api/web-contents.md +++ b/docs-translations/ko-KR/api/web-contents.md @@ -349,23 +349,25 @@ Returns: 표시합니다. 자세한 사항은 아래를 참고하세요. `mediaFlags`는 다음과 같은 속성을 가지고 있습니다: - * `inError` Boolean - 미디어 객체가 크래시되었는지 여부. - * `isPaused` Boolean - 미디어 객체가 일시중지되었는지 여부. - * `isMuted` Boolean - 미디어 객체가 음소거되었는지 여부. - * `hasAudio` Boolean - 미디어 객체가 오디오를 가지고 있는지 여부. - * `isLooping` Boolean - 미디어 객체가 루프중인지 여부. - * `isControlsVisible` Boolean - 미디어 객체의 컨트롤이 보이는지 여부. - * `canToggleControls` Boolean - 미디어 객체의 컨트롤을 토글할 수 있는지 여부. - * `canRotate` Boolean - 미디어 객체를 돌릴 수 있는지 여부. + +* `inError` Boolean - 미디어 객체가 크래시되었는지 여부. +* `isPaused` Boolean - 미디어 객체가 일시중지되었는지 여부. +* `isMuted` Boolean - 미디어 객체가 음소거되었는지 여부. +* `hasAudio` Boolean - 미디어 객체가 오디오를 가지고 있는지 여부. +* `isLooping` Boolean - 미디어 객체가 루프중인지 여부. +* `isControlsVisible` Boolean - 미디어 객체의 컨트롤이 보이는지 여부. +* `canToggleControls` Boolean - 미디어 객체의 컨트롤을 토글할 수 있는지 여부. +* `canRotate` Boolean - 미디어 객체를 돌릴 수 있는지 여부. `editFlags`는 다음과 같은 속성을 가지고 있습니다: - * `canUndo` Boolean - 렌더러에서 실행 취소할 수 있는지 여부. - * `canRedo` Boolean - 렌더러에서 다시 실행할 수 있는지 여부. - * `canCut` Boolean - 렌더러에서 잘라내기를 실행할 수 있는지 여부. - * `canCopy` Boolean - 렌더러에서 복사를 실행할 수 있는지 여부. - * `canPaste` Boolean - 렌더러에서 붙여넣기를 실행할 수 있는지 여부. - * `canDelete` Boolean - 렌더러에서 삭제를 실행할 수 있는지 여부. - * `canSelectAll` Boolean - 렌더러에서 모두 선택을 실행할 수 있는지 여부. + +* `canUndo` Boolean - 렌더러에서 실행 취소할 수 있는지 여부. +* `canRedo` Boolean - 렌더러에서 다시 실행할 수 있는지 여부. +* `canCut` Boolean - 렌더러에서 잘라내기를 실행할 수 있는지 여부. +* `canCopy` Boolean - 렌더러에서 복사를 실행할 수 있는지 여부. +* `canPaste` Boolean - 렌더러에서 붙여넣기를 실행할 수 있는지 여부. +* `canDelete` Boolean - 렌더러에서 삭제를 실행할 수 있는지 여부. +* `canSelectAll` Boolean - 렌더러에서 모두 선택을 실행할 수 있는지 여부. 새로운 컨텍스트 메뉴의 제어가 필요할 때 발생하는 이벤트입니다. @@ -904,12 +906,14 @@ Input `event`를 웹 페이지로 전송합니다. * `hasPreciseScrollingDeltas` Boolean * `canScroll` Boolean -### `webContents.beginFrameSubscription(callback)` +### `webContents.beginFrameSubscription([onlyDirty ,]callback)` +* `onlyDirty` Boolean (optional) - 기본값은 `false`입니다. * `callback` Function 캡처된 프레임과 프레젠테이션 이벤트를 구독하기 시작합니다. `callback`은 -프레젠테이션 이벤트가 발생했을 때 `callback(frameBuffer)` 형태로 호출됩니다. +프레젠테이션 이벤트가 발생했을 때 `callback(frameBuffer, dirtyRect)` 형태로 +호출됩니다. `frameBuffer`는 raw 픽셀 데이터를 가지고 있는 `Buffer` 객체입니다. 많은 장치에서 32비트 BGRA 포맷을 사용하여 효율적으로 픽셀 데이터를 저장합니다. 하지만 실질적인 @@ -917,6 +921,10 @@ Input `event`를 웹 페이지로 전송합니다. 프로세서에선 little-endian 방식을 사용하므로 위의 포맷을 그대로 표현합니다. 하지만 몇몇 프로세서는 big-endian 방식을 사용하는데, 이 경우 32비트 ARGB 포맷을 사용합니다) +`dirtyRect`는 페이지의 어떤 부분이 다시 그려졌는지를 표현하는 `x, y, width, height` +속성을 포함하는 객체입니다. 만약 `onlyDirty`가 `true`로 지정되어 있으면, +`frameBuffer`가 다시 그려진 부분만 포함합니다. `onlyDirty`의 기본값은 `false`입니다. + ### `webContents.endFrameSubscription()` 프레임 프레젠테이션 이벤트들에 대한 구독을 중지합니다. diff --git a/docs-translations/ko-KR/styleguide.md b/docs-translations/ko-KR/styleguide.md index 6fd30bbef10a..584d3245bdb5 100644 --- a/docs-translations/ko-KR/styleguide.md +++ b/docs-translations/ko-KR/styleguide.md @@ -53,14 +53,12 @@ Electron 문서 구조를 이해하는 데 참고할 수 있는 유용한 도움 [Method](https://developer.mozilla.org/ko/docs/Glossary/Method) 문서의 예시입니다: ---- - +``` `methodName(required[, optional]))` -* `require` String (**required**) +* `required` String (**required**) * `optional` Integer - ---- +``` 메서드 이름은 인수가 무엇을 받는지에 따라 결정됩니다. 선택적 인수는 브라켓([, ])으로 묶어 이 인수가 다른 인수뒤에서 선택적으로 사용될 수 있다는 것을 표시합니다. @@ -73,19 +71,25 @@ Electron 문서 구조를 이해하는 데 참고할 수 있는 유용한 도움 와 같은 일반적으로 쓰이는 타입 중 하나를 받거나 Electron의 [`webContent`](api/web-content.md) 같은 커스텀 타입을 받습니다. +만약 인수가 특정 플랫폼에 대해서만 적용된다면, 해당하는 플랫폼들을 공백-구분된 이텔릭체 +리스트를 데이터 타입 뒤에 표기합니다. 값은 `OS X`, `Windows` 또는 `Linux`가 될 수 +있습니다. + +``` +* `animate` Boolean (optional) _OS X_ _Windows_ +``` + ### Events [Event](https://developer.mozilla.org/ko/docs/Web/API/Event) 문서의 예시입니다: ---- - +``` Event: 'wake-up' Returns: * `time` String - ---- +``` 이벤트는 `.on` 리스너 메서드로 사용할 수 있습니다. 만약 이벤트에서 값을 반환한다면 문서에서 표시된 대로 일정한 타입의 값을 반환합니다. 이벤트를 처리하거나 응답하려 한다면 @@ -94,5 +98,5 @@ Returns: ```javascript Alarm.on('wake-up', (time) => { console.log(time) -}); +}) ``` diff --git a/docs-translations/ko-KR/tutorial/debugging-main-process.md b/docs-translations/ko-KR/tutorial/debugging-main-process.md index 339c28daab7a..fc6fee619b6d 100644 --- a/docs-translations/ko-KR/tutorial/debugging-main-process.md +++ b/docs-translations/ko-KR/tutorial/debugging-main-process.md @@ -19,11 +19,11 @@ ## node-inspector로 디버깅 하기 -**참고:** Electron은 현재 node-inspector 유틸리티와 호환성 문제가 있습니다. 따라서 -node-inspector 콘솔 내에서 메인 프로세스의 `process` 객체를 탐색할 경우 크래시가 +**참고:** Electron은 현재 node-inspector 유틸리티와 호환성 문제가 있습니다. 이러한 +이유로 node-inspector 콘솔 내에서 메인 프로세스의 `process` 객체를 탐색할 경우 크래시가 발생할 수 있습니다. -### 1. [node-gyp 필수 도구][node-gyp-required-tools]를 설치했는지 확인 +### 1. [node-gyp 필수 도구][node-gyp-required-tools] 설치 ### 2. [node-inspector][node-inspector] 설치 @@ -31,17 +31,19 @@ node-inspector 콘솔 내에서 메인 프로세스의 `process` 객체를 탐 $ npm install node-inspector ``` -### 3. 패치된 버전의 `node-pre-gyp` 설치 +### 3. [node-pre-gyp][node-pre-gyp] 설치 ```bash -$ npm install git+https://git@github.com/enlight/node-pre-gyp.git#detect-electron-runtime-in-find +$ npm install node-pre-gyp ``` -### 4. Electron용 `node-inspector` `v8` 모듈을 재 컴파일 (target을 사용하는 Electron의 버전에 맞춰 변경) +### 4. Electron용 `node-inspector` `v8` 모듈을 재 컴파일 + +**참고:** target 인수를 사용하는 Electron의 버전에 맞춰 변경하세요. ```bash -$ node_modules/.bin/node-pre-gyp --target=0.36.11 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall -$ node_modules/.bin/node-pre-gyp --target=0.36.11 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall ``` 또한 [네이티브 모듈을 사용하는 방법][how-to-install-native-modules] 문서도 참고해보세요. @@ -60,7 +62,7 @@ $ electron --debug=5858 your/app $ electron --debug-brk=5858 your/app ``` -### 6. Electron을 사용하는 [node-inspector][node-inspector] 시작 +### 6. Electron을 사용하는 [node-inspector][node-inspector] 시작하기 ```bash $ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin/inspector.js @@ -69,9 +71,10 @@ $ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin ### 7. 디버거 UI 로드 Chrome 브라우저에서 http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 주소에 -접속합니다. (기본 포트 또는 지정한 포트로 접속) 엔트리의 라인이 debug-brk로 시작하는 +접속합니다. (기본 포트 또는 지정한 포트로 접속) 엔트리의 라인이 `debug-brk`로 시작하는 경우 일시정지 버튼을 클릭해야 할 수도 있습니다. [node-inspector]: https://github.com/node-inspector/node-inspector +[node-pre-gyp]: https://github.com/mapbox/node-pre-gyp [node-gyp-required-tools]: https://github.com/nodejs/node-gyp#installation [how-to-install-native-modules]: using-native-node-modules.md#네이티브-모듈을-설치하는-방법 diff --git a/docs-translations/ko-KR/tutorial/quick-start.md b/docs-translations/ko-KR/tutorial/quick-start.md index 37611d319fc1..2f0d89bba30b 100644 --- a/docs-translations/ko-KR/tutorial/quick-start.md +++ b/docs-translations/ko-KR/tutorial/quick-start.md @@ -212,7 +212,7 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/ ### 미리 작성된 앱 실행하기 -[`atom/electron-quick-start`](https://github.com/electron/electron-quick-start) +[`electron/electron-quick-start`](https://github.com/electron/electron-quick-start) 저장소를 클론하면 이 문서에서 작성한 예시 앱을 바로 실행해 볼 수 있습니다. **참고**: 이 예시를 실행시키려면 [Git](https://git-scm.com)과 diff --git a/docs-translations/pt-BR/README.md b/docs-translations/pt-BR/README.md index 8498ca420fc8..336e446bec75 100644 --- a/docs-translations/pt-BR/README.md +++ b/docs-translations/pt-BR/README.md @@ -52,10 +52,10 @@ Existem muitas perguntas comuns que são feitas, verifique antes de criar uma is * [contentTracing](../../docs/api/content-tracing.md) * [dialog](../../docs/api/dialog.md) * [globalShortcut](../../docs/api/global-shortcut.md) -* [ipcMain](../../docs/api/ipc-main-process.md) +* [ipcMain](../../docs/api/ipc-main.md) * [Menu](../../docs/api/menu.md) * [MenuItem](../../docs/api/menu-item.md) -* [powerMonitor](../../docs/api/power-monitor.md) +* [powerMonitor](api/power-monitor.md) * [powerSaveBlocker](../../docs/api/power-save-blocker.md) * [protocol](../../docs/api/protocol.md) * [session](../../docs/api/session.md) @@ -64,7 +64,7 @@ Existem muitas perguntas comuns que são feitas, verifique antes de criar uma is ### Módulos para o Processo Renderizador: -* [DesktopCapturer](../../docs/api/desktop-capturer) +* [DesktopCapturer](../../docs/api/desktop-capturer.md) * [ipcRenderer](../../docs/api/ipc-renderer.md) * [remote](../../docs/api/remote.md) * [webFrame](../../docs/api/web-frame.md) diff --git a/docs-translations/pt-BR/api/auto-updater.md b/docs-translations/pt-BR/api/auto-updater.md index f7c49db8ff50..ff4db562a0c9 100644 --- a/docs-translations/pt-BR/api/auto-updater.md +++ b/docs-translations/pt-BR/api/auto-updater.md @@ -40,7 +40,7 @@ Emitido quando está verificando se uma atualização foi inicializada. ### Evento: 'update-available' -Emitido quando há uma atualização disponível. A autalização é baixada automaticamente. +Emitido quando há uma atualização disponível. A atualização é baixada automaticamente. ### Evento: 'update-not-available' diff --git a/docs-translations/pt-BR/api/power-monitor.md b/docs-translations/pt-BR/api/power-monitor.md new file mode 100644 index 000000000000..007560129354 --- /dev/null +++ b/docs-translations/pt-BR/api/power-monitor.md @@ -0,0 +1,35 @@ +# powerMonitor + +> Monitorar as mudanças de estado de energia. + +Você só pode usá-lo no processo principal. Você não deve usar este módulo até que o evento `ready` do modulo `app` seja emitido. + +Por exemplo: + +```javascript +app.on('ready', () => { + require('electron').powerMonitor.on('suspend', () => { + console.log('O sistema está indo dormir'); + }); +}); +``` + +## Eventos + +O módulo `power-monitor` emite os seguintes eventos: + +### Evento: 'suspend' + +Emitido quando o sistema está a suspender. + +### Evento: 'resume' + +Emitido quando o sistema está a retomar. + +### Evento: 'on-ac' _Windows_ + +Emitido quando o sistema muda para energia AC. + +### Evento: 'on-battery' _Windows_ + +Emitido quando o sistema muda para a energia da bateria. diff --git a/docs-translations/pt-BR/tutorial/devtools-extension.md b/docs-translations/pt-BR/tutorial/devtools-extension.md index 958b70c2e1e6..c488cee959c0 100644 --- a/docs-translations/pt-BR/tutorial/devtools-extension.md +++ b/docs-translations/pt-BR/tutorial/devtools-extension.md @@ -41,6 +41,6 @@ Atualmente o Electron não suporta páginas em segundo plano nas extensões do C Algumas extensões do Chrome podem usar a API `chrome.*`. Apesar de um esforço na implementação destas APIs no Electron, elas ainda não estão finalizadas. -Dado que nem todas as funções `chrome.*` esstão implementadas, algumas extensões que utilizam `chrome.devtools.*` podem não funcionar. Você pode reportar este erro no issue tracker para que possamos adicionar suporte a essas APIs. +Dado que nem todas as funções `chrome.*` estão implementadas, algumas extensões que utilizam `chrome.devtools.*` podem não funcionar. Você pode reportar este erro no issue tracker para que possamos adicionar suporte a essas APIs. [devtools-extension]: https://developer.chrome.com/extensions/devtools diff --git a/docs-translations/pt-BR/tutorial/online-offline-events.md b/docs-translations/pt-BR/tutorial/online-offline-events.md index 8cdc1a6647e5..ff7bb112cee6 100644 --- a/docs-translations/pt-BR/tutorial/online-offline-events.md +++ b/docs-translations/pt-BR/tutorial/online-offline-events.md @@ -1,8 +1,8 @@ # Online/Offline Event Detection -Os eventos de detecão Online e Offile podem ser implementados no processo +Os eventos de detecção Online e Offline podem ser implementados no processo de renderização utilizando a API padrão do HTML, como é mostrado no exemplo -a seguir. +a seguir: _main.js_ @@ -41,7 +41,7 @@ Pode haver casos onde você também deseja responder a estes eventos no processo Mas o processo principal não consegue detectar esses eventos diretamente, pois não possui um objeto `navigator`. Utilizando a ferramentas para comunicação entre processos, os eventos podem ser direcionados para o processo principal e manipulados quando necessário. Você -pode ver isto no exemplo abaixo. +pode ver isto no exemplo abaixo: _main.js_ diff --git a/docs-translations/zh-CN/api/native-image.md b/docs-translations/zh-CN/api/native-image.md index aa544d07dc33..fc5955cd21ac 100644 --- a/docs-translations/zh-CN/api/native-image.md +++ b/docs-translations/zh-CN/api/native-image.md @@ -107,11 +107,11 @@ const nativeImage = require('electron').nativeImage; var image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); ``` -### `image.toPng()` +### `image.toPNG()` 返回一个 [Buffer][buffer] ,它包含了图片的 `PNG` 编码数据. -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**必须**) - 在 0 - 100 之间. diff --git a/docs-translations/zh-CN/api/shell.md b/docs-translations/zh-CN/api/shell.md index a30334dcdc9c..17ebe5511a37 100644 --- a/docs-translations/zh-CN/api/shell.md +++ b/docs-translations/zh-CN/api/shell.md @@ -6,7 +6,7 @@ 在用户默认浏览器中打开URL的示例: ```javascript -var shell = require('shell'); +const {shell} = require('electron'); shell.openExternal('https://github.com'); ``` diff --git a/docs-translations/zh-CN/api/web-contents.md b/docs-translations/zh-CN/api/web-contents.md index 54b9c56dcc6c..c05b6d2593c7 100644 --- a/docs-translations/zh-CN/api/web-contents.md +++ b/docs-translations/zh-CN/api/web-contents.md @@ -846,14 +846,14 @@ win.webContents.debugger.sendCommand("Network.enable"); 发送给定命令给调试目标. -#### Event: 'detach' +### Event: 'detach' * `event` Event * `reason` String - 拆分调试器原因. 在调试 session 结束时发出事件.这在 `webContents` 关闭时或 `webContents` 请求开发者工具栏时发生. -#### Event: 'message' +### Event: 'message' * `event` Event * `method` String - 方法名. diff --git a/docs/api/app-locales.md b/docs/api/app-locales.md new file mode 100644 index 000000000000..52efba6282d5 --- /dev/null +++ b/docs/api/app-locales.md @@ -0,0 +1,137 @@ +# Possible Values of `app.getLocale()` + +Electron uses Chromium's `l10n_util` library to fetch the locale. Possible +values are listed below: + +| Language Code | Language Name | +|---------------|---------------| +| af | Afrikaans | +| ar-AE | Arabic (U.A.E.) | +| ar-IQ | Arabic (Iraq) | +| ar | Arabic (Standard) | +| ar-BH | Arabic (Bahrain) | +| ar-DZ | Arabic (Algeria) | +| ar-EG | Arabic (Egypt) | +| ar | Aragonese | +| ar-JO | Arabic (Jordan) | +| ar-KW | Arabic (Kuwait) | +| ar-LB | Arabic (Lebanon) | +| ar-LY | Arabic (Libya) | +| ar-MA | Arabic (Morocco) | +| ar-OM | Arabic (Oman) | +| ar-QA | Arabic (Qatar) | +| ar-SA | Arabic (Saudi Arabia) | +| ar-SY | Arabic (Syria) | +| ar-TN | Arabic (Tunisia) | +| ar-YE | Arabic (Yemen) | +| as | Assamese | +| ast | Asturian | +| az | Azerbaijani | +| be | Belarusian | +| bg | Bulgarian | +| bg | Bulgarian | +| bn | Bengali | +| br | Breton | +| bs | Bosnian | +| ca | Catalan | +| ce | Chechen | +| ch | Chamorro | +| co | Corsican | +| cr | Cree | +| cs | Czech | +| cv | Chuvash | +| da | Danish | +| de | German (Standard) | +| de-AT | German (Austria) | +| de-CH | German (Switzerland) | +| de-DE | German (Germany) | +| de-LI | German (Liechtenstein) | +| de-LU | German (Luxembourg) | +| el | Greek | +| en-AU | English (Australia) | +| en-BZ | English (Belize) | +| en | English | +| en-CA | English (Canada) | +| en-GB | English (United Kingdom) | +| en-IE | English (Ireland) | +| en-JM | English (Jamaica) | +| en-NZ | English (New Zealand) | +| en-PH | English (Philippines) | +| en-TT | English (Trinidad & Tobago) | +| en-US | English (United States) | +| en-ZA | English (South Africa) | +| en-ZW | English (Zimbabwe) | +| eo | Esperanto | +| et | Estonian | +| eu | Basque | +| fa | Persian | +| fa | Farsi | +| fa-IR | Persian/Iran | +| fi | Finnish | +| fj | Fijian | +| fo | Faeroese | +| fr-CH | French (Switzerland) | +| fr-FR | French (France) | +| fr-LU | French (Luxembourg) | +| fr-MC | French (Monaco) | +| fr | French (Standard) | +| fr-BE | French (Belgium) | +| fr-CA | French (Canada) | +| fur | Friulian | +| fy | Frisian | +| ga | Irish | +| gd-IE | Gaelic (Irish) | +| gd | Gaelic (Scots) | +| gl | Galacian | +| gu | Gujurati | +| he | Hebrew | +| hi | Hindi | +| hr | Croatian | +| ht | Haitian | +| hu | Hungarian | +| hy | Armenian | +| id | Indonesian | +| is | Icelandic | +| it-CH | Italian (Switzerland) | +| it | Italian (Standard) | +| iu | Inuktitut | +| ja | Japanese | +| ka | Georgian | +| kk | Kazakh | +| km | Khmer | +| kn | Kannada | +| ko | Korean | +| ko-KP | Korean (North Korea) | +| ko-KR | Korean (South Korea) | +| ks | Kashmiri | +| ky | Kirghiz | +| la | Latin | +| lb | Luxembourgish | +| lt | Lithuanian | +| lv | Latvian | +| mi | Maori | +| mk | FYRO Macedonian | +| ml | Malayalam | +| mo | Moldavian | +| mr | Marathi | +| ms | Malay | +| mt | Maltese | +| my | Burmese | +| nb | Norwegian (Bokmal) | +| ne | Nepali | +| ng | Ndonga | +| nl | Dutch (Standard) | +| nl-BE | Dutch (Belgian) | +| nn | Norwegian (Nynorsk) | +| no | Norwegian | +| nv | Navajo | +| oc | Occitan | +| om | Oromo | +| or | Oriya | +| sq | Albanian | +| tlh | Klingon | +| zh-TW | Chinese (Taiwan) | +| zh | Chinese | +| zh-CN | Chinese (PRC) | +| zh-HK | Chinese (Hong Kong) | +| zh-SG | Chinese (Singapore) | diff --git a/docs/api/app.md b/docs/api/app.md index c014395eb10c..cfda6b574be1 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -396,7 +396,8 @@ Overrides the current application's name. ### `app.getLocale()` -Returns the current application locale. +Returns the current application locale. Possible return values are documented +[here](app-locales.md). **Note:** When distributing your packaged app, you have to also ship the `locales` folder. @@ -577,6 +578,26 @@ Disables hardware acceleration for current app. This method can only be called before app is ready. +### `app.setBadgeCount(count)` _Linux_ _macOS_ + +* `count` Integer + +Sets the counter badge for current app. Setting the count to `0` will hide the +badge. Returns `true` when the call succeeded, otherwise returns `false`. + +On macOS it shows on the dock icon. On Linux it only works for Unity launcher, + +**Note:** Unity launcher requires the exsistence of a `.desktop` file to work, +for more information please read [Desktop Environment Integration][unity-requiremnt]. + +### `app.getBadgeCount()` _Linux_ _macOS_ + +Returns the current value displayed in the counter badge. + +### `app.isUnityRunning()` _Linux_ + +Returns whether current desktop environment is Unity launcher. + ### `app.commandLine.appendSwitch(switch[, value])` Append a switch (with optional `value`) to Chromium's command line. @@ -654,3 +675,4 @@ Sets the `image` associated with this dock icon. [LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme [handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html [activity-type]: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType +[unity-requiremnt]: ../tutorial/desktop-environment-integration.md#unity-launcher-shortcuts-linux diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index e95570450b62..1f3d9d49bf06 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -542,11 +542,11 @@ the [close event](#event-close). ### `win.focus()` -Focus on the window. +Focuses on the window. ### `win.blur()` -Remove focus on the window. +Removes focus from the window. ### `win.isFocused()` @@ -945,7 +945,7 @@ convey some sort of application status or to passively notify the user. ### `win.setHasShadow(hasShadow)` _macOS_ -* `hasShadow` (Boolean) +* `hasShadow` Boolean Sets whether the window should have a shadow. On Windows and Linux does nothing. @@ -1050,6 +1050,13 @@ All mouse events happened in this window will be passed to the window below this window, but if this window has focus, it will still receive keyboard events. +### `win.setContentProtection(enable)` _macOS_ _Windows_ + +Prevents the window contents from being captured by other apps. + +On macOS it sets the NSWindow's sharingType to NSWindowSharingNone. +On Windows it calls SetWindowDisplayAffinity with WDA_MONITOR. + ### `win.setFocusable(focusable)` _Windows_ * `focusable` Boolean diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index c2a16a79b9fb..85712bc651d9 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -75,6 +75,20 @@ Returns the content in the clipboard as RTF. Writes the `text` into the clipboard in RTF. +### `clipboard.readBookmark()` _macOS_ _Windows_ + +Returns an Object containing `title` and `url` keys representing the bookmark in +the clipboard. The `title` and `url` values will be empty strings when the +bookmark is unavailable. + +### `clipboard.writeBookmark(title, url[, type])` _macOS_ _Windows_ + +* `title` String +* `url` String +* `type` String (optional) + +Writes the `title` and `url` into the clipboard as a bookmark. + ### `clipboard.clear([type])` * `type` String (optional) @@ -111,6 +125,8 @@ Reads `data` from the clipboard. * `text` String * `html` String * `image` [NativeImage](native-image.md) + * `rtf` String + * `bookmark` String - The title of the url at `text`. * `type` String (optional) ```javascript diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index ec3fef55e576..3a9a565a9dcb 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -2,7 +2,7 @@ > Add items to native application menus and context menus. -See [`menu`](menu.md) for examples. +See [`Menu`](menu.md) for examples. ## Class: MenuItem @@ -11,12 +11,12 @@ Create a new `MenuItem` with the following method: ### new MenuItem(options) * `options` Object - * `click` Function - Will be called with `click(menuItem, browserWindow)` when - the menu item is clicked - * `role` String - Define the action of the menu item; when specified the - `click` property will be ignored + * `click` Function - Will be called with + `click(menuItem, browserWindow, event)` when the menu item is clicked. + * `role` String - Define the action of the menu item, when specified the + `click` property will be ignored. * `type` String - Can be `normal`, `separator`, `submenu`, `checkbox` or - `radio` + `radio`. * `label` String * `sublabel` String * `accelerator` [Accelerator](accelerator.md) @@ -25,20 +25,23 @@ Create a new `MenuItem` with the following method: unclickable. * `visible` Boolean - If false, the menu item will be entirely hidden. * `checked` Boolean - Should only be specified for `checkbox` or `radio` type - menu items. + 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 - is not a `Menu` then it will be automatically converted to one using - `Menu.buildFromTemplate`. + `submenu` is specified, the `type: 'submenu'` can be omitted. If the value + is not a `Menu` then it will be automatically converted to one using + `Menu.buildFromTemplate`. * `id` String - Unique within a single menu. If defined then it can be used - as a reference to this item by the position attribute. + as a reference to this item by the position attribute. * `position` String - This field allows fine-grained definition of the - specific location within a given menu. + specific location within a given menu. It is best to specify `role` for any menu item that matches a standard role, rather than trying to manually implement the behavior in a `click` function. The built-in `role` behavior will give the best native experience. +The `label` and `accelerator` are optional when using a `role` and will default +to appropriate values for each platform. + The `role` property can have following values: * `undo` @@ -51,6 +54,8 @@ The `role` property can have following values: * `delete` * `minimize` - Minimize current window * `close` - Close current window +* `quit`- Quit the application +* `togglefullscreen`- Toggle full screen mode on the current window On macOS `role` can also have following additional values: @@ -67,19 +72,29 @@ On macOS `role` can also have following additional values: When specifying `role` on macOS, `label` and `accelerator` are the only options that will affect the MenuItem. All other options will be ignored. -## Instance Properties +### Instance Properties -The following properties (and no others) can be updated on an existing `MenuItem`: +The following properties are available on instances of `MenuItem`: - * `enabled` Boolean - * `visible` Boolean - * `checked` Boolean +#### `menuItem.enabled` -Their meanings are as described above. +A Boolean indicating whether the item is enabled, this property can be +dynamically changed. -A `checkbox` menu item will toggle its `checked` property on and off when -selected. You can add a `click` function to do additional work. +#### `menuItem.visible` + +A Boolean indicating whether the item is visible, this property can be +dynamically changed. + +#### `menuItem.checked` + +A Boolean indicating whether the item is checked, this property can be +dynamically changed. + +A `checkbox` menu item will toggle the `checked` property on and off when +selected. 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. +will turn off that property for all adjacent items in the same menu. + +You can add a `click` function for additional behavior. diff --git a/docs/api/menu.md b/docs/api/menu.md index 7dea2c6b4cdd..d54469545e6d 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -39,45 +39,30 @@ const template = [ label: 'Edit', submenu: [ { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, { - label: 'Cut', - accelerator: 'CmdOrCtrl+X', role: 'cut' }, { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', role: 'copy' }, { - label: 'Paste', - accelerator: 'CmdOrCtrl+V', role: 'paste' }, { - label: 'Paste and Match Style', - accelerator: 'Shift+Command+V', role: 'pasteandmatchstyle' }, { - label: 'Delete', role: 'delete' }, { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', role: 'selectall' }, ] @@ -93,12 +78,7 @@ const template = [ } }, { - label: 'Toggle Full Screen', - accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', - click(item, focusedWindow) { - if (focusedWindow) - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); - } + role: 'togglefullscreen' }, { label: 'Toggle Developer Tools', @@ -111,23 +91,17 @@ const template = [ ] }, { - label: 'Window', role: 'window', submenu: [ { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' }, ] }, { - label: 'Help', role: 'help', submenu: [ { @@ -144,14 +118,12 @@ if (process.platform === 'darwin') { label: name, submenu: [ { - label: 'About ' + name, role: 'about' }, { type: 'separator' }, { - label: 'Services', role: 'services', submenu: [] }, @@ -159,26 +131,19 @@ if (process.platform === 'darwin') { type: 'separator' }, { - label: 'Hide ' + name, - accelerator: 'Command+H', role: 'hide' }, { - label: 'Hide Others', - accelerator: 'Command+Alt+H', role: 'hideothers' }, { - label: 'Show All', role: 'unhide' }, { type: 'separator' }, { - label: 'Quit', - accelerator: 'Command+Q', - click() { app.quit(); } + role: 'quit' }, ] }); diff --git a/docs/api/native-image.md b/docs/api/native-image.md index d2a2926ca6d9..f03178577b86 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -127,11 +127,11 @@ Creates a new `nativeImage` instance from `dataURL`. The following methods are available on instances of `nativeImage`: -### `image.toPng()` +### `image.toPNG()` Returns a [Buffer][buffer] that contains the image's `PNG` encoded data. -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**required**) - Between 0 - 100. diff --git a/docs/api/session.md b/docs/api/session.md index 030b2c7542ea..78b001fdab17 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -5,16 +5,15 @@ The `session` module can be used to create new `Session` objects. You can also access the `session` of existing pages by using the `session` -property of [`webContents`](web-contents.md) which is a property of -[`BrowserWindow`](browser-window.md). +property of [`WebContents`](web-contents.md), or from the `session` module. ```javascript -const {BrowserWindow} = require('electron'); +const {session, BrowserWindow} = require('electron') -let win = new BrowserWindow({width: 800, height: 600}); -win.loadURL('http://github.com'); +let win = new BrowserWindow({width: 800, height: 600}) +win.loadURL('http://github.com') -const ses = win.webContents.session; +const ses = win.webContents.session ``` ## Methods @@ -78,91 +77,6 @@ session.defaultSession.on('will-download', (event, item, webContents) => { The following methods are available on instances of `Session`: -#### `ses.cookies` - -The `cookies` gives you ability to query and modify cookies. For example: - -```javascript -// Query all cookies. -session.defaultSession.cookies.get({}, (error, cookies) => { - console.log(cookies); -}); - -// Query all cookies associated with a specific url. -session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { - console.log(cookies); -}); - -// Set a cookie with the given cookie data; -// may overwrite equivalent cookies if they exist. -const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}; -session.defaultSession.cookies.set(cookie, (error) => { - if (error) - console.error(error); -}); -``` - -#### `ses.cookies.get(filter, callback)` - -* `filter` Object - * `url` String (optional) - Retrieves cookies which are associated with - `url`. Empty implies retrieving cookies of all urls. - * `name` String (optional) - Filters cookies by name. - * `domain` String (optional) - Retrieves cookies whose domains match or are - subdomains of `domains` - * `path` String (optional) - Retrieves cookies whose path matches `path`. - * `secure` Boolean (optional) - Filters cookies by their Secure property. - * `session` Boolean (optional) - Filters out session or persistent cookies. -* `callback` Function - -Sends a request to get all cookies matching `details`, `callback` will be called -with `callback(error, cookies)` on complete. - -`cookies` is an Array of `cookie` objects. - -* `cookie` Object - * `name` String - The name of the cookie. - * `value` String - The value of the cookie. - * `domain` String - The domain of the cookie. - * `hostOnly` String - Whether the cookie is a host-only cookie. - * `path` String - The path of the cookie. - * `secure` Boolean - Whether the cookie is marked as secure. - * `httpOnly` Boolean - Whether the cookie is marked as HTTP only. - * `session` Boolean - Whether the cookie is a session cookie or a persistent - cookie with an expiration date. - * `expirationDate` Double (optional) - The expiration date of the cookie as - the number of seconds since the UNIX epoch. Not provided for session - cookies. - -#### `ses.cookies.set(details, callback)` - -* `details` Object - * `url` String - The url to associate the cookie with. - * `name` String - The name of the cookie. Empty by default if omitted. - * `value` String - The value of the cookie. Empty by default if omitted. - * `domain` String - The domain of the cookie. Empty by default if omitted. - * `path` String - The path of the cookie. Empty by default if omitted. - * `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to - false. - * `session` Boolean - Whether the cookie should be marked as HTTP only. Defaults - to false. - * `expirationDate` Double - The expiration date of the cookie as the number of - seconds since the UNIX epoch. If omitted then the cookie becomes a session - cookie and will not be retained between sessions. -* `callback` Function - -Sets a cookie with `details`, `callback` will be called with `callback(error)` -on complete. - -#### `ses.cookies.remove(url, name, callback)` - -* `url` String - The URL associated with the cookie. -* `name` String - The name of cookie to remove. -* `callback` Function - -Removes the cookies matching `url` and `name`, `callback` will called with -`callback()` on complete. - #### `ses.getCacheSize(callback)` * `callback` Function @@ -341,15 +255,157 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` +#### `ses.setUserAgent(userAgent[, acceptLanguages])` + +* `userAgent` String +* `acceptLanguages` String (optional) + +Overrides the `userAgent` and `acceptLanguages` for this session. + +The `acceptLanguages` must a comma separated ordered list of language codes, for +example `"en-US,fr,de,ko,zh-CN,ja"`. + +This doesn't affect existing `WebContents`, and each `WebContents` can use +`webContents.setUserAgent` to override the session-wide user agent. + +#### `ses.getUserAgent()` + +Returns a `String` representing the user agent for this session. + +### Instance Properties + +The following properties are available on instances of `Session`: + +#### `ses.cookies` + +Returns an instance of `Cookies` class for this session. + #### `ses.webRequest` -The `webRequest` API set allows to intercept and modify contents of a request at -various stages of its lifetime. +Returns an instance of `WebRequest` class for this session. -Each API accepts an optional `filter` and a `listener`, the `listener` will be -called with `listener(details)` when the API's event has happened, the `details` -is an object that describes the request. Passing `null` as `listener` will -unsubscribe from the event. +#### `ses.protocol` + +Returns an instance of [protocol](protocol.md) module for this session. + +```javascript +const {app, session} = require('electron') +const path = require('path') + +app.on('ready', function () { + const protocol = session.fromPartition(partitionName).protocol + protocol.registerFileProtocol('atom', function (request, callback) { + var url = request.url.substr(7) + callback({path: path.normalize(__dirname + '/' + url)}) + }, function (error) { + if (error) + console.error('Failed to register protocol') + }) +}) +``` + +## Class: Cookies + +The `Cookies` class gives you ability to query and modify cookies. Instances of +`Cookies` class must be received by using `cookies` property of `Session` class. + +For example: + +```javascript +// Query all cookies. +session.defaultSession.cookies.get({}, (error, cookies) => { + console.log(cookies) +}) + +// Query all cookies associated with a specific url. +session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { + console.log(cookies) +}) + +// Set a cookie with the given cookie data; +// may overwrite equivalent cookies if they exist. +const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'} +session.defaultSession.cookies.set(cookie, (error) => { + if (error) + console.error(error) +}) +``` + +### Instance Methods + +The following methods are available on instances of `Cookies`: + +#### `cookies.get(filter, callback)` + +* `filter` Object + * `url` String (optional) - Retrieves cookies which are associated with + `url`. Empty implies retrieving cookies of all urls. + * `name` String (optional) - Filters cookies by name. + * `domain` String (optional) - Retrieves cookies whose domains match or are + subdomains of `domains` + * `path` String (optional) - Retrieves cookies whose path matches `path`. + * `secure` Boolean (optional) - Filters cookies by their Secure property. + * `session` Boolean (optional) - Filters out session or persistent cookies. +* `callback` Function + +Sends a request to get all cookies matching `details`, `callback` will be called +with `callback(error, cookies)` on complete. + +`cookies` is an Array of `cookie` objects. + +* `cookie` Object + * `name` String - The name of the cookie. + * `value` String - The value of the cookie. + * `domain` String - The domain of the cookie. + * `hostOnly` String - Whether the cookie is a host-only cookie. + * `path` String - The path of the cookie. + * `secure` Boolean - Whether the cookie is marked as secure. + * `httpOnly` Boolean - Whether the cookie is marked as HTTP only. + * `session` Boolean - Whether the cookie is a session cookie or a persistent + cookie with an expiration date. + * `expirationDate` Double (optional) - The expiration date of the cookie as + the number of seconds since the UNIX epoch. Not provided for session + cookies. + +#### `cookies.set(details, callback)` + +* `details` Object + * `url` String - The url to associate the cookie with. + * `name` String - The name of the cookie. Empty by default if omitted. + * `value` String - The value of the cookie. Empty by default if omitted. + * `domain` String - The domain of the cookie. Empty by default if omitted. + * `path` String - The path of the cookie. Empty by default if omitted. + * `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to + false. + * `httpOnly` Boolean - Whether the cookie should be marked as HTTP only. + Defaults to false. + * `expirationDate` Double - The expiration date of the cookie as the number of + seconds since the UNIX epoch. If omitted then the cookie becomes a session + cookie and will not be retained between sessions. +* `callback` Function + +Sets a cookie with `details`, `callback` will be called with `callback(error)` +on complete. + +#### `cookies.remove(url, name, callback)` + +* `url` String - The URL associated with the cookie. +* `name` String - The name of cookie to remove. +* `callback` Function + +Removes the cookies matching `url` and `name`, `callback` will called with +`callback()` on complete. + +## Class: WebRequest + +The `WebRequest` class allows to intercept and modify contents of a request at +various stages of its lifetime. Instances of `WebRequest` class must be received +by using `webRequest` property of `Session` class. + +The methods of `WebRequest` accept an optional `filter` and a `listener`, the +`listener` will be called with `listener(details)` when the API's event has +happened, the `details` is an object that describes the request. Passing `null` +as `listener` will unsubscribe from the event. The `filter` is an object that has an `urls` property, which is an Array of URL patterns that will be used to filter out the requests that do not match the URL @@ -358,19 +414,25 @@ patterns. If the `filter` is omitted then all requests will be matched. For certain events the `listener` is passed with a `callback`, which should be called with an `response` object when `listener` has done its work. +An example of adding `User-Agent` header for requests: + ```javascript // Modify the user agent for all requests to the following urls. const filter = { urls: ['https://*.github.com/*', '*://electron.github.io'] -}; +} session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => { - details.requestHeaders['User-Agent'] = "MyAgent"; - callback({cancel: false, requestHeaders: details.requestHeaders}); -}); + details.requestHeaders['User-Agent'] = "MyAgent" + callback({cancel: false, requestHeaders: details.requestHeaders}) +}) ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### Instance Methods + +The following methods are available on instances of `WebRequest`: + +#### `webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -400,7 +462,7 @@ The `callback` has to be called with an `response` object: * `redirectURL` String (optional) - The original request is prevented from being sent or completed, and is instead redirected to the given URL. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -425,7 +487,7 @@ The `callback` has to be called with an `response` object: * `requestHeaders` Object (optional) - When provided, request will be made with these headers. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -442,7 +504,7 @@ response are visible by the time this listener is fired. * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter,]listener)` +#### `webRequest.onHeadersReceived([filter,]listener)` * `filter` Object * `listener` Function @@ -471,7 +533,7 @@ The `callback` has to be called with an `response` object: `responseHeaders` to change header status otherwise original response header's status will be used. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -492,7 +554,7 @@ and response headers are available. * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -513,7 +575,7 @@ redirect is about to occur. * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +#### `webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -532,7 +594,7 @@ completed. * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -547,22 +609,3 @@ The `listener` will be called with `listener(details)` when an error occurs. * `timestamp` Double * `fromCache` Boolean * `error` String - The error description. - -#### `ses.protocol` - -Returns an instance of [protocol](protocol.md) module for this session. - -```javascript -const {app, session} = require('electron') -const path = require('path') - -app.on('ready', function () { - const protocol = session.fromPartition(partitionName).protocol - protocol.registerFileProtocol('atom', function (request, callback) { - var url = request.url.substr(7) - callback({path: path.normalize(__dirname + '/' + url)}) - }, function (error) { - if (error) - console.error('Failed to register protocol') - }) -}) diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index a47975d0974b..586422e56654 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -40,6 +40,17 @@ example values of `event` are: Removes the subscriber with `id`. +### `systemPreferences.subscribeLocalNotification(event, callback)` _macOS_ + +Same as `subscribeNotification`, but uses `NSNotificationCenter` for local defaults. +This is necessary for events such as: + +* `NSUserDefaultsDidChangeNotification` + +### `systemPreferences.unsubscribeLocalNotification(id)` _macOS_ + +Same as `unsubscribeNotification`, but removes the subscriber from `NSNotificationCenter`. + ### `systemPreferences.getUserDefault(key, type)` _macOS_ * `key` String diff --git a/docs/api/tray.md b/docs/api/tray.md index 902444e5336b..a93e0dbf38d5 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -3,20 +3,20 @@ > Add icons and context menus to the system's notification area. ```javascript -const {app, Menu, Tray} = require('electron'); +const {app, Menu, Tray} = require('electron') -let appIcon = null; +let tray = null app.on('ready', () => { - appIcon = new Tray('/path/to/my/icon'); + tray = new Tray('/path/to/my/icon') const contextMenu = Menu.buildFromTemplate([ {label: 'Item1', type: 'radio'}, {label: 'Item2', type: 'radio'}, {label: 'Item3', type: 'radio', checked: true}, {label: 'Item4', type: 'radio'} ]); - appIcon.setToolTip('This is my application.'); - appIcon.setContextMenu(contextMenu); -}); + tray.setToolTip('This is my application.') + tray.setContextMenu(contextMenu) +}) ``` __Platform limitations:__ @@ -49,21 +49,18 @@ rely on the `click` event and always attach a context menu to the tray icon. Creates a new tray icon associated with the `image`. -## Events +### Instance Events The `Tray` module emits the following events: -**Note:** Some events are only available on specific operating systems and are -labeled as such. - -### Event: 'click' +#### Event: 'click' * `event` Event * `altKey` Boolean * `shiftKey` Boolean * `ctrlKey` Boolean * `metaKey` Boolean -* `bounds` Object - the bounds of tray icon. +* `bounds` Object _macOS_ _Windows_ - the bounds of tray icon. * `x` Integer * `y` Integer * `width` Integer @@ -71,9 +68,7 @@ labeled as such. Emitted when the tray icon is clicked. -**Note:** The `bounds` payload is only implemented on macOS and Windows. - -### Event: 'right-click' _macOS_ _Windows_ +#### Event: 'right-click' _macOS_ _Windows_ * `event` Event * `altKey` Boolean @@ -88,7 +83,7 @@ Emitted when the tray icon is clicked. Emitted when the tray icon is right clicked. -### Event: 'double-click' _macOS_ _Windows_ +#### Event: 'double-click' _macOS_ _Windows_ * `event` Event * `altKey` Boolean @@ -103,85 +98,82 @@ Emitted when the tray icon is right clicked. Emitted when the tray icon is double clicked. -### Event: 'balloon-show' _Windows_ +#### Event: 'balloon-show' _Windows_ Emitted when the tray balloon shows. -### Event: 'balloon-click' _Windows_ +#### Event: 'balloon-click' _Windows_ Emitted when the tray balloon is clicked. -### Event: 'balloon-closed' _Windows_ +#### Event: 'balloon-closed' _Windows_ Emitted when the tray balloon is closed because of timeout or user manually closes it. -### Event: 'drop' _macOS_ +#### Event: 'drop' _macOS_ Emitted when any dragged items are dropped on the tray icon. -### Event: 'drop-files' _macOS_ +#### Event: 'drop-files' _macOS_ -* `event` +* `event` Event * `files` Array - the file path of dropped files. Emitted when dragged files are dropped in the tray icon. -### Event: 'drag-enter' _macOS_ +#### Event: 'drag-enter' _macOS_ Emitted when a drag operation enters the tray icon. -### Event: 'drag-leave' _macOS_ +#### Event: 'drag-leave' _macOS_ Emitted when a drag operation exits the tray icon. -### Event: 'drag-end' _macOS_ +#### Event: 'drag-end' _macOS_ Emitted when a drag operation ends on the tray or ends at another location. -## Methods +### Instance Methods -The `Tray` module has the following methods: +The `Tray` class has the following methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. - -### `Tray.destroy()` +#### `tray.destroy()` Destroys the tray icon immediately. -### `Tray.setImage(image)` +#### `tray.setImage(image)` * `image` [NativeImage](native-image.md) Sets the `image` associated with this tray icon. -### `Tray.setPressedImage(image)` _macOS_ +#### `tray.setPressedImage(image)` _macOS_ * `image` [NativeImage](native-image.md) Sets the `image` associated with this tray icon when pressed on macOS. -### `Tray.setToolTip(toolTip)` +#### `tray.setToolTip(toolTip)` * `toolTip` String Sets the hover text for this tray icon. -### `Tray.setTitle(title)` _macOS_ +#### `tray.setTitle(title)` _macOS_ * `title` String Sets the title displayed aside of the tray icon in the status bar. -### `Tray.setHighlightMode(highlight)` _macOS_ +#### `tray.setHighlightMode(highlight)` _macOS_ * `highlight` Boolean Sets whether the tray icon's background becomes highlighted (in blue) when the tray icon is clicked. Defaults to true. -### `Tray.displayBalloon(options)` _Windows_ +#### `tray.displayBalloon(options)` _Windows_ * `options` Object * `icon` [NativeImage](native-image.md) @@ -190,7 +182,7 @@ when the tray icon is clicked. Defaults to true. Displays a tray balloon. -### `Tray.popUpContextMenu([menu, position])` _macOS_ _Windows_ +#### `tray.popUpContextMenu([menu, position])` _macOS_ _Windows_ * `menu` Menu (optional) * `position` Object (optional) - The pop up position. @@ -202,10 +194,20 @@ be shown instead of the tray icon's context menu. The `position` is only available on Windows, and it is (0, 0) by default. -### `Tray.setContextMenu(menu)` +#### `tray.setContextMenu(menu)` * `menu` Menu Sets the context menu for this icon. +#### `tray.getBounds()` _macOS_ _Windows_ + +Returns the `bounds` of this tray icon as `Object`. + +* `bounds` Object + * `x` Integer + * `y` Integer + * `width` Integer + * `height` Integer + [event-emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 56263780a08f..c3d175125b84 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -339,9 +339,9 @@ Returns: was invoked on. Elements with source URLs are images, audio and video. * `mediaType` String - Type of the node the context menu was invoked on. Can be `none`, `image`, `audio`, `video`, `canvas`, `file` or `plugin`. - * `hasImageContent` Boolean - Wether the context menu was invoked on an image + * `hasImageContent` Boolean - Whether the context menu was invoked on an image which has non-empty contents. - * `isEditable` Boolean - Wether the context is editable. + * `isEditable` Boolean - Whether the context is editable. * `selectionText` String - Text of the selection that the context menu was invoked on. * `titleText` String - Title or alt text of the selection that the context @@ -356,29 +356,31 @@ Returns: Can be `none`, `mouse`, `keyboard`, `touch`, `touchMenu`. * `mediaFlags` Object - The flags for the media element the context menu was invoked on. See more about this below. - * `editFlags` Object - These flags indicate wether the renderer believes it is + * `editFlags` Object - These flags indicate whether the renderer believes it is able to perform the corresponding action. See more about this below. The `mediaFlags` is an object with the following properties: - * `inError` Boolean - Wether the media element has crashed. - * `isPaused` Boolean - Wether the media element is paused. - * `isMuted` Boolean - Wether the media element is muted. - * `hasAudio` Boolean - Wether the media element has audio. - * `isLooping` Boolean - Wether the media element is looping. - * `isControlsVisible` Boolean - Wether the media element's controls are - visible. - * `canToggleControls` Boolean - Wether the media element's controls are - toggleable. - * `canRotate` Boolean - Wether the media element can be rotated. + +* `inError` Boolean - Whether the media element has crashed. +* `isPaused` Boolean - Whether the media element is paused. +* `isMuted` Boolean - Whether the media element is muted. +* `hasAudio` Boolean - Whether the media element has audio. +* `isLooping` Boolean - Whether the media element is looping. +* `isControlsVisible` Boolean - Whether the media element's controls are + visible. +* `canToggleControls` Boolean - Whether the media element's controls are + toggleable. +* `canRotate` Boolean - Whether the media element can be rotated. The `editFlags` is an object with the following properties: - * `canUndo` Boolean - Wether the renderer believes it can undo. - * `canRedo` Boolean - Wether the renderer believes it can redo. - * `canCut` Boolean - Wether the renderer believes it can cut. - * `canCopy` Boolean - Wether the renderer believes it can copy - * `canPaste` Boolean - Wether the renderer believes it can paste. - * `canDelete` Boolean - Wether the renderer believes it can delete. - * `canSelectAll` Boolean - Wether the renderer believes it can select all. + +* `canUndo` Boolean - Whether the renderer believes it can undo. +* `canRedo` Boolean - Whether the renderer believes it can redo. +* `canCut` Boolean - Whether the renderer believes it can cut. +* `canCopy` Boolean - Whether the renderer believes it can copy +* `canPaste` Boolean - Whether the renderer believes it can paste. +* `canDelete` Boolean - Whether the renderer believes it can delete. +* `canSelectAll` Boolean - Whether the renderer believes it can select all. Emitted when there is a new context menu that needs to be handled. @@ -919,7 +921,7 @@ For the `mouseWheel` event, the `event` object also have following properties: ### `webContents.beginFrameSubscription([onlyDirty ,]callback)` -* `onlyDirty` Boolean +* `onlyDirty` Boolean (optional) - Defaults to `false` * `callback` Function Begin subscribing for presentation events and captured frames, the `callback` @@ -941,6 +943,16 @@ defaults to `false`. End subscribing for frame presentation events. +### `webContents.startDrag(item)` + +* `item` object + * `file` String + * `icon` [NativeImage](native-image.md) + +Sets the `item` as dragging item for current drag-drop operation, `file` is the +absolute path of the file to be dragged, and `icon` is the image showing under +the cursor when dragging. + ### `webContents.savePage(fullPath, saveType, callback)` * `fullPath` String - The full file path. diff --git a/docs/development/atom-shell-vs-node-webkit.md b/docs/development/atom-shell-vs-node-webkit.md index 3ec11b78b7d1..0912a8d66e31 100644 --- a/docs/development/atom-shell-vs-node-webkit.md +++ b/docs/development/atom-shell-vs-node-webkit.md @@ -47,4 +47,6 @@ By using the [multi-context](http://strongloop.com/strongblog/whats-new-node-js- feature of Node, Electron doesn't introduce a new JavaScript context in web pages. +Note: NW.js has optionally supported multi-context since 0.13. + [node-bindings]: https://github.com/electron/electron/tree/master/atom/common diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index b3c2c4573462..7a7e1ee6c6e9 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -12,7 +12,7 @@ Follow the guidelines below for building Electron on Windows. * [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) +[dev.microsoftedge.com](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/) 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/styleguide.md b/docs/styleguide.md index 44afe2aadb81..ba7d707fd89f 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -1,98 +1,240 @@ # Electron Documentation Styleguide -Find the appropriate section for your task: [reading Electron documentation](#reading-electron-documentation) -or [writing Electron documentation](#writing-electron-documentation). +These are the guidelines for writing Electron documentation. -## Writing Electron Documentation +## Titles -These are the ways that we construct the Electron documentation. +* Each page must have a single `#`-level title at the top. +* Chapters in the same page must have `##`-level titles. +* Sub-chapters need to increase the number of `#` in the title according to + their nesting depth. +* All words in the page's title must be capitalized, except for conjunctions + like "of" and "and" . +* Only the first word of a chapter title must be capitalized. -- Maximum one `h1` title per page. -- Use `bash` instead of `cmd` in code blocks (because of syntax highlighter). -- Doc `h1` titles should match object name (i.e. `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)` _macOS_``` -- Prefer 'in the ___ process' over 'on' +Using `Quick Start` as example: -### Documentation Translations +```markdown +# Quick Start + +... + +## Main process + +... + +## Renderer process + +... + +## Run your app + +... + +### Run as a distribution + +... + +### Manually downloaded Electron binary + +... +``` + +For API references, there are exceptions to this rule. + +## Markdown rules + +* Use `bash` instead of `cmd` in code blocks (due to the syntax highlighter). +* Lines should be wrapped at 80 columns. +* No nesting lists more than 2 levels (due to the markdown renderer). + +## Picking words + +* Use "will" over "would" when describing outcomes. +* Prefer "in the ___ process" over "on". + +## API references + +The following rules only apply to the documentation of APIs. + +### Page title + +Each page must use the actual object name returned by `require('electron')` +as the title, such as `BrowserWindow`, `autoUpdater`, and `session`. + +Under the page tile must be a one-line description starting with `>`. + +Using `session` as example: + +```markdown +# session + +> Manage browser sessions, cookies, cache, proxy settings, etc. +``` + +### Module methods and events + +For modules that are not classes, their methods and events must be listed under +the `## Methods` and `## Events` chapters. + +Using `autoUpdater` as an example: + +```markdown +# autoUpdater + +## Events + +### Event: 'error' + +## Methods + +### `autoUpdater.setFeedURL(url[, requestHeaders])` +``` + +### Classes + +* API classes or classes that are part of modules must be listed under a + `## Class: TheClassName` chapter. +* One page can have multiple classes. +* The constructors must be listed with `###`-level titles. +* The methods must be listed under an `### Instance Methods` chapter. +* The events must be listed under an `### Instance Events` chapter. +* The properties must be listed under an `### Instance Properties` chapter. + +Using the `Session` and `Cookies` classes as an example: + +```markdown +# session + +## Methods + +### session.fromPartition(partition) + +## Properties + +### session.defaultSession + +## Class: Session + +### Instance Events + +#### Event: 'will-download' + +### Instance Methods + +#### `ses.getCacheSize(callback)` + +### Instance Properties + +#### `ses.cookies` + +## Class: Cookies + +### Instance Methods + +#### `cookies.get(filter, callback)` +``` + +### Methods + +The methods chapter must be in the following form: + +```markdown +### `objectName.methodName(required[, optional]))` + +* `required` String +* `optional` Integer (optional) + +... +``` + +The title can be `###` or `####`-levels depending on whether it is a method of +a module or a class. + +For modules, the `objectName` is the module's name. For classes, it must be the +name of the instance of the class, and must not be the same as the module's +name. + +For example, the methods of the `Session` class under the `session` module must +use `ses` as the `objectName`. + +The optional arguments are notated by square brackets `[]` surrounding the optional argument +as well as the comma required if this optional argument follows another +argument: + +``` +required[, optional] +``` + +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) +* [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean) +* Or a custom type like Electron's [`WebContent`](api/web-content.md) + +If an argument or a method is unique to certain platforms, those platforms are +denoted using a space-delimited italicized list following the datatype. Values +can be `macOS`, `Windows`, or `Linux`. + +```markdown +* `animate` Boolean (optional) _macOS_ _Windows_ +``` + +`Array` type arguments must specify what elements the array may include in +the description below. + +The description for `Function` type arguments should make it clear how it may be +called and list the types of the parameters that will be passed to it. + +### Events + +The events chapter must be in following form: + +```markdown +### Event: 'wake-up' + +Returns: + +* `time` String + +... +``` + +The title can be `###` or `####`-levels depending on whether it is an event of +a module or a class. + +The arguments of an event follow the same rules as methods. + +### Properties + +The properties chapter must be in following form: + +```markdown +### session.defaultSession + +... +``` + +The title can be `###` or `####`-levels depending on whether it is a property of +a module or a class. + +## Documentation Translations Translations of the Electron docs are located within the `docs-translations` directory. 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 +* Create a subdirectory named by language abbreviation. +* 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). +* Add a link to your translation directory on the main Electron + [README](https://github.com/electron/electron#documentation-translations). -## Reading Electron Documentation - -Here are some tips for understanding Electron documentation syntax. - -### Methods - -An example of [method](https://developer.mozilla.org/en-US/docs/Glossary/Method) -documentation: - ---- - -`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`](api/web-content.md). - -### Events - -An example of [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) -documentation: - ---- - -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', (time) => { - console.log(time); -}); -``` +Note that the files under `docs-translations` must only include the translated +ones, the original English files should not be copied there. diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index 714b7100cf47..3c153061a7e8 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -11,7 +11,7 @@ Use the following command line switches to debug Electron's main process: ### `--debug=[port]` When this switch is used Electron will listen for V8 debugger protocol -messages on `port`. The default `port` is `5858`. +messages on the `port`. The default `port` is `5858`. ### `--debug-brk=[port]` @@ -19,11 +19,11 @@ Like `--debug` but pauses the script on the first line. ## Use node-inspector for Debugging -**Note:** Electron doesn't currently work very well -with node-inspector, and the main process will crash if you inspect the -`process` object under node-inspector's console. +**Note:** Electron doesn't currently work very well with node-inspector, and the +main process will crash if you inspect the `process` object under +node-inspector's console. -### 1. Make sure you have [node-gyp required tools][node-gyp-required-tools] installed +### 1. Install the [node-gyp required tools][node-gyp-required-tools] ### 2. Install [node-inspector][node-inspector] @@ -31,17 +31,19 @@ with node-inspector, and the main process will crash if you inspect the $ npm install node-inspector ``` -### 3. Install a patched version of `node-pre-gyp` +### 3. Install [node-pre-gyp][node-pre-gyp] ```bash -$ npm install git+https://git@github.com/enlight/node-pre-gyp.git#detect-electron-runtime-in-find +$ npm install node-pre-gyp ``` -### 4. Recompile the `node-inspector` `v8` modules for electron (change the target to your electron version number) +### 4. Recompile the `node-inspector` `v8` modules for Electron + +**Note:** Update the target argument to be your Electron version number ```bash -$ node_modules/.bin/node-pre-gyp --target=0.36.11 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall -$ node_modules/.bin/node-pre-gyp --target=0.36.11 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall ``` See also [How to install native modules][how-to-install-native-modules]. @@ -60,7 +62,7 @@ or, to pause your script on the first line: $ electron --debug-brk=5858 your/app ``` -### 6. Start the [node-inspector][node-inspector] server using electron +### 6. Start the [node-inspector][node-inspector] server using Electron ```bash $ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin/inspector.js @@ -69,9 +71,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 +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-pre-gyp]: https://github.com/mapbox/node-pre-gyp [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 adb08ad5dc40..ab84681609e1 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -321,6 +321,35 @@ win.setRepresentedFilename('/etc/passwd'); win.setDocumentEdited(true); ``` +## Dragging files out of the window + +For certain kinds of apps that manipulate on files, it is important to be able +to drag files from Electron to other apps. To implement this feature in your +app, you need to call `webContents.startDrag(item)` API on `ondragstart` event. + +In web page: + +```html +item + +``` + +In the main process: + +```javascript +ipcMain.on('ondragstart', (event, filePath) => { + event.sender.startDrag({ + file: filePath, + icon: '/path/to/icon.png' + }) +}) +``` + [addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows [clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows [setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 59bfb4bb26b7..59a019596ba9 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -211,7 +211,7 @@ and then executing the packaged app. ### Try this Example -Clone and run the code in this tutorial by using the [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start) +Clone and run the code in this tutorial by using the [`electron/electron-quick-start`](https://github.com/electron/electron-quick-start) repository. **Note**: Running this requires [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which includes [npm](https://npmjs.org)) on your system. diff --git a/electron.gyp b/electron.gyp index 09c1e279793c..cfa96eae2286 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.2.3', + 'version%': '1.2.6', }, 'includes': [ 'filenames.gypi', diff --git a/filenames.gypi b/filenames.gypi index 7c655b86ced8..96eff4aa0972 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -21,6 +21,7 @@ 'lib/browser/api/ipc-main.js', 'lib/browser/api/menu.js', 'lib/browser/api/menu-item.js', + 'lib/browser/api/menu-item-roles.js', 'lib/browser/api/navigation-controller.js', 'lib/browser/api/power-monitor.js', 'lib/browser/api/power-save-blocker.js', @@ -246,6 +247,9 @@ 'atom/browser/ui/atom_menu_model.h', 'atom/browser/ui/cocoa/atom_menu_controller.h', 'atom/browser/ui/cocoa/atom_menu_controller.mm', + 'atom/browser/ui/drag_util_mac.mm', + 'atom/browser/ui/drag_util_views.cc', + 'atom/browser/ui/drag_util.h', 'atom/browser/ui/file_dialog.h', 'atom/browser/ui/file_dialog_gtk.cc', 'atom/browser/ui/file_dialog_mac.mm', diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 09835a792e0e..0ea788b94a71 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -1,12 +1,15 @@ 'use strict' +const bindings = process.atomBinding('app') +const {app} = bindings + +// Only one app object permitted. +module.exports = app + const electron = require('electron') const {deprecate, Menu} = electron const {EventEmitter} = require('events') -const bindings = process.atomBinding('app') -const {app} = bindings - Object.setPrototypeOf(app, EventEmitter.prototype) let appPath = null @@ -42,6 +45,15 @@ if (process.platform === 'darwin') { } } +if (process.platform === 'linux') { + app.launcher = { + setBadgeCount: bindings.unityLauncherSetBadgeCount, + getBadgeCount: bindings.unityLauncherGetBadgeCount, + isCounterBadgeAvailable: bindings.unityLauncherAvailable, + isUnityRunning: bindings.unityLauncherAvailable + } +} + app.allowNTLMCredentialsForAllDomains = function (allow) { if (!process.noDeprecations) { deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains') @@ -67,6 +79,3 @@ process.atomBinding('download_item')._setWrapDownloadItem((downloadItem) => { // downloadItem is an EventEmitter. Object.setPrototypeOf(downloadItem, EventEmitter.prototype) }) - -// Only one App object pemitted. -module.exports = app diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js new file mode 100644 index 000000000000..082099818fdc --- /dev/null +++ b/lib/browser/api/menu-item-roles.js @@ -0,0 +1,147 @@ +const {app} = require('electron') + +const roles = { + about: { + get label () { + return process.platform === 'linux' ? 'About' : `About ${app.getName()}` + } + }, + close: { + label: 'Close', + accelerator: 'CommandOrControl+W', + windowMethod: 'close' + }, + copy: { + label: 'Copy', + accelerator: 'CommandOrControl+C', + webContentsMethod: 'copy' + }, + cut: { + label: 'Cut', + accelerator: 'CommandOrControl+X', + webContentsMethod: 'cut' + }, + delete: { + label: 'Delete', + webContentsMethod: 'delete' + }, + front: { + label: 'Bring All to Front' + }, + help: { + label: 'Help' + }, + hide: { + get label () { + return `Hide ${app.getName()}` + }, + accelerator: 'Command+H' + }, + hideothers: { + label: 'Hide Others', + accelerator: 'Command+Alt+H' + }, + minimize: { + label: 'Minimize', + accelerator: 'CommandOrControl+M', + windowMethod: 'minimize' + }, + paste: { + label: 'Paste', + accelerator: 'CommandOrControl+V', + webContentsMethod: 'paste' + }, + pasteandmatchstyle: { + label: 'Paste and Match Style', + accelerator: 'Shift+CommandOrControl+V', + webContentsMethod: 'pasteAndMatchStyle' + }, + quit: { + get label () { + switch (process.platform) { + case 'darwin': return `Quit ${app.getName()}` + case 'win32': return 'Exit' + default: return 'Quit' + } + }, + accelerator: process.platform === 'win32' ? null : 'Command+Q', + appMethod: 'quit' + }, + redo: { + label: 'Redo', + accelerator: 'Shift+CommandOrControl+Z', + webContentsMethod: 'redo' + }, + selectall: { + label: 'Select All', + accelerator: 'CommandOrControl+A', + webContentsMethod: 'selectAll' + }, + services: { + label: 'Services' + }, + togglefullscreen: { + label: 'Toggle Full Screen', + accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11', + windowMethod: function (window) { + window.setFullScreen(!window.isFullScreen()) + } + }, + undo: { + label: 'Undo', + accelerator: 'CommandOrControl+Z', + webContentsMethod: 'undo' + }, + unhide: { + label: 'Show All' + }, + window: { + label: 'Window' + }, + zoom: { + label: 'Zoom' + } +} + +exports.getDefaultLabel = (role) => { + if (roles.hasOwnProperty(role)) { + return roles[role].label + } else { + return '' + } +} + +exports.getDefaultAccelerator = (role) => { + if (roles.hasOwnProperty(role)) return roles[role].accelerator +} + +exports.execute = (role, focusedWindow) => { + if (!roles.hasOwnProperty(role)) return false + if (process.platform === 'darwin') return false + + const {appMethod, webContentsMethod, windowMethod} = roles[role] + + if (appMethod) { + app[appMethod]() + return true + } + + if (windowMethod && focusedWindow != null) { + if (typeof windowMethod === 'function') { + windowMethod(focusedWindow) + } else { + focusedWindow[windowMethod]() + } + return true + } + + if (webContentsMethod && focusedWindow != null) { + const {webContents} = focusedWindow + if (webContents) { + webContents[webContentsMethod]() + } + return true + } + + return false +} diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 4d2737d0fa98..8fb1b51d1e65 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -1,115 +1,87 @@ 'use strict' -var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap +const roles = require('./menu-item-roles') -nextCommandId = 0 +let nextCommandId = 0 -// Maps role to methods of webContents -rolesMap = { - undo: 'undo', - redo: 'redo', - cut: 'cut', - copy: 'copy', - paste: 'paste', - pasteandmatchstyle: 'pasteAndMatchStyle', - selectall: 'selectAll', - minimize: 'minimize', - close: 'close', - delete: 'delete' -} +const MenuItem = function (options) { + const {Menu} = require('electron') -// Maps methods that should be called directly on the BrowserWindow instance -methodInBrowserWindow = { - minimize: true, - close: true -} + this.selector = options.selector + this.type = options.type + this.role = options.role + this.label = options.label + this.sublabel = options.sublabel + this.accelerator = options.accelerator + this.icon = options.icon + this.enabled = options.enabled + this.visible = options.visible + this.checked = options.checked -MenuItem = (function () { - MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] + this.submenu = options.submenu + if (this.submenu != null && this.submenu.constructor !== Menu) { + this.submenu = Menu.buildFromTemplate(this.submenu) + } + if (this.type == null && this.submenu != null) { + this.type = 'submenu' + } + if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) { + throw new Error('Invalid submenu') + } - function MenuItem (options) { - var click, ref - const Menu = require('electron').Menu - click = options.click - this.selector = options.selector - this.type = options.type - this.role = options.role - this.label = options.label - this.sublabel = options.sublabel - this.accelerator = options.accelerator - this.icon = options.icon - this.enabled = options.enabled - this.visible = options.visible - this.checked = options.checked - this.submenu = options.submenu - if ((this.submenu != null) && this.submenu.constructor !== Menu) { - this.submenu = Menu.buildFromTemplate(this.submenu) + this.overrideReadOnlyProperty('type', 'normal') + this.overrideReadOnlyProperty('role') + this.overrideReadOnlyProperty('accelerator', roles.getDefaultAccelerator(this.role)) + this.overrideReadOnlyProperty('icon') + this.overrideReadOnlyProperty('submenu') + + this.overrideProperty('label', roles.getDefaultLabel(this.role)) + this.overrideProperty('sublabel', '') + this.overrideProperty('enabled', true) + this.overrideProperty('visible', true) + this.overrideProperty('checked', false) + + if (!MenuItem.types.includes(this.type)) { + throw new Error(`Unknown menu item type: ${this.type}`) + } + + this.overrideReadOnlyProperty('commandId', ++nextCommandId) + + const click = options.click + this.click = (event, focusedWindow) => { + // Manually flip the checked flags when clicked. + if (this.type === 'checkbox' || this.type === 'radio') { + this.checked = !this.checked } - if ((this.type == null) && (this.submenu != null)) { - this.type = 'submenu' - } - if (this.type === 'submenu' && ((ref = this.submenu) != null ? ref.constructor : void 0) !== Menu) { - throw new Error('Invalid submenu') - } - this.overrideReadOnlyProperty('type', 'normal') - this.overrideReadOnlyProperty('role') - this.overrideReadOnlyProperty('accelerator') - this.overrideReadOnlyProperty('icon') - this.overrideReadOnlyProperty('submenu') - this.overrideProperty('label', '') - this.overrideProperty('sublabel', '') - this.overrideProperty('enabled', true) - this.overrideProperty('visible', true) - this.overrideProperty('checked', false) - if (MenuItem.types.indexOf(this.type) === -1) { - throw new Error('Unknown menu type ' + this.type) - } - this.commandId = ++nextCommandId - this.click = (focusedWindow) => { - // Manually flip the checked flags when clicked. - var methodName, ref1, ref2 - if ((ref1 = this.type) === 'checkbox' || ref1 === 'radio') { - this.checked = !this.checked - } - if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && (focusedWindow != null)) { - methodName = rolesMap[this.role] - if (methodInBrowserWindow[methodName]) { - return focusedWindow[methodName]() - } else { - return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0 - } - } else if (typeof click === 'function') { - return click(this, focusedWindow) + + if (!roles.execute(this.role, focusedWindow)) { + if (typeof click === 'function') { + click(this, focusedWindow, event) } else if (typeof this.selector === 'string' && process.platform === 'darwin') { - return Menu.sendActionToFirstResponder(this.selector) + Menu.sendActionToFirstResponder(this.selector) } } } +} - MenuItem.prototype.overrideProperty = function (name, defaultValue) { - if (defaultValue == null) { - defaultValue = null - } - this[name] != null ? this[name] : this[name] = defaultValue +MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] - return this[name] +MenuItem.prototype.overrideProperty = function (name, defaultValue) { + if (defaultValue == null) { + defaultValue = null } - - MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { - if (defaultValue == null) { - defaultValue = null - } - if (this[name] == null) { - this[name] = defaultValue - } - return Object.defineProperty(this, name, { - enumerable: true, - writable: false, - value: this[name] - }) + if (this[name] == null) { + this[name] = defaultValue } +} - return MenuItem -})() +MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { + this.overrideProperty(name, defaultValue) + Object.defineProperty(this, name, { + enumerable: true, + writable: false, + value: this[name] + }) +} module.exports = MenuItem diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index b122fc36cf37..3b1082193639 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -114,9 +114,9 @@ Menu.prototype._init = function () { var command = this.commandsMap[commandId] return command != null ? command.icon : undefined }, - executeCommand: (commandId) => { + executeCommand: (event, commandId) => { var command = this.commandsMap[commandId] - return command != null ? command.click(BrowserWindow.getFocusedWindow()) : undefined + return command != null ? command.click(event, BrowserWindow.getFocusedWindow()) : undefined }, menuWillShow: () => { // Make sure radio groups have at least one menu item seleted. diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js index e3cd4540780e..4bb4a51c191d 100644 --- a/lib/browser/chrome-extension.js +++ b/lib/browser/chrome-extension.js @@ -72,21 +72,29 @@ const backgroundPages = {} const startBackgroundPages = function (manifest) { if (backgroundPages[manifest.extensionId] || !manifest.background) return - const scripts = manifest.background.scripts.map((name) => { - return `` - }).join('') - const html = new Buffer(`${scripts}`) + let html + let name + if (manifest.background.page) { + name = manifest.background.page + html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)) + } else { + name = '_generated_background_page.html' + const scripts = manifest.background.scripts.map((name) => { + return `` + }).join('') + html = new Buffer(`${scripts}`) + } const contents = webContents.create({ isBackgroundPage: true, commandLineSwitches: ['--background-page'] }) - backgroundPages[manifest.extensionId] = { html: html, webContents: contents } + backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name } contents.loadURL(url.format({ protocol: 'chrome-extension', slashes: true, hostname: manifest.extensionId, - pathname: '_generated_background_page.html' + pathname: name })) } @@ -309,11 +317,11 @@ app.once('ready', function () { const manifest = manifestMap[parsed.hostname] if (!manifest) return callback() - if (parsed.path === '/_generated_background_page.html' && - backgroundPages[parsed.hostname]) { + const page = backgroundPages[parsed.hostname] + if (page && parsed.path === `/${page.name}`) { return callback({ mimeType: 'text/html', - data: backgroundPages[parsed.hostname].html + data: page.html }) } diff --git a/lib/browser/init.js b/lib/browser/init.js index 924ad1420c2e..68a555ad5858 100644 --- a/lib/browser/init.js +++ b/lib/browser/init.js @@ -119,7 +119,7 @@ for (i = 0, len = searchPaths.length; i < len; i++) { packagePath = searchPaths[i] try { packagePath = path.join(process.resourcesPath, packagePath) - packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json'))) + packageJson = require(path.join(packagePath, 'package.json')) break } catch (error) { continue @@ -173,6 +173,9 @@ require('./chrome-extension') // Load internal desktop-capturer module. require('./desktop-capturer') +// Load protocol module to ensure it is populated on app ready +require('./api/protocol') + // Set main startup script of the app. var mainStartupScript = packageJson.main || 'index.js' diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index 0b954e518b95..083c5e4d03e0 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -54,7 +54,7 @@ let getObjectPrototype = function (object) { // Convert a real value into meta data. let valueToMeta = function (sender, value, optimizeSimpleObject = false) { // Determine the type of value. - let meta = { type: typeof value } + const meta = { type: typeof value } if (meta.type === 'object') { // Recognize certain types of objects. if (value === null) { @@ -93,6 +93,10 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) { } else if (meta.type === 'buffer') { meta.value = Array.prototype.slice.call(value, 0) } else if (meta.type === 'promise') { + // Add default handler to prevent unhandled rejections in main process + // Instead they should appear in the renderer process + value.then(function () {}, function () {}) + meta.then = valueToMeta(sender, function (onFulfilled, onRejected) { value.then(onFulfilled, onRejected) }) @@ -114,7 +118,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) { } // Convert object to meta by value. -var plainObjectToMeta = function (obj) { +const plainObjectToMeta = function (obj) { return Object.getOwnPropertyNames(obj).map(function (name) { return { name: name, @@ -124,7 +128,7 @@ var plainObjectToMeta = function (obj) { } // Convert Error into meta data. -var exceptionToMeta = function (error) { +const exceptionToMeta = function (error) { return { type: 'exception', message: error.message, @@ -133,10 +137,9 @@ var exceptionToMeta = function (error) { } // Convert array of meta data from renderer into array of real values. -var unwrapArgs = function (sender, args) { - var metaToValue - metaToValue = function (meta) { - var i, len, member, ref, returnValue +const unwrapArgs = function (sender, args) { + const metaToValue = function (meta) { + let i, len, member, ref, returnValue switch (meta.type) { case 'value': return meta.value @@ -200,8 +203,8 @@ var unwrapArgs = function (sender, args) { // Call a function and send reply asynchronously if it's a an asynchronous // style function and the caller didn't pass a callback. -var callFunction = function (event, func, caller, args) { - var funcMarkedAsync, funcName, funcPassedCallback, ref, ret +const callFunction = function (event, func, caller, args) { + let funcMarkedAsync, funcName, funcPassedCallback, ref, ret funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous') funcPassedCallback = typeof args[args.length - 1] === 'function' try { @@ -209,7 +212,7 @@ var callFunction = function (event, func, caller, args) { args.push(function (ret) { event.returnValue = valueToMeta(event.sender, ret, true) }) - return func.apply(caller, args) + func.apply(caller, args) } else { ret = func.apply(caller, args) event.returnValue = valueToMeta(event.sender, ret, true) diff --git a/lib/common/api/callbacks-registry.js b/lib/common/api/callbacks-registry.js index 7a9bf534f72d..459c392bc188 100644 --- a/lib/common/api/callbacks-registry.js +++ b/lib/common/api/callbacks-registry.js @@ -55,7 +55,11 @@ class CallbacksRegistry { } remove (id) { - return delete this.callbacks[id] + const callback = this.callbacks[id] + if (callback) { + v8Util.deleteHiddenValue(callback, 'callbackId') + delete this.callbacks[id] + } } } diff --git a/lib/common/reset-search-paths.js b/lib/common/reset-search-paths.js index 4d09f6c89ca6..924cb388d4d9 100644 --- a/lib/common/reset-search-paths.js +++ b/lib/common/reset-search-paths.js @@ -9,31 +9,19 @@ module.paths = [] module.parent.paths = [] // Prevent Node from adding paths outside this app to search paths. +const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep +const originalNodeModulePaths = Module._nodeModulePaths Module._nodeModulePaths = function (from) { - from = path.resolve(from) - + const paths = originalNodeModulePaths(from) + const fromPath = path.resolve(from) + path.sep // If "from" is outside the app then we do nothing. - const skipOutsidePaths = from.startsWith(process.resourcesPath) - - // Following logic is copied from module.js. - const splitRe = process.platform === 'win32' ? /[\/\\]/ : /\// - const paths = [] - const parts = from.split(splitRe) - - let tip - let i - for (tip = i = parts.length - 1; i >= 0; tip = i += -1) { - const part = parts[tip] - if (part === 'node_modules') { - continue - } - const dir = parts.slice(0, tip + 1).join(path.sep) - if (skipOutsidePaths && !dir.startsWith(process.resourcesPath)) { - break - } - paths.push(path.join(dir, 'node_modules')) + if (fromPath.startsWith(resourcesPathWithTrailingSlash)) { + return paths.filter(function (candidate) { + return candidate.startsWith(resourcesPathWithTrailingSlash) + }) + } else { + return paths } - return paths } // Patch Module._resolveFilename to always require the Electron API when diff --git a/lib/renderer/chrome-api.js b/lib/renderer/chrome-api.js index 6c28c98edf44..b49d7382c041 100644 --- a/lib/renderer/chrome-api.js +++ b/lib/renderer/chrome-api.js @@ -119,7 +119,13 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) { if (args.length === 1) { message = args[0] } else if (args.length === 2) { - [targetExtensionId, message] = args + // A case of not provide extension-id: (message, responseCallback) + if (typeof args[1] === 'function') { + console.error('responseCallback is not supported') + message = args[0] + } else { + [targetExtensionId, message] = args + } } else { console.error('options and responseCallback are not supported') } diff --git a/lib/renderer/extensions/storage.js b/lib/renderer/extensions/storage.js index 7b880f08310e..af3978e64846 100644 --- a/lib/renderer/extensions/storage.js +++ b/lib/renderer/extensions/storage.js @@ -1,5 +1,5 @@ -const getStorage = () => { - const data = window.localStorage.getItem('__chrome.storage.sync__') +const getStorage = (storageType) => { + const data = window.localStorage.getItem(`__chrome.storage.${storageType}__`) if (data != null) { return JSON.parse(data) } else { @@ -7,16 +7,22 @@ const getStorage = () => { } } -const setStorage = (storage) => { +const setStorage = (storageType, storage) => { const json = JSON.stringify(storage) - window.localStorage.setItem('__chrome.storage.sync__', json) + window.localStorage.setItem(`__chrome.storage.${storageType}__`, json) } -module.exports = { - sync: { +const scheduleCallback = (items, callback) => { + setTimeout(function () { + callback(items) + }) +} + +const getStorageManager = (storageType) => { + return { get (keys, callback) { - const storage = getStorage() - if (keys == null) return storage + const storage = getStorage(storageType) + if (keys == null) return scheduleCallback(storage, callback) let defaults = {} switch (typeof keys) { @@ -30,7 +36,7 @@ module.exports = { } break } - if (keys.length === 0) return {} + if (keys.length === 0) return scheduleCallback({}, callback) let items = {} keys.forEach(function (key) { @@ -38,22 +44,24 @@ module.exports = { if (value == null) value = defaults[key] items[key] = value }) - - setTimeout(function () { - callback(items) - }) + scheduleCallback(items, callback) }, set (items, callback) { - const storage = getStorage() + const storage = getStorage(storageType) Object.keys(items).forEach(function (name) { storage[name] = items[name] }) - setStorage(storage) + setStorage(storageType, storage) setTimeout(callback) } } } + +module.exports = { + sync: getStorageManager('sync'), + local: getStorageManager('local') +} diff --git a/package.json b/package.json index f4eb3eb287aa..d8e71d36b493 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "electron", - "version": "1.2.3", + "version": "1.2.6", "devDependencies": { "asar": "^0.11.0", "request": "*", - "standard": "^7.1.0" + "standard": "^7.1.2" }, "optionalDependencies": { "runas": "^3.0.0" @@ -24,7 +24,7 @@ "bootstrap": "python ./script/bootstrap.py", "build": "python ./script/build.py -c D", "lint": "npm run lint-js && npm run lint-cpp", - "lint-js": "standard && standard spec", + "lint-js": "standard && cd spec && standard", "lint-cpp": "python ./script/cpplint.py", "preinstall": "node -e 'process.exit(0)'", "repl": "python ./script/start.py --interactive", diff --git a/script/create-dist.py b/script/create-dist.py index 3f95cf2281d6..d2862e90f501 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -152,6 +152,10 @@ def create_symbols(): dsyms = glob.glob(os.path.join(OUT_DIR, '*.dSYM')) for dsym in dsyms: shutil.copytree(dsym, os.path.join(DIST_DIR, os.path.basename(dsym))) + elif PLATFORM == 'win32': + pdbs = glob.glob(os.path.join(OUT_DIR, '*.pdb')) + for pdb in pdbs: + shutil.copy2(pdb, DIST_DIR) def create_dist_zip(): @@ -223,6 +227,14 @@ def create_symbols_zip(): with scoped_cwd(DIST_DIR): dsyms = glob.glob('*.dSYM') make_zip(os.path.join(DIST_DIR, dsym_name), licenses, dsyms) + elif PLATFORM == 'win32': + pdb_name = '{0}-{1}-{2}-{3}-pdb.zip'.format(PROJECT_NAME, + ELECTRON_VERSION, + get_platform_key(), + get_target_arch()) + with scoped_cwd(DIST_DIR): + pdbs = glob.glob('*.pdb') + make_zip(os.path.join(DIST_DIR, pdb_name), pdbs + licenses, []) if __name__ == '__main__': diff --git a/script/lib/config.py b/script/lib/config.py index 125cecb5a35e..08297c58a272 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -8,7 +8,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = '2932c73bec8852c8cdf4b18f8b6372075c3e651e' +LIBCHROMIUMCONTENT_COMMIT = '31144d583c19b70d8d9de7d4ef15f0f720779860' PLATFORM = { 'cygwin': 'win32', diff --git a/script/update-external-binaries.py b/script/update-external-binaries.py index 470f735fdb42..4494df252629 100755 --- a/script/update-external-binaries.py +++ b/script/update-external-binaries.py @@ -8,7 +8,7 @@ from lib.config import get_target_arch from lib.util import safe_mkdir, rm_rf, extract_zip, tempdir, download -VERSION = 'v1.0.0' +VERSION = 'v1.1.0' SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) FRAMEWORKS_URL = 'http://github.com/electron/electron-frameworks/releases' \ '/download/' + VERSION diff --git a/script/upload.py b/script/upload.py index 9faf4fa8d05b..1abd67aaf0d2 100755 --- a/script/upload.py +++ b/script/upload.py @@ -35,6 +35,10 @@ DSYM_NAME = '{0}-{1}-{2}-{3}-dsym.zip'.format(PROJECT_NAME, ELECTRON_VERSION, get_platform_key(), get_target_arch()) +PDB_NAME = '{0}-{1}-{2}-{3}-pdb.zip'.format(PROJECT_NAME, + ELECTRON_VERSION, + get_platform_key(), + get_target_arch()) def main(): @@ -85,6 +89,8 @@ def main(): upload_electron(github, release, os.path.join(DIST_DIR, SYMBOLS_NAME)) if PLATFORM == 'darwin': upload_electron(github, release, os.path.join(DIST_DIR, DSYM_NAME)) + elif PLATFORM == 'win32': + upload_electron(github, release, os.path.join(DIST_DIR, PDB_NAME)) # Upload free version of ffmpeg. ffmpeg = 'ffmpeg-{0}-{1}-{2}.zip'.format( diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index d2bda5794ae5..111d387d3404 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -7,7 +7,6 @@ const path = require('path') const {remote} = require('electron') const {app, BrowserWindow, ipcMain} = remote -const isCI = remote.getGlobal('isCi') describe('electron module', function () { it('does not expose internal modules to require', function () { @@ -44,7 +43,6 @@ describe('electron module', function () { window.loadURL('file://' + path.join(__dirname, 'fixtures', 'api', 'electron-module-app', 'index.html')) }) }) - }) describe('app module', function () { @@ -111,12 +109,10 @@ describe('app module', function () { describe('app.relaunch', function () { let server = null - const socketPath = process.platform === 'win32' ? - '\\\\.\\pipe\\electron-app-relaunch' : - '/tmp/electron-app-relaunch' + const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch' beforeEach(function (done) { - fs.unlink(socketPath, (error) => { + fs.unlink(socketPath, () => { server = net.createServer() server.listen(socketPath) done() @@ -128,7 +124,7 @@ describe('app module', function () { if (process.platform === 'win32') { done() } else { - fs.unlink(socketPath, (error) => { + fs.unlink(socketPath, () => { done() }) } @@ -164,14 +160,13 @@ describe('app module', function () { } it('sets the current activity', function () { - app.setUserActivity('com.electron.testActivity', {testData: '123'}); - assert.equal(app.getCurrentActivityType(), 'com.electron.testActivity'); + app.setUserActivity('com.electron.testActivity', {testData: '123'}) + assert.equal(app.getCurrentActivityType(), 'com.electron.testActivity') }) }) describe('app.importCertificate', function () { - if (process.platform !== 'linux') - return + if (process.platform !== 'linux') return this.timeout(5000) @@ -190,8 +185,8 @@ describe('app module', function () { var server = https.createServer(options, function (req, res) { if (req.client.authorized) { - res.writeHead(200); - res.end('authorized'); + res.writeHead(200) + res.end('authorized') } }) @@ -289,4 +284,18 @@ describe('app module', function () { }) }) }) + + describe('app.setBadgeCount API', function () { + const shouldFail = process.platform === 'win32' || + (process.platform === 'linux' && !app.isUnityRunning()) + + it('returns false when failed', function () { + assert.equal(app.setBadgeCount(42), !shouldFail) + }) + + it('should set a badge count', function () { + app.setBadgeCount(42) + assert.equal(app.getBadgeCount(), shouldFail ? 0 : 42) + }) + }) }) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index b5f55905589a..2bc55484dbcc 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -23,9 +23,9 @@ describe('browser-window module', function () { before(function (done) { server = http.createServer(function (req, res) { - function respond() { res.end(''); } + 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() @@ -44,7 +44,10 @@ describe('browser-window module', function () { w = new BrowserWindow({ show: false, width: 400, - height: 400 + height: 400, + webPreferences: { + backgroundThrottling: false + } }) }) @@ -125,7 +128,7 @@ describe('browser-window module', function () { 'did-get-response-details.html': 'mainFrame', 'logo.png': 'image' } - var responses = 0; + var responses = 0 w.webContents.on('did-get-response-details', function (event, status, newUrl, oldUrl, responseCode, method, referrer, headers, resourceType) { responses++ var fileName = newUrl.slice(newUrl.lastIndexOf('/') + 1) @@ -287,7 +290,7 @@ describe('browser-window module', function () { describe('BrowserWindow.setAspectRatio(ratio)', function () { it('resets the behaviour when passing in 0', function (done) { var size = [300, 400] - w.setAspectRatio(1/2) + w.setAspectRatio(1 / 2) w.setAspectRatio(0) w.once('resize', function () { var newSize = w.getSize() @@ -343,7 +346,7 @@ describe('browser-window module', function () { if (process.platform === 'darwin') { app.dock.setIcon(path.join(fixtures, 'assets', 'logo.png')) } - w.setProgressBar(.5) + w.setProgressBar(0.5) if (process.platform === 'darwin') { app.dock.setIcon(null) @@ -430,7 +433,7 @@ describe('browser-window module', function () { }) }) - describe('"enableLargerThanScreen" option', function () { + describe('enableLargerThanScreen" option', function () { if (process.platform === 'linux') { return } @@ -603,12 +606,20 @@ describe('browser-window module', function () { }) describe('beginFrameSubscription method', function () { - it('subscribes to frame updates', function (done) { - this.timeout(20000) + // This test is too slow, only test it on CI. + if (!isCI) return + this.timeout(20000) + + it('subscribes to frame updates', function (done) { + let called = false w.loadURL('file://' + fixtures + '/api/frame-subscriber.html') w.webContents.on('dom-ready', function () { w.webContents.beginFrameSubscription(function (data) { + // This callback might be called twice. + if (called) return + called = true + assert.notEqual(data.length, 0) w.webContents.endFrameSubscription() done() @@ -617,11 +628,14 @@ describe('browser-window module', function () { }) it('subscribes to frame updates (only dirty rectangle)', function (done) { - this.timeout(20000) - + let called = false w.loadURL('file://' + fixtures + '/api/frame-subscriber.html') w.webContents.on('dom-ready', function () { w.webContents.beginFrameSubscription(true, function (data) { + // This callback might be called twice. + if (called) return + called = true + assert.notEqual(data.length, 0) w.webContents.endFrameSubscription() done() @@ -630,12 +644,10 @@ describe('browser-window module', function () { }) it('throws error when subscriber is not well defined', function (done) { - this.timeout(20000) - w.loadURL('file://' + fixtures + '/api/frame-subscriber.html') - try{ + try { w.webContents.beginFrameSubscription(true, true) - } catch(e) { + } catch (e) { done() } }) @@ -702,7 +714,7 @@ describe('browser-window module', function () { describe('loading main frame state', function () { it('is true when the main frame is loading', function (done) { - w.webContents.on('did-start-loading', function() { + w.webContents.on('did-start-loading', function () { assert.equal(w.webContents.isLoadingMainFrame(), true) done() }) @@ -710,9 +722,9 @@ describe('browser-window module', function () { }) it('is false when only a subframe is loading', function (done) { - w.webContents.once('did-finish-load', function() { + w.webContents.once('did-finish-load', function () { assert.equal(w.webContents.isLoadingMainFrame(), false) - w.webContents.on('did-start-loading', function() { + w.webContents.on('did-start-loading', function () { assert.equal(w.webContents.isLoadingMainFrame(), false) done() }) @@ -726,9 +738,9 @@ describe('browser-window module', function () { }) it('is true when navigating to pages from the same origin', function (done) { - w.webContents.once('did-finish-load', function() { + w.webContents.once('did-finish-load', function () { assert.equal(w.webContents.isLoadingMainFrame(), false) - w.webContents.on('did-start-loading', function() { + w.webContents.on('did-start-loading', function () { assert.equal(w.webContents.isLoadingMainFrame(), true) done() }) @@ -988,10 +1000,11 @@ describe('browser-window module', function () { w.webContents.on('devtools-opened', function () { var showPanelIntevalId = setInterval(function () { if (w && w.devToolsWebContents) { - w.devToolsWebContents.executeJavaScript('(' + (function () { + var showLastPanel = function () { var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id WebInspector.inspectorView.showPanel(lastPanelId) - }).toString() + ')()') + } + w.devToolsWebContents.executeJavaScript(`(${showLastPanel})()`) } else { clearInterval(showPanelIntevalId) } @@ -1021,7 +1034,10 @@ describe('browser-window module', function () { assert.equal(message.runtimeId, 'foo') assert.equal(message.tabId, w.webContents.id) assert.equal(message.i18nString, 'foo - bar (baz)') - assert.deepEqual(message.storageItems, {foo: 'bar'}) + assert.deepEqual(message.storageItems, { + local: {hello: 'world'}, + sync: {foo: 'bar'} + }) done() }) }) @@ -1060,10 +1076,11 @@ describe('browser-window module', function () { w.webContents.on('devtools-opened', function () { var showPanelIntevalId = setInterval(function () { if (w && w.devToolsWebContents) { - w.devToolsWebContents.executeJavaScript('(' + (function () { + var showLastPanel = function () { var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id WebInspector.inspectorView.showPanel(lastPanelId) - }).toString() + ')()') + } + w.devToolsWebContents.executeJavaScript(`(${showLastPanel})()`) } else { clearInterval(showPanelIntevalId) } @@ -1112,14 +1129,14 @@ describe('browser-window module', function () { }) it('works after page load and during subframe load', function (done) { - w.webContents.once('did-finish-load', function() { + 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() { + `, function () { + w.webContents.executeJavaScript('console.log(\'hello\')', function () { done() }) }) @@ -1128,7 +1145,7 @@ describe('browser-window module', function () { }) it('executes after page load', function (done) { - w.webContents.executeJavaScript(code, function(result) { + w.webContents.executeJavaScript(code, function (result) { assert.equal(result, expected) done() }) diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 344e01452fba..25b65b4784d0 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -41,6 +41,24 @@ describe('clipboard module', function () { }) }) + describe('clipboard.readBookmark', function () { + it('returns title and url', function () { + if (process.platform === 'linux') return + + clipboard.writeBookmark('a title', 'http://electron.atom.io') + assert.deepEqual(clipboard.readBookmark(), { + title: 'a title', + url: 'http://electron.atom.io' + }) + + clipboard.writeText('no bookmark') + assert.deepEqual(clipboard.readBookmark(), { + title: '', + url: '' + }) + }) + }) + describe('clipboard.write()', function () { it('returns data correctly', function () { var text = 'test' @@ -48,16 +66,22 @@ describe('clipboard module', function () { var p = path.join(fixtures, 'assets', 'logo.png') var i = nativeImage.createFromPath(p) var markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi' + var bookmark = {title: 'a title', url: 'test'} clipboard.write({ text: 'test', html: 'Hi', rtf: '{\\rtf1\\utf8 text}', + bookmark: 'a title', image: p }) assert.equal(clipboard.readText(), text) assert.equal(clipboard.readHTML(), markup) assert.equal(clipboard.readRTF(), rtf) assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()) + + if (process.platform !== 'linux') { + assert.deepEqual(clipboard.readBookmark(), bookmark) + } }) }) }) diff --git a/spec/api-ipc-spec.js b/spec/api-ipc-spec.js index e88815873af9..2bf5213cae39 100644 --- a/spec/api-ipc-spec.js +++ b/spec/api-ipc-spec.js @@ -80,9 +80,9 @@ describe('ipc module', function () { it('is referenced by its members', function () { let stringify = remote.getGlobal('JSON').stringify - gc(); + global.gc() stringify({}) - }); + }) }) describe('remote value in browser', function () { @@ -90,15 +90,15 @@ describe('ipc module', function () { it('keeps its constructor name for objects', function () { var buf = new Buffer('test') - var print_name = remote.require(print) - assert.equal(print_name.print(buf), 'Buffer') + var printName = remote.require(print) + assert.equal(printName.print(buf), 'Buffer') }) it('supports instanceof Date', function () { var now = new Date() - var print_name = remote.require(print) - assert.equal(print_name.print(now), 'Date') - assert.deepEqual(print_name.echo(now), now) + var printName = remote.require(print) + assert.equal(printName.print(now), 'Date') + assert.deepEqual(printName.echo(now), now) }) }) @@ -126,6 +126,33 @@ describe('ipc module', function () { done() }) }) + + it('does not emit unhandled rejection events in the main process', function (done) { + remote.process.once('unhandledRejection', function (reason) { + done(reason) + }) + + var promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')) + promise.reject().then(function () { + done(new Error('Promise was not rejected')) + }).catch(function (error) { + assert.equal(error.message, 'rejected') + done() + }) + }) + + it('emits unhandled rejection events in the renderer process', function (done) { + window.addEventListener('unhandledrejection', function (event) { + event.preventDefault() + assert.equal(event.reason.message, 'rejected') + done() + }) + + var promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')) + promise.reject().then(function () { + done(new Error('Promise was not rejected')) + }) + }) }) describe('remote webContents', function () { @@ -173,9 +200,9 @@ describe('ipc module', function () { it('is referenced by methods in prototype chain', function () { let method = derived.method derived = null - gc() + global.gc() assert.equal(method(), 'method') - }); + }) }) describe('ipc.sender.send', function () { diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index 6866448e0fd1..a725d9fd69b8 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -1,10 +1,7 @@ const assert = require('assert') -const remote = require('electron').remote -const ipcRenderer = require('electron').ipcRenderer - -const Menu = remote.require('electron').Menu -const MenuItem = remote.require('electron').MenuItem +const {ipcRenderer, remote} = require('electron') +const {Menu, MenuItem} = remote describe('menu module', function () { describe('Menu.buildFromTemplate', function () { @@ -231,7 +228,7 @@ describe('menu module', function () { } } ]) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) }) }) @@ -244,7 +241,7 @@ describe('menu module', function () { } ]) assert.equal(menu.items[0].checked, false) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) assert.equal(menu.items[0].checked, true) }) @@ -255,9 +252,9 @@ describe('menu module', function () { type: 'radio' } ]) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) assert.equal(menu.items[0].checked, true) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) assert.equal(menu.items[0].checked, true) }) @@ -359,4 +356,71 @@ describe('menu module', function () { } }) }) + + describe('MenuItem command id', function () { + it('cannot be overwritten', function () { + var item = new MenuItem({ + label: 'item' + }) + + var commandId = item.commandId + assert(commandId != null) + item.commandId = '' + commandId + '-modified' + assert.equal(item.commandId, commandId) + }) + }) + + describe('MenuItem with invalid type', function () { + it('throws an exception', function () { + assert.throws(function () { + Menu.buildFromTemplate([ + { + label: 'text', + type: 'not-a-type' + } + ]) + }, /Unknown menu item type: not-a-type/) + }) + }) + + describe('MenuItem with submenu type and missing submenu', function () { + it('throws an exception', function () { + assert.throws(function () { + Menu.buildFromTemplate([ + { + label: 'text', + type: 'submenu' + } + ]) + }, /Invalid submenu/) + }) + }) + + describe('MenuItem role', function () { + it('includes a default label and accelerator', function () { + var item = new MenuItem({role: 'close'}) + assert.equal(item.label, 'Close') + assert.equal(item.accelerator, 'CommandOrControl+W') + + item = new MenuItem({role: 'close', label: 'Other'}) + assert.equal(item.label, 'Other') + assert.equal(item.accelerator, 'CommandOrControl+W') + + item = new MenuItem({role: 'close', accelerator: 'D'}) + assert.equal(item.label, 'Close') + assert.equal(item.accelerator, 'D') + + item = new MenuItem({role: 'close', label: 'C', accelerator: 'D'}) + assert.equal(item.label, 'C') + assert.equal(item.accelerator, 'D') + + item = new MenuItem({role: 'help'}) + assert.equal(item.label, 'Help') + assert.equal(item.accelerator, undefined) + + item = new MenuItem({role: 'hide'}) + assert.equal(item.label, 'Hide Electron Test') + assert.equal(item.accelerator, 'Command+H') + }) + }) }) diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index 4ada22af9ed1..7e49cbf78e51 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -9,6 +9,7 @@ describe('nativeImage module', () => { it('returns an empty image for invalid paths', () => { assert(nativeImage.createFromPath('').isEmpty()) assert(nativeImage.createFromPath('does-not-exist.png').isEmpty()) + assert(nativeImage.createFromPath('does-not-exist.ico').isEmpty()) }) it('loads images from paths relative to the current working directory', () => { @@ -47,5 +48,15 @@ describe('nativeImage module', () => { // If all bytes are null, that's Bad assert.equal(nsimage.reduce((acc, x) => acc || (x !== 0), false), true) }) + + it('loads images from .ico files on Windows', () => { + if (process.platform !== 'win32') return + + const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico') + const image = nativeImage.createFromPath(imagePath) + assert(!image.isEmpty()) + assert.equal(image.getSize().height, 256) + assert.equal(image.getSize().width, 256) + }) }) }) diff --git a/spec/api-protocol-spec.js b/spec/api-protocol-spec.js index 57e8638ee6d3..f665210c0fd4 100644 --- a/spec/api-protocol-spec.js +++ b/spec/api-protocol-spec.js @@ -514,7 +514,7 @@ describe('protocol module', function () { it('works when target URL redirects', function (done) { var contents = null var server = http.createServer(function (req, res) { - if (req.url == '/serverRedirect') { + if (req.url === '/serverRedirect') { res.statusCode = 301 res.setHeader('Location', 'http://' + req.rawHeaders[1]) res.end() @@ -919,7 +919,7 @@ describe('protocol module', function () { }) w.loadURL(origin) }) - }), + }) it('can have fetch working in it', function (done) { const content = '' diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 25a09ba313b7..0557508e6da8 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -16,10 +16,6 @@ describe('session module', function () { var fixtures = path.resolve(__dirname, 'fixtures') var w = null var url = 'http://127.0.0.1' - var partitionName = 'temp' - var protocolName = 'sp' - const partitionProtocol = session.fromPartition(partitionName).protocol - const protocol = session.defaultSession.protocol beforeEach(function () { if (w != null) { @@ -73,6 +69,17 @@ describe('session module', function () { }) }) + it('calls back with an error when setting a cookie with missing required fields', function (done) { + session.defaultSession.cookies.set({ + url: '', + name: '1', + value: '1' + }, function (error) { + assert.equal(error.message, 'Setting cookie failed') + done() + }) + }) + it('should over-write the existent cookie', function (done) { session.defaultSession.cookies.set({ url: url, @@ -274,45 +281,46 @@ describe('session module', function () { }) describe('session.protocol', function () { - beforeEach(function () { - if (w != null) { - w.destroy() - } + const partitionName = 'temp' + const protocolName = 'sp' + const partitionProtocol = session.fromPartition(partitionName).protocol + const protocol = session.defaultSession.protocol + const handler = function (ignoredError, callback) { + callback({data: 'test', mimeType: 'text/html'}) + } + + beforeEach(function (done) { + if (w != null) w.destroy() w = new BrowserWindow({ show: false, - width: 400, - height: 400, webPreferences: { partition: partitionName } }) + partitionProtocol.registerStringProtocol(protocolName, handler, function (error) { + done(error != null ? error : undefined) + }) }) afterEach(function (done) { partitionProtocol.unregisterProtocol(protocolName, () => done()) }) - it('handles requests from a partition', function (done) { - var handler = function (error, callback) { - callback({ - data: 'test' - }) - } - partitionProtocol.registerStringProtocol(protocolName, handler, function (error) { - if (error) { - return done(error) - } - protocol.isProtocolHandled(protocolName, function (result) { - assert.equal(result, false) - partitionProtocol.isProtocolHandled(protocolName, function (result) { - assert.equal(result, true) - w.webContents.on('did-finish-load', function () { - done() - }) - w.loadURL(protocolName + "://fake-host") - }) + it('does not affect defaultSession', function (done) { + protocol.isProtocolHandled(protocolName, function (result) { + assert.equal(result, false) + partitionProtocol.isProtocolHandled(protocolName, function (result) { + assert.equal(result, true) + done() }) }) }) + + xit('handles requests from partition', function (done) { + w.webContents.on('did-finish-load', function () { + done() + }) + w.loadURL(`${protocolName}://fake-host`) + }) }) }) diff --git a/spec/api-system-preferences-spec.js b/spec/api-system-preferences-spec.js index 333c4dbacb4e..be7009ae6bf1 100644 --- a/spec/api-system-preferences-spec.js +++ b/spec/api-system-preferences-spec.js @@ -18,5 +18,4 @@ describe('systemPreferences module', function () { assert(languages.length > 0) }) }) - }) diff --git a/spec/api-web-request-spec.js b/spec/api-web-request-spec.js index 211c69a14765..7b1c1556ecbc 100644 --- a/spec/api-web-request-spec.js +++ b/spec/api-web-request-spec.js @@ -7,7 +7,7 @@ const session = remote.session describe('webRequest module', function () { var ses = session.defaultSession var server = http.createServer(function (req, res) { - if (req.url == '/serverRedirect') { + if (req.url === '/serverRedirect') { res.statusCode = 301 res.setHeader('Location', 'http://' + req.rawHeaders[1]) res.end() @@ -308,7 +308,7 @@ describe('webRequest module', function () { ses.webRequest.onHeadersReceived(function (details, callback) { var responseHeaders = details.responseHeaders callback({ - responseHeaders: responseHeaders, + responseHeaders: responseHeaders }) }) $.ajax({ @@ -328,7 +328,7 @@ describe('webRequest module', function () { var responseHeaders = details.responseHeaders callback({ responseHeaders: responseHeaders, - statusLine: "HTTP/1.1 404 Not Found" + statusLine: 'HTTP/1.1 404 Not Found' }) }) $.ajax({ diff --git a/spec/asar-spec.js b/spec/asar-spec.js index ac0779d2f266..376e5ef0805f 100644 --- a/spec/asar-spec.js +++ b/spec/asar-spec.js @@ -1,5 +1,5 @@ const assert = require('assert') -const child_process = require('child_process') +const ChildProcess = require('child_process') const fs = require('fs') const path = require('path') @@ -536,7 +536,7 @@ describe('asar package', function () { describe('child_process.fork', function () { it('opens a normal js file', function (done) { - var child = child_process.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js')) + var child = ChildProcess.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js')) child.on('message', function (msg) { assert.equal(msg, 'message') done() @@ -546,7 +546,7 @@ describe('asar package', function () { it('supports asar in the forked js', function (done) { var file = path.join(fixtures, 'asar', 'a.asar', 'file1') - var child = child_process.fork(path.join(fixtures, 'module', 'asar.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js')) child.on('message', function (content) { assert.equal(content, fs.readFileSync(file).toString()) done() @@ -556,11 +556,10 @@ describe('asar package', function () { }) describe('child_process.exec', function () { - var child_process = require('child_process'); var echo = path.join(fixtures, 'asar', 'echo.asar', 'echo') it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { - child_process.exec('echo ' + echo + ' foo bar', function (error, stdout) { + ChildProcess.exec('echo ' + echo + ' foo bar', function (error, stdout) { assert.equal(error, null) assert.equal(stdout.toString().replace(/\r/g, ''), echo + ' foo bar\n') done() @@ -569,24 +568,22 @@ describe('asar package', function () { }) describe('child_process.execSync', function () { - var child_process = require('child_process'); var echo = path.join(fixtures, 'asar', 'echo.asar', 'echo') it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { - var stdout = child_process.execSync('echo ' + echo + ' foo bar') + var stdout = ChildProcess.execSync('echo ' + echo + ' foo bar') assert.equal(stdout.toString().replace(/\r/g, ''), echo + ' foo bar\n') done() }) }) describe('child_process.execFile', function () { - var echo, execFile, execFileSync, ref2 + var echo, execFile, execFileSync if (process.platform !== 'darwin') { return } - ref2 = require('child_process') - execFile = ref2.execFile - execFileSync = ref2.execFileSync + execFile = ChildProcess.execFile + execFileSync = ChildProcess.execFileSync echo = path.join(fixtures, 'asar', 'echo.asar', 'echo') it('executes binaries', function (done) { @@ -785,7 +782,7 @@ describe('asar package', function () { }) it('is available in forked scripts', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'original-fs.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')) child.on('message', function (msg) { assert.equal(msg, 'object') done() diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 68836fb927ed..e682773c4309 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -235,10 +235,11 @@ describe('chromium feature', function () { it('defines a window.location getter', function (done) { var b, targetURL - if (process.platform == 'win32') + if (process.platform === 'win32') { targetURL = 'file:///' + fixtures.replace(/\\/g, '/') + '/pages/base-page.html' - else + } else { targetURL = 'file://' + fixtures + '/pages/base-page.html' + } b = window.open(targetURL) webContents.fromId(b.guestId).once('did-finish-load', function () { assert.equal(b.location, targetURL) diff --git a/spec/fixtures/api/relaunch/main.js b/spec/fixtures/api/relaunch/main.js index 74cafc6f0d53..27a1e07efa63 100644 --- a/spec/fixtures/api/relaunch/main.js +++ b/spec/fixtures/api/relaunch/main.js @@ -1,9 +1,7 @@ -const {app, dialog} = require('electron') +const {app} = require('electron') const net = require('net') -const socketPath = process.platform === 'win32' ? - '\\\\.\\pipe\\electron-app-relaunch' : - '/tmp/electron-app-relaunch' +const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch' process.on('uncaughtException', () => { app.exit(1) diff --git a/spec/fixtures/assets/icon.ico b/spec/fixtures/assets/icon.ico new file mode 100644 index 000000000000..aa0917755465 Binary files /dev/null and b/spec/fixtures/assets/icon.ico differ diff --git a/spec/fixtures/devtools-extensions/foo/index.html b/spec/fixtures/devtools-extensions/foo/index.html index b10288227aca..70db43a37c2c 100644 --- a/spec/fixtures/devtools-extensions/foo/index.html +++ b/spec/fixtures/devtools-extensions/foo/index.html @@ -6,16 +6,25 @@