diff --git a/.node-version b/.node-version index 339cbf236a73..ed15bef9ae26 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v5.10.0 +v6.1.0 diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 5d4aafca6999..bc29b988eeea 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -110,6 +110,8 @@ int GetPathConstant(const std::string& name) { return chrome::DIR_USER_PICTURES; else if (name == "videos") return chrome::DIR_USER_VIDEOS; + else if (name == "pepperFlashSystemPlugin") + return chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN; else return -1; } @@ -261,13 +263,14 @@ void App::OnContinueUserActivity( } #endif -void App::OnLogin(LoginHandler* login_handler) { +void App::OnLogin(LoginHandler* login_handler, + const base::DictionaryValue& request_details) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); bool prevent_default = Emit( "login", WebContents::CreateFrom(isolate(), login_handler->GetWebContents()), - login_handler->request(), + request_details, login_handler->auth_info(), base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler))); @@ -447,6 +450,15 @@ bool App::Relaunch(mate::Arguments* js_args) { return relauncher::RelaunchApp(argv); } +void App::DisableHardwareAcceleration(mate::Arguments* args) { + if (Browser::Get()->is_ready()) { + args->ThrowError("app.disableHardwareAcceleration() can only be called " + "before app is ready"); + return; + } + content::GpuDataManager::GetInstance()->DisableHardwareAcceleration(); +} + #if defined(USE_NSS_CERTS) void App::ImportCertificate( const base::DictionaryValue& options, @@ -528,7 +540,9 @@ void App::BuildPrototype( #endif .SetMethod("makeSingleInstance", &App::MakeSingleInstance) .SetMethod("releaseSingleInstance", &App::ReleaseSingleInstance) - .SetMethod("relaunch", &App::Relaunch); + .SetMethod("relaunch", &App::Relaunch) + .SetMethod("disableHardwareAcceleration", + &App::DisableHardwareAcceleration); } } // namespace api diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index e2bcb4583dda..b550f0ba7b90 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -70,7 +70,8 @@ class App : public AtomBrowserClient::Delegate, void OnActivate(bool has_visible_windows) override; void OnWillFinishLaunching() override; void OnFinishLaunching() override; - void OnLogin(LoginHandler* login_handler) override; + void OnLogin(LoginHandler* login_handler, + const base::DictionaryValue& request_details) override; #if defined(OS_MACOSX) void OnContinueUserActivity( bool* prevent_default, @@ -111,7 +112,7 @@ class App : public AtomBrowserClient::Delegate, const ProcessSingleton::NotificationCallback& callback); void ReleaseSingleInstance(); bool Relaunch(mate::Arguments* args); - + void DisableHardwareAcceleration(mate::Arguments* args); #if defined(USE_NSS_CERTS) void ImportCertificate(const base::DictionaryValue& options, const net::CompletionCallback& callback); diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc index 96826a250f57..81107abceac9 100644 --- a/atom/browser/api/atom_api_download_item.cc +++ b/atom/browser/api/atom_api_download_item.cc @@ -25,6 +25,9 @@ struct Converter { content::DownloadItem::DownloadState state) { std::string download_state; switch (state) { + case content::DownloadItem::IN_PROGRESS: + download_state = "progressing"; + break; case content::DownloadItem::COMPLETE: download_state = "completed"; break; @@ -85,7 +88,7 @@ void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) { // Destroy the item once item is downloaded. base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); } else { - Emit("updated"); + Emit("updated", item->GetState()); } } @@ -99,10 +102,18 @@ void DownloadItem::Pause() { download_item_->Pause(); } +bool DownloadItem::IsPaused() const { + return download_item_->IsPaused(); +} + void DownloadItem::Resume() { download_item_->Resume(); } +bool DownloadItem::CanResume() const { + return download_item_->CanResume(); +} + void DownloadItem::Cancel() { download_item_->Cancel(true); download_item_->Remove(); @@ -141,6 +152,14 @@ const GURL& DownloadItem::GetURL() const { return download_item_->GetURL(); } +content::DownloadItem::DownloadState DownloadItem::GetState() const { + return download_item_->GetState(); +} + +bool DownloadItem::IsDone() const { + return download_item_->IsDone(); +} + void DownloadItem::SetSavePath(const base::FilePath& path) { save_path_ = path; } @@ -155,7 +174,9 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate, mate::ObjectTemplateBuilder(isolate, prototype) .MakeDestroyable() .SetMethod("pause", &DownloadItem::Pause) + .SetMethod("isPaused", &DownloadItem::IsPaused) .SetMethod("resume", &DownloadItem::Resume) + .SetMethod("canResume", &DownloadItem::CanResume) .SetMethod("cancel", &DownloadItem::Cancel) .SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes) .SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes) @@ -164,6 +185,8 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate, .SetMethod("getFilename", &DownloadItem::GetFilename) .SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition) .SetMethod("getURL", &DownloadItem::GetURL) + .SetMethod("getState", &DownloadItem::GetState) + .SetMethod("isDone", &DownloadItem::IsDone) .SetMethod("setSavePath", &DownloadItem::SetSavePath) .SetMethod("getSavePath", &DownloadItem::GetSavePath); } diff --git a/atom/browser/api/atom_api_download_item.h b/atom/browser/api/atom_api_download_item.h index bc7ddd821bb9..fa330b90a0e3 100644 --- a/atom/browser/api/atom_api_download_item.h +++ b/atom/browser/api/atom_api_download_item.h @@ -27,7 +27,9 @@ class DownloadItem : public mate::TrackableObject, v8::Local prototype); void Pause(); + bool IsPaused() const; void Resume(); + bool CanResume() const; void Cancel(); int64_t GetReceivedBytes() const; int64_t GetTotalBytes() const; @@ -36,6 +38,8 @@ class DownloadItem : public mate::TrackableObject, std::string GetFilename() const; std::string GetContentDisposition() const; const GURL& GetURL() const; + content::DownloadItem::DownloadState GetState() const; + bool IsDone() const; void SetSavePath(const base::FilePath& path); base::FilePath GetSavePath() const; diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index 226f689d32de..db8facfe0953 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -12,8 +12,12 @@ #include "atom/browser/net/url_request_fetch_job.h" #include "atom/browser/net/url_request_string_job.h" #include "atom/common/native_mate_converters/callback.h" -#include "atom/common/native_mate_converters/net_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/node_includes.h" +#include "atom/common/options_switches.h" +#include "base/command_line.h" +#include "base/strings/string_util.h" +#include "content/public/browser/child_process_security_policy.h" #include "native_mate/dictionary.h" #include "url/url_util.h" @@ -158,21 +162,21 @@ namespace { void RegisterStandardSchemes( const std::vector& schemes) { - for (const auto& scheme : schemes) + auto policy = content::ChildProcessSecurityPolicy::GetInstance(); + for (const auto& scheme : schemes) { url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT); -} + policy->RegisterWebSafeScheme(scheme); + } -mate::Handle CreateProtocol(v8::Isolate* isolate) { - auto browser_context = static_cast( - atom::AtomBrowserMainParts::Get()->browser_context()); - return atom::api::Protocol::Create(isolate, browser_context); + auto command_line = base::CommandLine::ForCurrentProcess(); + command_line->AppendSwitchASCII(atom::switches::kStandardSchemes, + base::JoinString(schemes, ",")); } void Initialize(v8::Local exports, v8::Local unused, v8::Local context, void* priv) { v8::Isolate* isolate = context->GetIsolate(); mate::Dictionary dict(isolate, exports); - dict.SetMethod("createProtocolObject", base::Bind(&CreateProtocol, isolate)); dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes); } diff --git a/atom/browser/api/atom_api_protocol.h b/atom/browser/api/atom_api_protocol.h index d33e63fa9cff..734d179dfacd 100644 --- a/atom/browser/api/atom_api_protocol.h +++ b/atom/browser/api/atom_api_protocol.h @@ -9,6 +9,7 @@ #include #include +#include "atom/browser/api/trackable_object.h" #include "atom/browser/net/atom_url_request_job_factory.h" #include "base/callback.h" #include "base/containers/scoped_ptr_hash_map.h" @@ -16,7 +17,10 @@ #include "native_mate/arguments.h" #include "native_mate/dictionary.h" #include "native_mate/handle.h" -#include "native_mate/wrappable.h" + +namespace base { +class DictionaryValue; +} namespace net { class URLRequest; @@ -30,10 +34,10 @@ class AtomURLRequestJobFactory; namespace api { -class Protocol : public mate::Wrappable { +class Protocol : public mate::TrackableObject { public: using Handler = - base::Callback)>; + base::Callback)>; using CompletionCallback = base::Callback)>; using BooleanCallback = base::Callback; diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 15aa2afe9cf3..602729b9e382 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -9,6 +9,7 @@ #include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/api/atom_api_download_item.h" +#include "atom/browser/api/atom_api_protocol.h" #include "atom/browser/api/atom_api_web_request.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" @@ -462,6 +463,14 @@ v8::Local Session::Cookies(v8::Isolate* isolate) { return v8::Local::New(isolate, cookies_); } +v8::Local Session::Protocol(v8::Isolate* isolate) { + if (protocol_.IsEmpty()) { + auto handle = atom::api::Protocol::Create(isolate, browser_context()); + protocol_.Reset(isolate, handle.ToV8()); + } + return v8::Local::New(isolate, protocol_); +} + v8::Local Session::WebRequest(v8::Isolate* isolate) { if (web_request_.IsEmpty()) { auto handle = atom::api::WebRequest::Create(isolate, browser_context()); @@ -512,6 +521,7 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("allowNTLMCredentialsForDomains", &Session::AllowNTLMCredentialsForDomains) .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 0cebf09ea1f3..bb67fa106de1 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -81,10 +81,12 @@ class Session: public mate::TrackableObject, void ClearHostResolverCache(mate::Arguments* args); void AllowNTLMCredentialsForDomains(const std::string& domains); v8::Local Cookies(v8::Isolate* isolate); + v8::Local Protocol(v8::Isolate* isolate); v8::Local WebRequest(v8::Isolate* isolate); // Cached object. v8::Global cookies_; + v8::Global protocol_; v8::Global web_request_; // The X-DevTools-Emulate-Network-Conditions-Client-Id. diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 9d93d980be4a..6cb9efb9614f 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -389,6 +389,11 @@ void WebContents::ActivateContents(content::WebContents* source) { Emit("activate"); } +void WebContents::UpdateTargetURL(content::WebContents* source, + const GURL& url) { + Emit("update-target-url", url); +} + bool WebContents::IsPopupOrPanel(const content::WebContents* source) const { return type_ == BROWSER_WINDOW; } @@ -739,6 +744,15 @@ int WebContents::GetID() const { return web_contents()->GetRenderProcessHost()->GetID(); } +std::string WebContents::GetType() const { + switch (type_) { + case BROWSER_WINDOW: return "window"; + case WEB_VIEW: return "webview"; + case REMOTE: return "remote"; + default: return ""; + } +} + bool WebContents::Equal(const WebContents* web_contents) const { return GetID() == web_contents->GetID(); } @@ -1089,6 +1103,14 @@ void WebContents::StopFindInPage(content::StopFindAction action) { web_contents()->StopFinding(action); } +void WebContents::ShowDefinitionForSelection() { +#if defined(OS_MACOSX) + const auto view = web_contents()->GetRenderWidgetHostView(); + if (view) + view->ShowDefinitionForSelection(); +#endif +} + void WebContents::Focus() { web_contents()->Focus(); } @@ -1274,6 +1296,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) .SetMethod("setSize", &WebContents::SetSize) .SetMethod("isGuest", &WebContents::IsGuest) + .SetMethod("getType", &WebContents::GetType) .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) @@ -1284,6 +1307,8 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("_printToPDF", &WebContents::PrintToPDF) .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) + .SetMethod("showDefinitionForSelection", + &WebContents::ShowDefinitionForSelection) .SetProperty("id", &WebContents::ID) .SetProperty("session", &WebContents::Session) .SetProperty("hostWebContents", &WebContents::HostWebContents) @@ -1350,6 +1375,8 @@ void Initialize(v8::Local exports, v8::Local unused, dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents); dict.SetMethod("fromId", &mate::TrackableObject::FromWeakMapID); + dict.SetMethod("getAllWebContents", + &mate::TrackableObject::GetAll); } } // namespace diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 85c43f1486aa..13c6ce5a7b2c 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -59,6 +59,7 @@ class WebContents : public mate::TrackableObject, v8::Local prototype); int GetID() const; + std::string GetType() const; bool Equal(const WebContents* web_contents) const; void LoadURL(const GURL& url, const mate::Dictionary& options); void DownloadURL(const GURL& url); @@ -116,6 +117,7 @@ class WebContents : public mate::TrackableObject, void ReplaceMisspelling(const base::string16& word); uint32_t FindInPage(mate::Arguments* args); void StopFindInPage(content::StopFindAction action); + void ShowDefinitionForSelection(); // Focus. void Focus(); @@ -182,6 +184,7 @@ class WebContents : public mate::TrackableObject, const gfx::Rect& pos) override; void CloseContents(content::WebContents* source) override; void ActivateContents(content::WebContents* contents) override; + void UpdateTargetURL(content::WebContents* source, const GURL& url) override; bool IsPopupOrPanel(const content::WebContents* source) const override; void HandleKeyboardEvent( content::WebContents* source, diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index ac3c510193ed..c95a43340418 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -623,12 +623,6 @@ void Window::UnhookAllWindowMessages() { } #endif -#if defined(OS_MACOSX) -void Window::ShowDefinitionForSelection() { - window_->ShowDefinitionForSelection(); -} -#endif - #if defined(TOOLKIT_VIEWS) void Window::SetIcon(mate::Handle icon) { #if defined(OS_WIN) @@ -760,10 +754,6 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage) .SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages) #endif -#if defined(OS_MACOSX) - .SetMethod("showDefinitionForSelection", - &Window::ShowDefinitionForSelection) -#endif #if defined(TOOLKIT_VIEWS) .SetMethod("setIcon", &Window::SetIcon) #endif diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index e698eaaf78cc..23ab162261ff 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -169,10 +169,6 @@ class Window : public mate::TrackableObject, void UnhookAllWindowMessages(); #endif -#if defined(OS_MACOSX) - void ShowDefinitionForSelection(); -#endif - #if defined(TOOLKIT_VIEWS) void SetIcon(mate::Handle icon); #endif diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index e3cb9c5c8c89..c1b50459c680 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -169,6 +169,14 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( if (process_type != "renderer") return; + // Copy following switches to child process. + static const char* const kCommonSwitchNames[] = { + switches::kStandardSchemes, + }; + command_line->CopySwitchesFrom( + *base::CommandLine::ForCurrentProcess(), + kCommonSwitchNames, arraysize(kCommonSwitchNames)); + // The registered service worker schemes. if (!g_custom_service_worker_schemes.empty()) command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes, diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc index 59ac258ea13a..1115aa049d69 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.cc +++ b/atom/browser/atom_resource_dispatcher_host_delegate.cc @@ -31,10 +31,13 @@ void HandleExternalProtocolInUI( if (!web_contents) return; - GURL escaped_url(net::EscapeExternalHandlerValue(url.spec())); - auto callback = base::Bind(&OnOpenExternal, escaped_url); auto permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); + if (!permission_helper) + return; + + GURL escaped_url(net::EscapeExternalHandlerValue(url.spec())); + auto callback = base::Bind(&OnOpenExternal, escaped_url); permission_helper->RequestOpenExternalPermission(callback, has_user_gesture); } diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 093209ef7c47..8c00c1287376 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -151,8 +151,12 @@ void Browser::DidFinishLaunching() { FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching()); } -void Browser::RequestLogin(LoginHandler* login_handler) { - FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler)); +void Browser::RequestLogin( + LoginHandler* login_handler, + std::unique_ptr request_details) { + FOR_EACH_OBSERVER(BrowserObserver, + observers_, + OnLogin(login_handler, *(request_details.get()))); } void Browser::NotifyAndShutdown() { diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 18d0c97c93ac..2c1678083034 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -21,6 +21,7 @@ #endif namespace base { +class DictionaryValue; class FilePath; } @@ -165,7 +166,8 @@ class Browser : public WindowListObserver { void DidFinishLaunching(); // Request basic auth login. - void RequestLogin(LoginHandler* login_handler); + void RequestLogin(LoginHandler* login_handler, + std::unique_ptr request_details); void AddObserver(BrowserObserver* obs) { observers_.AddObserver(obs); diff --git a/atom/browser/browser_observer.h b/atom/browser/browser_observer.h index c8b0082beb7e..41aeceb6375b 100644 --- a/atom/browser/browser_observer.h +++ b/atom/browser/browser_observer.h @@ -49,7 +49,8 @@ class BrowserObserver { virtual void OnFinishLaunching() {} // The browser requests HTTP login. - virtual void OnLogin(LoginHandler* login_handler) {} + virtual void OnLogin(LoginHandler* login_handler, + const base::DictionaryValue& request_details) {} #if defined(OS_MACOSX) // The browser wants to resume a user activity via handoff. (OS X only) diff --git a/atom/browser/login_handler.cc b/atom/browser/login_handler.cc index 1a6c6947b617..827154b0d0b4 100644 --- a/atom/browser/login_handler.cc +++ b/atom/browser/login_handler.cc @@ -5,6 +5,8 @@ #include "atom/browser/login_handler.h" #include "atom/browser/browser.h" +#include "atom/common/native_mate_converters/net_converter.h" +#include "base/values.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/resource_dispatcher_host.h" @@ -37,11 +39,18 @@ LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info, render_frame_id_(0) { content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame( &render_process_host_id_, &render_frame_id_); + + // Fill request details on IO thread. + std::unique_ptr request_details( + new base::DictionaryValue); + FillRequestDetails(request_details.get(), request_); + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&Browser::RequestLogin, base::Unretained(Browser::Get()), - base::RetainedRef(make_scoped_refptr(this)))); + base::RetainedRef(make_scoped_refptr(this)), + base::Passed(&request_details))); } LoginHandler::~LoginHandler() { diff --git a/atom/browser/login_handler.h b/atom/browser/login_handler.h index 52ec1abf5b1a..ba1371336cc1 100644 --- a/atom/browser/login_handler.h +++ b/atom/browser/login_handler.h @@ -36,7 +36,6 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate { void Login(const base::string16& username, const base::string16& password); const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); } - const net::URLRequest* request() const { return request_; } protected: ~LoginHandler() override; diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index c22159fc25db..e0224836dace 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -10,6 +10,7 @@ #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/browser.h" #include "atom/browser/window_list.h" #include "atom/common/api/api_messages.h" #include "atom/common/native_mate_converters/file_path_converter.h" @@ -166,7 +167,7 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) { // For normal window, use white as default background. SetBackgroundColor("#FFFF"); } - std::string title("Electron"); + std::string title(Browser::Get()->GetName()); options.Get(options::kTitle, &title); SetTitle(title); @@ -281,9 +282,6 @@ bool NativeWindow::IsDocumentEdited() { return false; } -void NativeWindow::SetIgnoreMouseEvents(bool ignore) { -} - void NativeWindow::SetMenu(ui::MenuModel* menu) { } @@ -337,10 +335,6 @@ void NativeWindow::CapturePage(const gfx::Rect& rect, kBGRA_8888_SkColorType); } -void NativeWindow::ShowDefinitionForSelection() { - NOTIMPLEMENTED(); -} - void NativeWindow::SetAutoHideMenuBar(bool auto_hide) { } diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 0846fbde4a9c..c7d099fcadf2 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -154,7 +154,7 @@ class NativeWindow : public base::SupportsUserData, virtual std::string GetRepresentedFilename(); virtual void SetDocumentEdited(bool edited); virtual bool IsDocumentEdited(); - virtual void SetIgnoreMouseEvents(bool ignore); + virtual void SetIgnoreMouseEvents(bool ignore) = 0; virtual void SetMenu(ui::MenuModel* menu); virtual bool HasModalDialog(); virtual gfx::NativeWindow GetNativeWindow() = 0; @@ -179,9 +179,6 @@ class NativeWindow : public base::SupportsUserData, virtual void CapturePage(const gfx::Rect& rect, const CapturePageCallback& callback); - // Show popup dictionary. - virtual void ShowDefinitionForSelection(); - // Toggle the menu bar. virtual void SetAutoHideMenuBar(bool auto_hide); virtual bool IsMenuBarAutoHide(); diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index c1694c3c784a..27857239e8c8 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -83,7 +83,6 @@ class NativeWindowMac : public NativeWindow { void SetProgressBar(double progress) override; void SetOverlayIcon(const gfx::Image& overlay, const std::string& description) override; - void ShowDefinitionForSelection() override; void SetVisibleOnAllWorkspaces(bool visible) override; bool IsVisibleOnAllWorkspaces() override; @@ -139,6 +138,9 @@ class NativeWindowMac : public NativeWindow { // The presentation options before entering kiosk mode. NSApplicationPresentationOptions kiosk_options_; + // The window title, for frameless windows we only set title when fullscreen. + std::string title_; + // Force showing the buttons for frameless window. bool force_show_buttons_; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 8bc4052f8538..8de4b109373a 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -193,10 +193,16 @@ bool ScopedDisableResize::disable_resize_ = false; - (void)windowDidEnterFullScreen:(NSNotification*)notification { shell_->NotifyWindowEnterFullScreen(); + // For frameless window we don't set title for normal mode since the title + // bar is expected to be empty, but after entering fullscreen mode we have + // to set one, because title bar is visible here. + NSWindow* window = shell_->GetNativeWindow(); + if (shell_->transparent() || !shell_->has_frame()) + [window setTitle:base::SysUTF8ToNSString(shell_->GetTitle())]; + // Restore the native toolbar immediately after entering fullscreen, if we do // this before leaving fullscreen, traffic light buttons will be jumping. if (shell_->should_hide_native_toolbar_in_fullscreen()) { - NSWindow* window = shell_->GetNativeWindow(); base::scoped_nsobject toolbar( [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]); [toolbar setShowsBaselineSeparator:NO]; @@ -209,6 +215,11 @@ bool ScopedDisableResize::disable_resize_ = false; } - (void)windowWillExitFullScreen:(NSNotification*)notification { + // Restore the title bar to empty. + NSWindow* window = shell_->GetNativeWindow(); + if (shell_->transparent() || !shell_->has_frame()) + [window setTitle:@""]; + // Turn off the style for toolbar. if (shell_->should_hide_native_toolbar_in_fullscreen()) shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask); @@ -513,10 +524,6 @@ NativeWindowMac::NativeWindowMac( options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor); [window_ setDisableAutoHideCursor:disableAutoHideCursor]; - // Disable zoom button if window is not resizable. - if (!maximizable) - SetMaximizable(false); - NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; @@ -544,6 +551,12 @@ NativeWindowMac::NativeWindowMac( }]; InstallView(); + + // Disable zoom button if window is not resizable. + // Set maximizable state last to ensure zoom button does not get reset + // by calls to other APIs. + if (!maximizable) + SetMaximizable(false); } NativeWindowMac::~NativeWindowMac() { @@ -781,6 +794,8 @@ void NativeWindowMac::Center() { } void NativeWindowMac::SetTitle(const std::string& title) { + title_ = title; + // We don't want the title to show in transparent or frameless window. if (transparent() || !has_frame()) return; @@ -789,7 +804,7 @@ void NativeWindowMac::SetTitle(const std::string& title) { } std::string NativeWindowMac::GetTitle() { - return base::SysNSStringToUTF8([window_ title]); + return title_; } void NativeWindowMac::FlashFrame(bool flash) { @@ -920,15 +935,6 @@ void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay, const std::string& description) { } -void NativeWindowMac::ShowDefinitionForSelection() { - if (!web_contents()) - return; - auto rwhv = web_contents()->GetRenderWidgetHostView(); - if (!rwhv) - return; - rwhv->ShowDefinitionForSelection(); -} - void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible) { SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces); } diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index e699892d4ac5..29c4187a124c 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -89,6 +89,21 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) { (modifiers == (Modifiers::AltKey | Modifiers::IsRight)); } +#if defined(USE_X11) +int SendClientEvent(XDisplay* display, ::Window window, const char* msg) { + XEvent event = {}; + event.xclient.type = ClientMessage; + event.xclient.send_event = True; + event.xclient.message_type = XInternAtom(display, msg, False); + event.xclient.window = window; + event.xclient.format = 32; + XSendEvent(display, DefaultRootWindow(display), False, + SubstructureRedirectMask | SubstructureNotifyMask, &event); + XFlush(display); + return True; +} +#endif + class NativeWindowClientView : public views::ClientView { public: NativeWindowClientView(views::Widget* widget, @@ -298,10 +313,19 @@ void NativeWindowViews::CloseImmediately() { } void NativeWindowViews::Focus(bool focus) { - if (focus) + if (focus) { +#if defined(OS_WIN) window_->Activate(); - else +#elif defined(USE_X11) + // The "Activate" implementation of Chromium is not reliable on Linux. + ::Window window = GetAcceleratedWidget(); + XDisplay* xdisplay = gfx::GetXDisplay(); + SendClientEvent(xdisplay, window, "_NET_ACTIVE_WINDOW"); + XMapRaised(xdisplay, window); +#endif + } else { window_->Deactivate(); + } } bool NativeWindowViews::IsFocused() { @@ -648,6 +672,26 @@ bool NativeWindowViews::HasShadow() { return wm::GetShadowType(GetNativeWindow()) != wm::SHADOW_TYPE_NONE; } +void NativeWindowViews::SetIgnoreMouseEvents(bool ignore) { +#if defined(OS_WIN) + LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); + if (ignore) + ex_style |= (WS_EX_TRANSPARENT | WS_EX_LAYERED); + else + ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED); + ::SetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE, ex_style); +#elif defined(USE_X11) + if (ignore) { + XRectangle r = {0, 0, 1, 1}; + XShapeCombineRectangles(gfx::GetXDisplay(), GetAcceleratedWidget(), + ShapeInput, 0, 0, &r, 1, ShapeSet, YXBanded); + } else { + XShapeCombineMask(gfx::GetXDisplay(), GetAcceleratedWidget(), + ShapeInput, 0, 0, None, ShapeSet); + } +#endif +} + void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) { if (menu_model == nullptr) { // Remove accelerators diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 909c5b6fd4ba..6a26c14e0084 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -91,6 +91,7 @@ class NativeWindowViews : public NativeWindow, void SetBackgroundColor(const std::string& color_name) override; void SetHasShadow(bool has_shadow) override; bool HasShadow() override; + void SetIgnoreMouseEvents(bool ignore) override; void SetMenu(ui::MenuModel* menu_model) override; gfx::NativeWindow GetNativeWindow() override; void SetOverlayIcon(const gfx::Image& overlay, diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index fd0c52b3554f..323342ca823d 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -71,18 +71,13 @@ bool MatchesFilterCondition(net::URLRequest* request, // Overloaded by multiple types to fill the |details| object. void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) { + FillRequestDetails(details, request); details->SetInteger("id", request->identifier()); - details->SetString("url", request->url().spec()); - details->SetString("method", request->method()); details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); auto info = content::ResourceRequestInfo::ForRequest(request); details->SetString("resourceType", info ? ResourceTypeToString(info->GetResourceType()) : "other"); - std::unique_ptr list(new base::ListValue); - GetUploadData(list.get(), request); - if (!list->empty()) - details->Set("uploadData", std::move(list)); } void ToDictionary(base::DictionaryValue* details, diff --git a/atom/browser/net/js_asker.cc b/atom/browser/net/js_asker.cc index 8362c1add1c6..67fb578650fb 100644 --- a/atom/browser/net/js_asker.cc +++ b/atom/browser/net/js_asker.cc @@ -44,7 +44,7 @@ void HandlerCallback(const BeforeStartCallback& before_start, void AskForOptions(v8::Isolate* isolate, const JavaScriptHandler& handler, - net::URLRequest* request, + std::unique_ptr request_details, const BeforeStartCallback& before_start, const ResponseCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -53,7 +53,7 @@ void AskForOptions(v8::Isolate* isolate, v8::Local context = isolate->GetCurrentContext(); v8::Context::Scope context_scope(context); handler.Run( - request, + *(request_details.get()), mate::ConvertToV8(isolate, base::Bind(&HandlerCallback, before_start, callback))); } diff --git a/atom/browser/net/js_asker.h b/atom/browser/net/js_asker.h index 6301f4de2c2a..df64a2175049 100644 --- a/atom/browser/net/js_asker.h +++ b/atom/browser/net/js_asker.h @@ -5,6 +5,7 @@ #ifndef ATOM_BROWSER_NET_JS_ASKER_H_ #define ATOM_BROWSER_NET_JS_ASKER_H_ +#include "atom/common/native_mate_converters/net_converter.h" #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" @@ -19,7 +20,7 @@ namespace atom { using JavaScriptHandler = - base::Callback)>; + base::Callback)>; namespace internal { @@ -31,7 +32,7 @@ using ResponseCallback = // Ask handler for options in UI thread. void AskForOptions(v8::Isolate* isolate, const JavaScriptHandler& handler, - net::URLRequest* request, + std::unique_ptr request_details, const BeforeStartCallback& before_start, const ResponseCallback& callback); @@ -67,12 +68,15 @@ class JsAsker : public RequestJob { private: // RequestJob: void Start() override { + std::unique_ptr request_details( + new base::DictionaryValue); + FillRequestDetails(request_details.get(), RequestJob::request()); content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::Bind(&internal::AskForOptions, isolate_, handler_, - RequestJob::request(), + base::Passed(&request_details), base::Bind(&JsAsker::BeforeStartInUI, weak_factory_.GetWeakPtr()), base::Bind(&JsAsker::OnResponse, diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 28eddfa61235..7fda39ce35e8 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.1 + 1.2.2 CFBundleShortVersionString - 1.2.1 + 1.2.2 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 7e386d319d9d..b82f28a47899 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,1,0 - PRODUCTVERSION 1,2,1,0 + FILEVERSION 1,2,2,0 + PRODUCTVERSION 1,2,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.2.1" + VALUE "FileVersion", "1.2.2" 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.1" + VALUE "ProductVersion", "1.2.2" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index 24098914b7cb..642721e6bd35 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -33,7 +33,7 @@ Role kRolesMap[] = { { @selector(copy:), "copy" }, { @selector(paste:), "paste" }, { @selector(delete:), "delete" }, - { @selector(pasteAndMatchStyle:), "paste-and-match-style" }, + { @selector(pasteAndMatchStyle:), "pasteandmatchstyle" }, { @selector(selectAll:), "selectall" }, { @selector(performMiniaturize:), "minimize" }, { @selector(performClose:), "close" }, diff --git a/atom/browser/ui/win/taskbar_host.cc b/atom/browser/ui/win/taskbar_host.cc index f7841cfa856c..78bcc578c19f 100644 --- a/atom/browser/ui/win/taskbar_host.cc +++ b/atom/browser/ui/win/taskbar_host.cc @@ -54,7 +54,7 @@ TaskbarHost::~TaskbarHost() { bool TaskbarHost::SetThumbarButtons( HWND window, const std::vector& buttons) { - if (buttons.size() > kMaxButtonsCount || !InitailizeTaskbar()) + if (buttons.size() > kMaxButtonsCount || !InitializeTaskbar()) return false; callback_map_.clear(); @@ -118,7 +118,7 @@ bool TaskbarHost::SetThumbarButtons( } bool TaskbarHost::SetProgressBar(HWND window, double value) { - if (!InitailizeTaskbar()) + if (!InitializeTaskbar()) return false; HRESULT r; @@ -133,7 +133,7 @@ bool TaskbarHost::SetProgressBar(HWND window, double value) { bool TaskbarHost::SetOverlayIcon( HWND window, const gfx::Image& overlay, const std::string& text) { - if (!InitailizeTaskbar()) + if (!InitializeTaskbar()) return false; base::win::ScopedHICON icon( @@ -152,7 +152,7 @@ bool TaskbarHost::HandleThumbarButtonEvent(int button_id) { return false; } -bool TaskbarHost::InitailizeTaskbar() { +bool TaskbarHost::InitializeTaskbar() { if (FAILED(taskbar_.CreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER)) || diff --git a/atom/browser/ui/win/taskbar_host.h b/atom/browser/ui/win/taskbar_host.h index 185b88a6b5b6..0cd5aa714fc2 100644 --- a/atom/browser/ui/win/taskbar_host.h +++ b/atom/browser/ui/win/taskbar_host.h @@ -44,8 +44,8 @@ class TaskbarHost { bool HandleThumbarButtonEvent(int button_id); private: - // Initailize the taskbar object. - bool InitailizeTaskbar(); + // Initialize the taskbar object. + bool InitializeTaskbar(); using CallbackMap = std::map; CallbackMap callback_map_; diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index a32e23de68f5..8a7eee09201f 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -166,6 +166,13 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( command_line->AppendSwitchASCII(::switches::kEnableBlinkFeatures, blink_features); + // Disable blink features. + std::string disable_blink_features; + if (web_preferences.GetString(options::kDisableBlinkFeatures, + &disable_blink_features)) + command_line->AppendSwitchASCII(::switches::kDisableBlinkFeatures, + disable_blink_features); + // The initial visibility state. NativeWindow* window = NativeWindow::FromWebContents(web_contents); diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 455bfacbfc11..fc11a68b2915 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 1 +#define ATOM_PATCH_VERSION 2 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h index f84f6ac15516..2cd932bd55ad 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.63" +#define CHROME_VERSION_STRING "51.0.2704.84" #define CHROME_VERSION "v" CHROME_VERSION_STRING #endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/atom/common/native_mate_converters/net_converter.cc b/atom/common/native_mate_converters/net_converter.cc index 772ae6e22087..71bc50fde7d3 100644 --- a/atom/common/native_mate_converters/net_converter.cc +++ b/atom/common/native_mate_converters/net_converter.cc @@ -22,22 +22,6 @@ namespace mate { -// static -v8::Local Converter::ToV8( - v8::Isolate* isolate, const net::URLRequest* val) { - std::unique_ptr dict(new base::DictionaryValue); - dict->SetString("method", val->method()); - std::string url; - if (!val->url_chain().empty()) url = val->url().spec(); - dict->SetStringWithoutPathExpansion("url", url); - dict->SetString("referrer", val->referrer()); - std::unique_ptr list(new base::ListValue); - atom::GetUploadData(list.get(), val); - if (!list->empty()) - dict->Set("uploadData", std::move(list)); - return mate::ConvertToV8(isolate, *(dict.get())); -} - // static v8::Local Converter::ToV8( v8::Isolate* isolate, const net::AuthChallengeInfo* val) { @@ -69,6 +53,19 @@ v8::Local Converter>::ToV8( namespace atom { +void FillRequestDetails(base::DictionaryValue* details, + const net::URLRequest* request) { + details->SetString("method", request->method()); + std::string url; + if (!request->url_chain().empty()) url = request->url().spec(); + details->SetStringWithoutPathExpansion("url", url); + details->SetString("referrer", request->referrer()); + std::unique_ptr list(new base::ListValue); + GetUploadData(list.get(), request); + if (!list->empty()) + details->Set("uploadData", std::move(list)); +} + void GetUploadData(base::ListValue* upload_data_list, const net::URLRequest* request) { const net::UploadDataStream* upload_data = request->get_upload(); diff --git a/atom/common/native_mate_converters/net_converter.h b/atom/common/native_mate_converters/net_converter.h index b7fd9481a207..37e42806955d 100644 --- a/atom/common/native_mate_converters/net_converter.h +++ b/atom/common/native_mate_converters/net_converter.h @@ -9,6 +9,7 @@ #include "native_mate/converter.h" namespace base { +class DictionaryValue; class ListValue; } @@ -20,12 +21,6 @@ class X509Certificate; namespace mate { -template<> -struct Converter { - static v8::Local ToV8(v8::Isolate* isolate, - const net::URLRequest* val); -}; - template<> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, @@ -42,6 +37,9 @@ struct Converter> { namespace atom { +void FillRequestDetails(base::DictionaryValue* details, + const net::URLRequest* request); + void GetUploadData(base::ListValue* upload_data_list, const net::URLRequest* request); diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index de130eee13eb..d370f0ef859f 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -107,8 +107,12 @@ const char kOpenerID[] = "openerId"; const char kScrollBounce[] = "scrollBounce"; // Enable blink features. +// TODO(kevinsawicki) Rename to enableBlinkFeatures in 2.0 const char kBlinkFeatures[] = "blinkFeatures"; +// Disable blink features. +const char kDisableBlinkFeatures[] = "disableBlinkFeatures"; + } // namespace options namespace switches { @@ -125,6 +129,9 @@ const char kPpapiFlashVersion[] = "ppapi-flash-version"; // Disable HTTP cache. const char kDisableHttpCache[] = "disable-http-cache"; +// The list of standard schemes. +const char kStandardSchemes[] = "standard-schemes"; + // Register schemes to handle service worker. const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index fadde79f18c8..5924c0549119 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -58,6 +58,7 @@ extern const char kExperimentalCanvasFeatures[]; extern const char kOpenerID[]; extern const char kScrollBounce[]; extern const char kBlinkFeatures[]; +extern const char kDisableBlinkFeatures[]; } // namespace options @@ -70,6 +71,7 @@ extern const char kEnablePlugins[]; extern const char kPpapiFlashPath[]; extern const char kPpapiFlashVersion[]; extern const char kDisableHttpCache[]; +extern const char kStandardSchemes[]; extern const char kRegisterServiceWorkerSchemes[]; extern const char kSSLVersionFallbackMin[]; extern const char kCipherSuiteBlacklist[]; diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index ee2dd7d54e04..99b00227c3b1 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -122,6 +122,16 @@ bool IsDevToolsExtension(content::RenderFrame* render_frame) { AtomRendererClient::AtomRendererClient() : node_bindings_(NodeBindings::Create(false)), atom_bindings_(new AtomBindings) { + // Parse --standard-schemes=scheme1,scheme2 + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + std::string custom_schemes = command_line->GetSwitchValueASCII( + switches::kStandardSchemes); + if (!custom_schemes.empty()) { + std::vector schemes_list = base::SplitString( + custom_schemes, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + for (const std::string& scheme : schemes_list) + url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT); + } } AtomRendererClient::~AtomRendererClient() { diff --git a/default_app/main.js b/default_app/main.js index e26c70590b1a..37f69cd93b89 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -8,11 +8,14 @@ const url = require('url') // Parse command line options. const argv = process.argv.slice(1) -const option = { file: null, help: null, version: null, webdriver: null, modules: [] } +const option = { file: null, help: null, version: null, abi: null, webdriver: null, modules: [] } for (let i = 0; i < argv.length; i++) { if (argv[i] === '--version' || argv[i] === '-v') { option.version = true break + } else if (argv[i] === '--abi') { + option.abi = true + break } else if (argv[i].match(/^--app=/)) { option.file = argv[i].split('=')[1] break @@ -201,7 +204,7 @@ app.once('ready', () => { type: 'separator' }, { - label: 'Quit', + label: 'Quit ' + app.getName(), accelerator: 'Command+Q', click () { app.quit() } } @@ -304,6 +307,9 @@ if (option.file && !option.webdriver) { } else if (option.version) { console.log('v' + process.versions.electron) process.exit(0) +} else if (option.abi) { + console.log(process.versions.modules) + process.exit(0) } else if (option.help) { const helpMessage = `Electron ${process.versions.electron} - Build cross platform desktop apps with JavaScript, HTML, and CSS @@ -321,7 +327,8 @@ if (option.file && !option.webdriver) { -h, --help Print this usage message. -i, --interactive Open a REPL to the main process. -r, --require Module to preload (option can be repeated) - -v, --version Print the version.` + -v, --version Print the version. + --abi Print the application binary interface.` console.log(helpMessage) process.exit(0) } else if (option.interactive) { diff --git a/docs-translations/ko-KR/api/ipc-renderer.md b/docs-translations/ko-KR/api/ipc-renderer.md index 3584d41b556d..a112e0ba4484 100644 --- a/docs-translations/ko-KR/api/ipc-renderer.md +++ b/docs-translations/ko-KR/api/ipc-renderer.md @@ -12,7 +12,7 @@ `ipcRenderer`는 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다: -### `ipcMain.on(channel, listener)` +### `ipcRenderer.on(channel, listener)` * `channel` String * `listener` Function @@ -20,7 +20,7 @@ `channel`에 대해 이벤트를 리스닝합니다. 새로운 메시지가 도착하면 `listener`가 `listener(event, args...)` 형식으로 호출됩니다. -### `ipcMain.once(channel, listener)` +### `ipcRenderer.once(channel, listener)` * `channel` String * `listener` Function @@ -28,7 +28,7 @@ 이벤트에 대해 한 번만 작동하는 `listener` 함수를 등록합니다. 이 `listener`는 등록된 후 `channel`에 보내지는 메시지에 한해 호출됩니다. 호출된 이후엔 리스너가 삭제됩니다. -### `ipcMain.removeListener(channel, listener)` +### `ipcRenderer.removeListener(channel, listener)` * `channel` String * `listener` Function @@ -39,7 +39,7 @@ 지정한 `channel`에 대한 리스너를 저장하는 배열에서 지정한 `listener`를 삭제합니다. -### `ipcMain.removeAllListeners(channel)` +### `ipcRenderer.removeAllListeners(channel)` * `channel` String (optional) diff --git a/docs-translations/ko-KR/api/remote.md b/docs-translations/ko-KR/api/remote.md index b8489d119e72..4b2c9be14996 100644 --- a/docs-translations/ko-KR/api/remote.md +++ b/docs-translations/ko-KR/api/remote.md @@ -76,7 +76,7 @@ exports.withLocalCallback = () => { ```javascript // 렌더러 프로세스 -const mapNumbers = require('remote').require('./mapNumbers'); +const mapNumbers = require('electron').remote.require('./mapNumbers'); const withRendererCb = mapNumbers.withRendererCallback(x => x + 1); diff --git a/docs-translations/ko-KR/api/web-contents.md b/docs-translations/ko-KR/api/web-contents.md index 41592011613c..467c019a7ef7 100644 --- a/docs-translations/ko-KR/api/web-contents.md +++ b/docs-translations/ko-KR/api/web-contents.md @@ -284,6 +284,15 @@ Returns: ``` +### Event: 'update-target-url' + +Returns: + +* `event` Event +* `url` URL + +마우스나 키보드를 사용해 링크에 포커스를 할 때 발생하는 이벤트입니다. + ### Event: 'cursor-changed' Returns: @@ -591,6 +600,12 @@ CSS 코드를 현재 웹 페이지에 삽입합니다. 웹 페이지에서 `replaceMisspelling` 편집 커맨드를 실행합니다. +### `webContents.insertText(text)` + +* `text` String + +포커스된 요소에 `text`를 삽입합니다. + ### `webContents.findInPage(text[, options])` * `text` String - 찾을 콘텐츠, 반드시 공백이 아니여야 합니다. diff --git a/docs-translations/ko-KR/tutorial/application-distribution.md b/docs-translations/ko-KR/tutorial/application-distribution.md index fedd613c0f11..66f729a18e0f 100644 --- a/docs-translations/ko-KR/tutorial/application-distribution.md +++ b/docs-translations/ko-KR/tutorial/application-distribution.md @@ -117,25 +117,7 @@ MyApp.app/Contents 또한 Electron 소스 코드를 다시 빌드할 때 어플리케이션 이름을 변경할 수 있습니다. -`GYP_DEFINES` 환경변수를 사용하여 다음과 같이 다시 빌드할 수 있습니다: - -__Windows__ - -```bash -> set "GYP_DEFINES=project_name=myapp product_name=MyApp" -> python script\clean.py -> python script\bootstrap.py -> python script\build.py -c R -t myapp -``` - -__Bash__ - -```bash -$ export GYP_DEFINES="project_name=myapp product_name=MyApp" -$ script/clean.py -$ script/bootstrap.py -$ script/build.py -c Release -t myapp -``` +`atom.gyp` 파일을 수정하여 다음과 같이 다시 빌드할 수 있습니다: ### grunt-build-atom-shell @@ -175,16 +157,16 @@ Electron의 개발자로써, Electron을 매우 많은 시나리오에서도 작 3. 다음의 환경 변수들을 설정합니다: -* `ELECTRON_GITHUB_TOKEN` - GitHub에 릴리즈를 만들 수 있는 토큰. -* `ELECTRON_S3_ACCESS_KEY`, `ELECTRON_S3_BUCKET`, `ELECTRON_S3_SECRET_KEY` - + * `ELECTRON_GITHUB_TOKEN` - GitHub에 릴리즈를 만들 수 있는 토큰. + * `ELECTRON_S3_ACCESS_KEY`, `ELECTRON_S3_BUCKET`, `ELECTRON_S3_SECRET_KEY` - node.js 헤더 뿐만 아니라 심볼을 업로드할 장소. -* `ELECTRON_RELEASE` - `true`로 지정하고 업로드 부분이 실행되면, 설정되지 않은 + * `ELECTRON_RELEASE` - `true`로 지정하고 업로드 부분이 실행되면, 설정되지 않은 부분을 남기고 `surf-build`가 CI-type 확인을 실행합니다. 모든 pull request를 실행할 때 적합합니다. -* `CI` - `true` 또는 다른 것을 지정하면 실패합니다. -* `GITHUB_TOKEN` - `ELECTRON_GITHUB_TOKEN`와 같게 설정 -* `SURF_TEMP` - Windows에선 `C:\Temp`로 설정하면 긴 경로 문제를 해결할 수 있습니다. -* `TARGET_ARCH` - `ia32` 또는 `x64`를 지정. + * `CI` - `true` 또는 다른 것을 지정하면 실패합니다. + * `GITHUB_TOKEN` - `ELECTRON_GITHUB_TOKEN`와 같게 설정 + * `SURF_TEMP` - Windows에선 `C:\Temp`로 설정하면 긴 경로 문제를 해결할 수 있습니다. + * `TARGET_ARCH` - `ia32` 또는 `x64`를 지정. 4. Electron에 기여를 하는 기여자라면, _반드시_ `script/upload.py`에서 포크를 위해 `ELECTRON_REPO`를 설정해야 합니다. (`MYORG/electron`) diff --git a/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md b/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md index d163cb5e5c79..439909ba4c14 100644 --- a/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md +++ b/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md @@ -120,6 +120,21 @@ productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RES 문서를 참고하여 기본적인 개념을 이해해야 합니다. 그리고 자격(plist) 파일에 어플리케이션에서 요구하는 권한의 키를 추가합니다. +그 외에 [electron-osx-sign][electron-osx-sign] 모듈을 이용해서 직접 서명할 수도 있습니다. + +#### 네이티브 모듈 서명하기 + +앱 내부에서 사용한 네이티브 모듈들도 서명이 필요합니다. +electron-osx-sign 을 사용한다면, 앱 실행 인수 목록에 경로를 반드시 지정해야 합니다. + +```bash +electron-osx-sign YourApp.app YourApp.app/Contents/Resources/app/node_modules/nativemodule/build/release/nativemodule +``` + +참고할 점은 네이티브 모듈이 의도하지 않았지만 오브젝트 파일(.o)을 포함하는 경우도 있습니다. +이 경우 오브젝트 파일들의 서명을 해야할 수도 있습니다. +[electron-packager][electron-packager]를 사용한다면, 빌드 과정에 `--ignore=.+\.o$` 코드를 추가해 해당 파일을 무시해줍시다. + ### 어플리케이션 업로드 어플리케이션 서명을 완료한 후 iTunes Connect에 업로드하기 위해 Application Loader를 @@ -190,6 +205,8 @@ ERN의 승인을 얻는 방법은, 다음 글을 참고하는 것이 좋습니 [nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps [enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html [create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html +[electron-osx-sign]: https://github.com/electron-userland/electron-osx-sign +[electron-packager]: https://github.com/electron-userland/electron-packager [submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html [app-sandboxing]: https://developer.apple.com/app-sandboxing/ [ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ diff --git a/docs-translations/ko-KR/tutorial/quick-start.md b/docs-translations/ko-KR/tutorial/quick-start.md index 48e76d5cd54f..d96a430ecc93 100644 --- a/docs-translations/ko-KR/tutorial/quick-start.md +++ b/docs-translations/ko-KR/tutorial/quick-start.md @@ -41,7 +41,7 @@ API를 사용하여 low-level 수준으로 운영체제와 상호작용할 수 프로세스에서 그 작업을 처리할 수 있도록 메인 프로세스와 통신을 해야 합니다. Electron에는 메인 프로세스와 렌더러 프로세스 사이에 통신을 할 수 있도록 -[ipc](../api/ipc-renderer.md) 모듈을 제공하고 있습니다. +[ipcRenderer](../api/ipc-renderer.md)와 [ipcMain](../api/ipc-main.md) 모듈을 제공하고 있습니다. 또는 [remote](../api/remote.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다. 또한 FAQ에서 [다양한 객체를 공유하는 방법](share-data)도 소개하고 있습니다. diff --git a/docs-translations/ru-RU/README.md b/docs-translations/ru-RU/README.md index 4e7ed443468a..d2e9d532246d 100644 --- a/docs-translations/ru-RU/README.md +++ b/docs-translations/ru-RU/README.md @@ -1,10 +1,10 @@ -Пожалуйста, убедитесь, что вы используете документацию, которые соответствует вашей версии Electron. -Номер версии должен быть частью адреса страницы. Если это не так, вы -возможно, используете документацию ветки разработки, которая может содержать изменения api, +Пожалуйста, убедитесь, что Вы используете документацию, которая соответствует вашей версии Electron. +Номер версии должен быть частью адреса страницы. Если это не так, Вы +возможно, используете документацию ветки разработки, которая может содержать изменения api, которые не совместимы с вашей версией Electron. Если это так, Вы можете переключиться на другую версию документации в списке -[доступные версии](http://electron.atom.io/docs/) на atom.io, или -если вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и +[доступные версии](http://electron.atom.io/docs/) на [atom.io](atom.io), или +если Вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и выберите тег, который соответствует вашей версии. ## Руководства @@ -13,7 +13,7 @@ * [Application Distribution](tutorial/application-distribution.md) * [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md) * [Application Packaging](tutorial/application-packaging.md) -* [Using Native Node Modules](tutorial/using-native-node-modules.md) +* [Использование нативных модулей NodeJS](tutorial/using-native-node-modules.md) * [Отладка главного процесса](tutorial/debugging-main-process.md) * [Использование Selenium и WebDriver](tutorial/using-selenium-and-webdriver.md) * [DevTools Extension](tutorial/devtools-extension.md) @@ -22,8 +22,8 @@ ## Учебники * [Быстрый старт](tutorial/quick-start.md) -* [Desktop Environment Integration](tutorial/desktop-environment-integration.md) -* [Online/Offline Event Detection](tutorial/online-offline-events.md) +* [Интеграция рабочего окружения](tutorial/desktop-environment-integration.md) +* [Определение Онлайн/Оффлайн состояния](tutorial/online-offline-events.md) ## API References @@ -37,7 +37,7 @@ * [`` Tag](api/web-view-tag.md) * [`window.open` Function](api/window-open.md) -### Modules for the Main Process: +### Модули для Main Process: * [app](api/app.md) * [autoUpdater](api/auto-updater.md) @@ -61,7 +61,7 @@ * [remote](api/remote.md) * [webFrame](api/web-frame.md) -### Modules for Both Processes: +### Модули для обоих процессов: * [clipboard](api/clipboard.md) * [crashReporter](api/crash-reporter.md) @@ -72,11 +72,11 @@ ## Разработка * [Стиль кодирования](development/coding-style.md) -* [Source Code Directory Structure](development/source-code-directory-structure.md) +* [Структура папок с исходным кодом](development/source-code-directory-structure.md) * [Technical Differences to NW.js (formerly node-webkit)](development/atom-shell-vs-node-webkit.md) * [Обзор системы сборки](development/build-system-overview.md) * [Инструкции по сборке (OS X)](development/build-instructions-osx.md) * [Инструкции по сборке (Windows)](development/build-instructions-windows.md) * [Инструкции по сборке (Linux)](development/build-instructions-linux.md) * [Настройка сервера символов для отладчика](development/setting-up-symbol-server.md) - + diff --git a/docs-translations/ru-RU/tutorial/application-distribution.md b/docs-translations/ru-RU/tutorial/application-distribution.md index a43be71f65a9..5ed04b5e767d 100644 --- a/docs-translations/ru-RU/tutorial/application-distribution.md +++ b/docs-translations/ru-RU/tutorial/application-distribution.md @@ -1,11 +1,11 @@ # Распространение приложения Чтобы разпространять ваше приложение на Electron, папка с вашим приложением -должна называться `app` и находиться в папке ресурсов Electron (на OS X это -`Electron.app/Contents/Resources/`, на Linux и Windows - `resources/`), +должна называться `app` и находиться в папке ресурсов Electron (в OS X это +`Electron.app/Contents/Resources/`, в Linux и Windows - `resources/`), вот так: -На OS X: +Для OS X: ```text electron/Electron.app/Contents/Resources/app/ @@ -14,7 +14,7 @@ electron/Electron.app/Contents/Resources/app/ └── index.html ``` -На Windows и Linux: +Для Windows и Linux: ```text electron/resources/app @@ -23,13 +23,13 @@ electron/resources/app └── index.html ``` -Затем запустите `Electron.app` (или `electron` на Linux, `electron.exe` на Windows), +Затем запустите `Electron.app` (или `electron` в Linux, `electron.exe` в Windows), и Electron запустится как ваше приложение. Теперь папка `electron` и есть дистрибутив, -который вы должны распространять пользователям. +который Вы должны распространять пользователям. ## Упаковка вашего приложения в файл -Если вы не хотите распространять исходные коды вашего проект, вы можете +Если Вы `не хотите` распространять исходные коды вашего проекта, Вы можете упаковать его в архив [asar](https://github.com/atom/asar), чтобы не показывать пользователям исходные коды. @@ -38,25 +38,26 @@ electron/resources/app после чего Electron попробует считать ресурсы и запустить архив. -На OS X: +Для OS X: ```text electron/Electron.app/Contents/Resources/ └── app.asar ``` -На Windows и Linux: +Для Windows и Linux: ```text electron/resources/ └── app.asar ``` -Больше деталей можна найти в [инстуркции по упаковке приложения](application-packaging.md). +Больше деталей можно найти в [инстуркции по упаковке приложения](application-packaging.md). ## Ребрендирование скачанных исполняемых файлов -После того, как вы подключили ваше приложение к Electron, -вам наверняка захочеться ребрендировать его перед распространением. + +После того, как Вы подключили ваше приложение к Electron, +Вам наверняка захочется ребрендировать его перед распространением. ### Windows @@ -73,7 +74,7 @@ electron/resources/ * `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist` Вы таже можете переименовать приложение-помощник, чтобы оно не показывало `Electron Helper`, -убедитесь, что вы переименовали его исполняемый файл. +убедитесь, что Вы переименовали его исполняемый файл. Структура переименованного приложения выглядит примерно так: @@ -104,22 +105,22 @@ MyApp.app/Contents ## Rebranding by Rebuilding Electron from Source Вы также можете ребрендировать Electron изменив имя продукта и собрав его -из исходных кодов. Чтобы сделать это вам нужно изменить `atom.gyp` и полностью +из исходных кодов. Чтобы сделать это Вам нужно изменить `atom.gyp` и полностью пересобрать Electron. ### grunt-build-atom-shell Проверка и пересборка кода Electron довольно сложная задача, так что мы -мы сделали файл-инструкцию для Grunt, который будет делать это автоматически: +сделали файл-инструкцию для Grunt, который будет делать это автоматически: [grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell). -Этот файл автоматически просмотрит изменения в `.gyp` фалле, соберёт +Этот файл автоматически просмотрит изменения в `.gyp` файле, соберёт Electron из исходных кодов и пересоберёт модули Node, чтобы всё подходило под новое имя. ## Инструменты -Вы также можете использовать инструменты оттретьих лиц, +Вы также можете использовать инструменты от третьих лиц, которые сделают работу за вас: * [electron-packager](https://github.com/maxogden/electron-packager) diff --git a/docs-translations/ru-RU/tutorial/application-packaging.md b/docs-translations/ru-RU/tutorial/application-packaging.md index b63565ed7b8e..6488ea1e19db 100644 --- a/docs-translations/ru-RU/tutorial/application-packaging.md +++ b/docs-translations/ru-RU/tutorial/application-packaging.md @@ -1,7 +1,7 @@ # Упаковка приложения Чтобы смягчить [проблемы](https://github.com/joyent/node/issues/6960) с длинными -именами под Windows, немного ускорить `require` и скрыть ваши исходные коды, вы +именами под Windows, немного ускорить `require` и скрыть ваши исходные коды, Вы можете упаковать его в архив [asar][asar], немного поменяв исходный код. ## Генерация архива `asar` @@ -25,16 +25,16 @@ $ asar pack your-app app.asar ## Использование архивов `asar` -В Electron есть два вида API: API Node, которые устанавливаются с помощью Node.Js и +В Electron есть два вида API: API Node, которые устанавливаются с помощью Node.JS и веб API, которые предоставляюся Chromium. Оба предоставляют возможность считывать из архивов `asar`. ### Node API -С специальными патчами в Electron, части Node API вроде `fs.readFile` и `require` +Со специальными патчами в Electron, части Node API вроде `fs.readFile` и `require` считают архивы `asar` виртуальными папками и файлы в них доступны как в обычных. -Например, у нас есть арихив `example.asar` в `/path/to`: +Например, у нас есть архив `example.asar` в `/path/to`: ```bash $ asar list /path/to/example.asar @@ -60,13 +60,13 @@ const fs = require('fs'); fs.readdirSync('/path/to/example.asar'); ``` -Ичпользуем модуль из архива: +Используем модуль из архива: ```javascript require('/path/to/example.asar/dir/module.js'); ``` -Вы также можете показывать веб страницы из архива `asar` через `BrowserWindow`: +Вы также можете показывать веб-страницы из архива `asar` через `BrowserWindow`: ```javascript const BrowserWindow = require('electron').BrowserWindow; @@ -76,7 +76,7 @@ win.loadURL('file:///path/to/example.asar/static/index.html'); ### Веб API -На веб страницах файлы запрашиваются с помощью протокола `file:`. Как и в Node API +На веб-страницах файлы запрашиваются с помощью протокола `file:`. Как и в Node API архивы `asar` считаются за директории. Пример получения файла с помощью `$.get`: @@ -93,7 +93,7 @@ $.get('file:///path/to/example.asar/file.txt', function(data) { ### Использование архива `asar` в качестве обычного файла -Для случаев, когда вам, например, нужно проверить хэш-сумму архива `asar`, +Для случаев, когда Вам, например, нужно проверить хэш-сумму архива `asar`, нужно использовать архив как файл. Для этой цели существует встроенный модуль `original-fs`, который предоставляет доступ к `fs` без поддежки `asar`: @@ -101,6 +101,7 @@ $.get('file:///path/to/example.asar/file.txt', function(data) { var originalFs = require('original-fs'); originalFs.readFileSync('/path/to/example.asar'); ``` + Вы также можете выставить `process.noAsar` в `true`, чтобы выключить поддержку `asar` в модуле `fs`: @@ -112,7 +113,7 @@ fs.readFileSync('/path/to/example.asar'); ## Ограничения Node API Хотя мы и старались как могли, чтобы сделать `asar` максимально похожим на папки, -всё ещё существуют некоторые ограничения из-за низкоуровневой натуры Node API. +всё ещё существуют некоторые ограничения из-за низкоуровневой архитектуры Node API. ### Архивы только для чтения @@ -121,8 +122,8 @@ fs.readFileSync('/path/to/example.asar'); ### Нельзя установить рабочую директорию в архиве -Хотя архивы `asar` и считаются папками, они ими на самом деле не являются, -так что вы не можете установить в них рабочую директорию. Передача +Хотя архивы `asar` и считаются папками, они ими на самом деле не являются ими, +так что Вы не можете установить в них рабочую директорию. Передача архивов `asar` в качестве аргумента `cwd` некоторым API также может вызывать ошибки. @@ -154,18 +155,18 @@ API которым нужна распаковка: `child_process.spawn` и `child_process.execFile`, но только `execFile` может исполнять файлы из архивов `asar`. -Так вышло потому, что `exec` и `spawn` принимают `команду` а не `файл` как параметр, +Так вышло потому, что `exec` и `spawn` принимают `команду`, а не `файл` как параметр, а `команды` исполняются в оболочке. Нет никакой реальной возможности проверить, реален ли файл или находится в архиве `asar`, и даже если мы смогли бы проверить, то неясно, как земенить путь к файлу без побочных эффектов. ## Добавление распакованых файлов в архив `asar` -Как говорилось выше, некоторые API Node будут распаковывать файлв, +Как говорилось выше, некоторые API Node будут распаковывать файлы, чтобы их использовать. Кроме увеличенного потребления ресурсов это также -может вызвать предупрждения от антивирусов. +может вызвать предупреждения от антивирусов. -Чтобы обойти это, вы можете распаковать некоторые файлы, создавая архивы, +Чтобы обойти это, Вы можете распаковать некоторые файлы, создавая архивы, с помощью опции `--unpack`. Пример показывает распаковку нативных модулей: ```bash diff --git a/docs-translations/ru-RU/tutorial/mac-app-store-submission-guide.md b/docs-translations/ru-RU/tutorial/mac-app-store-submission-guide.md index 480ec35f760b..598f32337288 100644 --- a/docs-translations/ru-RU/tutorial/mac-app-store-submission-guide.md +++ b/docs-translations/ru-RU/tutorial/mac-app-store-submission-guide.md @@ -1,7 +1,7 @@ #Руководство по утверждению вашего приложения в App Store -Начиная с версии v0.34.0 Electron позволяет вам сформировать данные для App Store к вашему приложению. +Начиная с версии v0.34.0 Electron позволяет Вам сформировать данные для App Store к вашему приложению. Данное руководство представляет собой пошаговую инструкцию по созданию данных файлов. Помните, что когда Вы подаете свое приложение на рассмотрение в App Store Вы должны обладать аккаунтом разработчика, @@ -9,9 +9,7 @@ ## Как отправить свое приложение на рассмотрение в App Store -Последующие шаги подробно описывают, что и в какой последовательности Вам требуется сделать, но не гарантируют что Ваше приложение -будет рассмотрено Apple. Мы так же рекомендуем вам прочитать официальную документацию по оформлению своего приложения и информации к нему -чтобы пройти проверку в App Store. +Последующие шаги подробно описывают, что и в какой последовательности Вам требуется сделать, но не гарантируют, что Ваше приложение будет рассмотрено Apple. Мы также рекомендуем Вам прочитать официальную документацию по оформлению своего приложения и информации к нему, чтобы пройти проверку в App Store. ## Получение сертификата @@ -24,8 +22,9 @@ Во-первых, нам нужно подготовить два файла: - child.plist: - +`child.plist`: + +```xml @@ -36,9 +35,11 @@ - - parent.plist: - +``` + +`parent.plist`: + +```xml @@ -49,8 +50,11 @@ (allow mach-lookup (global-name-regex #"^org.chromium.Chromium.rohitfork.[0-9]+$")) +``` Затем подписываем свое приложение, с помощью специального сценария: + +```bash #!/bin/bash # Имя вашего приложения. @@ -79,42 +83,45 @@ codesign -s "$APP_KEY" -f --entitlements parent.plist "$APP_PATH" productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH" - -Если вы только начали разрабатывать под Mac OS X, то мы желаем Вам прочитать [App SandBox](https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html "Ссылка для новичков в разработке приложений для Mac OS X") +``` -## Обновление приложения +Если вы только начали разрабатывать под Mac OS X, то мы советуем Вам прочитать [App SandBox](https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html "Ссылка для новичков в разработке приложений для Mac OS X") -После того, как Вы подписали свое приложение вы сможете загрузить его в Itunes Connect для обработки, убедитесь, что вы создали [запись](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html "ссылка на показ как создавать запись в Itunes Connect") перед отправкой. +## Обновление приложения + +После того, как Вы подписали свое приложение Вы сможете загрузить его в Itunes Connect для обработки, убедитесь, что Вы создали [запись](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html "ссылка на показ как создавать запись в Itunes Connect") перед отправкой. ## Объяснение использования 'temporary-exception' -Когда песочница Apple временно исключила ваше приложение, согласно [документации](https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html "Документация по исключениям") вам нужно объяснить насколько это важное исключение: +Когда песочница Apple временно исключила ваше приложение, согласно [документации](https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html "Документация по исключениям") Вам нужно объяснить насколько это важное исключение: ->Примечание: если вы временно исключаете свое приложение, обязательно прочитайте и выполните рекомендации по правам на исключение. +>Примечание: если Вы временно исключаете свое приложение, обязательно прочитайте и выполните рекомендации по правам на исключение. >которые предоставляются в Itunes Connect. Самое важное указать почему ваше приложение должно быть исключенно. -Вы можете объяснить, что ваше приложение построено на основе браузера Chromium, который использует Mach из-за его мульти-процесс архитектуры. Но есть еще вероятность, что ваше приложение не удалось проверить именно из-за этого. +Вы можете объяснить, что ваше приложение построено на основе браузера Chromium, который использует Mach из-за его мультипроцессной архитектуры. Но есть еще вероятность, что ваше приложение не удалось проверить именно из-за этого. ## Отправка приложения на проверку Следующие шаги описаны в официальной [документации](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html "Официальная статья по отправке приложения на проверку") -#Ограничения в Mac App Store +# Ограничения в Mac App Store -Для того чтобы удовлетворить всем просьбам SandBox App Store , некотоыре из модулей были отключены: +Для того чтобы удовлетворить всем просьбам SandBox App Store, некоторые из модулей были отключены: - crashReporter - autoUpdater -и следующие проблемы были несколько изменены: + +А также следующие проблемы были несколько изменены: - Захват видео на некоторых машинах может не работать -- Некоторые специалньые возможности могут не работать +- Некоторые специальные возможности могут не работать - Приложения не будут в курсе изменения DNS -Так же из за использования SandBox App Store некоторые возможности могут быть не доступны или ограничены, подробнее о ограничениях + +Также из-за использования SandBox App Store некоторые возможности могут быть не доступны или ограничены, подробнее о ограничениях Вы можете прочитать в [документации](https://developer.apple.com/app-sandboxing/ "Ссылка на ограничения в SandBox AppStore") -# Криптографические алгоритмы котоыре использует Electron +# Криптографические алгоритмы которые использует Electron -Смотря в какой стране и городе Вы находитесь Apple может потребовать от Вас задокументировать алгоритмы криптографии котоыре вы используете -и если потребуется то попросит предоставить Вас копию регистрации вашего алгоритма шифрования. +Смотря в какой стране и городе Вы находитесь, Apple может потребовать от Вас задокументировать алгоритмы криптографии которые Вы используете +и если потребуется, то попросит Вас предоставить копию регистрации вашего алгоритма шифрования. Electron использует следующие алгоритмы шифрования: - AES - NIST SP 800-38A, NIST SP 800-38D, RFC 3394 @@ -141,5 +148,5 @@ Electron использует следующие алгоритмы шифров - RC5 - http://people.csail.mit.edu/rivest/Rivest-rc5rev.pdf - RIPEMD - ISO/IEC 10118-3 -Если Вы используете необычный алгоритм, то вот статья о том как получить разрешение на использование собственного алгоритма шифрования в +Если Вы используете необычный алгоритм, то вот статья о том, как получить разрешение на использование собственного алгоритма шифрования в рамках закона США - [статья](https://pupeno.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ "Статья о том как получить разрешение на свой алгоритм шифрования") diff --git a/docs-translations/ru-RU/tutorial/quick-start.md b/docs-translations/ru-RU/tutorial/quick-start.md index 07244ec9be19..b8041b5e856b 100644 --- a/docs-translations/ru-RU/tutorial/quick-start.md +++ b/docs-translations/ru-RU/tutorial/quick-start.md @@ -1,11 +1,11 @@ # Быстрый старт -Electron позволяет вам делать приложения для рабочего стола на чистом JavaScript, -предоставляя среду с богатым API. Можете представлять его как Node.js, который -ориентирован на рабочий стол, а не веб сервера. +Electron позволяет Вам делать приложения для рабочего стола на чистом JavaScript, +предоставляя среду с богатым API. Можете представлять его как Node.js приложение, которое +ориентировано для рабочего стола, а не для веб сервера. -Это, однако, не значит, что Electron — лишь привязки к GUI билиотекам. На деле -Electron использует веб-страницы как интерфейс, так что вы можете считать его +Однако это не значит, что Electron — лишь привязка к GUI билиотекам. На деле +Electron использует веб-страницы как интерфейс, так что Вы можете считать его небольшим Chroumium браузером, который контролируется с помощью JavaScript. ### Главный процесс @@ -18,11 +18,11 @@ __главным процессом__. Скрипт, который работа Так как Electron использует Chromium для показа веб-страниц, мульти-процессовая архитектура показа страниц Chromium тоже используется. -Каждая веб-страницы в Electron работает в своём собственном процессе, +Каждая веб-страница в Electron работает в своём собственном процессе, который называется __процесс-рендерер__. В обычных браузерах веб-страницы обычно запускаются в "песочнице" и им недоступны -реальные ресурсы компьютера. Пользователи Electron же могут использовать API +реальные ресурсы компьютера. Пользователи Electron напротив могут использовать API Node.js на страницах, что допускает более низкоуровневую работу с операционной системой. ### Разница мужду главным процессом и процессом-рендерером @@ -43,11 +43,11 @@ Node.js на страницах, что допускает более низко В Electron есть несолько способов общения между процессам. Например, модули [`ipcRenderer`](../api/ipc-renderer.md) и [`ipcMain`](../api/ipc-main.md) используются для отправки сообщений, а [remote](../api/remote.md) - для коммуникации в RPC стиле. -В ЧАВО также есть пункт о том, [как разделять информацию между страницами][share-data] +В FAQ также есть пункт о том, [как разделять информацию между страницами][share-data] ## Первое приложение на Electron -Как правило, приложение Electron структурировано следующим образом:: +Как правило, приложение Electron структурировано следующим образом: ```text your-app/ @@ -56,9 +56,9 @@ your-app/ └── index.html ``` -Формат `package.json` точно такой же, как у модулей Node и сприпт, объявленый +Формат `package.json` точно такой же, как у модулей Node и скрипт, объявленый как `main`, будет выполняться при запуске вашего приложения, работая в -главном процессе. Например, ваш `package.json` может выглядеть вот так: +главном процессе. Например, Ваш `package.json` может выглядеть вот так: ```json { @@ -82,12 +82,12 @@ const app = electron.app // Модуль, создающий окно приложения. const BrowserWindow = electron.BrowserWindow -// Удерживайте глобальное обращение к объекту окна, если вы так не сделаете, то +// Удерживайте глобальное обращение к объекту окна, если Вы так не сделаете, то // окно само закроется после того, как объект будет собран сборщиком мусора. let mainWindow function createWindow () { - // Создаём окно браузера. + // Создаём окно браузера mainWindow = new BrowserWindow({width: 800, height: 600}) // и загружаем index.html приложения. @@ -98,9 +98,9 @@ function createWindow () { // Будет выполнено, когда пользователь закроет окно mainWindow.on('closed', function () { - //Убрать обращение на объект окна, обычно стоит хранить окна в массиве, - //если ваше приложение поддерживает несколько, сейчас стоит удалить - //соответствующий элемент. + // Убрать обращение на объект окна, обычно стоит хранить окна в массиве, + // если ваше приложение поддерживает несколько, сейчас стоит удалить + // соответствующий элемент. mainWindow = null }) } @@ -114,7 +114,7 @@ app.on('ready', createWindow) // Выйти, если все окна закрыты app.on('window-all-closed', function () { //На OS X приложение и его строка меню обычно остаются активными, - //пока пользователь не завершит их с помощью Cmd + Q. + //пока пользователь не завершит их с помощью `Cmd + Q`. if (process.platform !== 'darwin') { app.quit() } @@ -129,12 +129,12 @@ app.on('activate', function () { } }) -//В этот файл вы можете включить остальной код вашего главного процесса. +//В этот файл Вы можете включить остальной код вашего главного процесса. //Вы также можете разложить его по отдельным файлам и подключить с помощью require. ``` -Наконец, `index.html`, страница, которую вы хотите показать: +Наконец, `index.html`, страница, которую Вы хотите показать: ```html @@ -154,22 +154,22 @@ app.on('activate', function () { ## Запуск вашего приложения -Когда вы создали `main.js`, `index.html` и `package.json` вас скорее всего захочется -запустить ваше приложение, чтобы проверить, что оно работает так, как надо. +После того как Вы создали `main.js`, `index.html` и `package.json` Вам скорее всего захочется +запустить приложение, чтобы проверить, что оно работает так, как надо. ### electron-prebuilt [`electron-prebuilt`](https://github.com/electron-userland/electron-prebuilt) — `npm` модуль, который содержит прекомпилированную версию Electron. -Если вы установили Electron глобально через `npm`, то вам нужно будет всего лишь +Если вы установили Electron глобально через `npm`, то Вам нужно будет всего лишь запустить сдедующее в папке вашего проекта: ```bash electron . ``` -Если вы установили Electron локально, то выполните это: +Если Вы установили Electron локально, то выполните это: ```bash ./node_modules/.bin/electron . @@ -177,7 +177,7 @@ electron . ### Исполняемые файлы Electron, скачанные вручную -Если вы скачали Electron вручную, то вы можете использовать +Если Вы скачали Electron вручную, то Вы можете использовать исполняемые файлы прямо в папке вашего проекта. #### Windows @@ -198,27 +198,27 @@ $ ./electron/electron your-app/ $ ./Electron.app/Contents/MacOS/Electron your-app/ ``` -`Electron.app` — часть реализного пакета Electron, вы можете скачать его +`Electron.app` — часть реализного пакета Electron, Вы можете скачать его [тут](https://github.com/electron/electron/releases). ### Запустить как дистрибутив -Когда вы закончили написание вашего приложения, вы можете создать +Когда Вы закончили написание вашего приложения, Вы можете создать дистрибутив, следуя инструкциям [отсюда](./application-distribution.md) и затем запустить полученное приложение. ### Попробуйте этот пример -Скопируйте и запустите этот обучающий код, ичпользуя репозиторий [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start) +Скопируйте и запустите этот обучающий код, используя репозиторий [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start) **Заметка**: Для запуска требуется [Git](https://git-scm.com) и [Node.js](https://nodejs.org/en/download/) (который включает в себя [npm](https://npmjs.org)). ```bash -# Склонируйте репозиторий +# Клонируем репозиторий $ git clone https://github.com/electron/electron-quick-start -# Перейдите в папку репозитория +# Переходим в папку скачанного репозитория $ cd electron-quick-start -# Установите зависимости и запустите +# Устанавливаем зависимости и запускаем $ npm install && npm start ``` diff --git a/docs-translations/ru-RU/tutorial/supported-platforms.md b/docs-translations/ru-RU/tutorial/supported-platforms.md index d927bc0a7b98..8321ddb7becc 100644 --- a/docs-translations/ru-RU/tutorial/supported-platforms.md +++ b/docs-translations/ru-RU/tutorial/supported-platforms.md @@ -1,15 +1,24 @@ -Платформы поддерживаемые Electron: +# Платформы поддерживаемые Electron: -#OS X -Поддерживает только 64-ых битные OS X. Минимально поддерживаемой версией является OS X 10.9 +Следующие платформы поддерживаются Electron: -#Windows -Поддерживаются операционные систем Windows 7 и выше, старые операционные системы не поддерживаются (и не работают). -Поддерживаются платформы на x86 и amd64 (64-разрядная) для Windows. Будьте внимательны, что ARM Windows не поддерживается на данный момент. +### OS X + +Поддерживает только 64-x битные OS X. Минимально поддерживаемой версией является OS X 10.9 + +### Windows + +Поддерживаются операционные системы Windows 7 и выше, старые операционные системы не поддерживаются (и не работают). + +Поддерживаются бинарники `x86` и `amd64` (x64) для Windows. Будьте внимательны, `ARM` версия Windows не поддерживается на данный момент. + +### Linux + +Подготовленные бинарники Electron для `ia32`(`i686`) и `x64`(`amd64`) архитектур сделаны на Ubuntu 12.04, +`arm` бинарник сделан на ARM v7 с hard-float ABI и NEON для Debian Wheezy. -#Linux -Поддерживает архитектуры ia32(i686 в) и x64(amd64). Гарантированно будет работать в дистрибутивах: -- Ubuntu 12.04 и выше -- Fedora 21 -- Debian 8 + +* Ubuntu 12.04 и выше +* Fedora 21 +* Debian 8 diff --git a/docs-translations/zh-CN/api/app.md b/docs-translations/zh-CN/api/app.md index 21c73cf836a9..24cb70e84206 100644 --- a/docs-translations/zh-CN/api/app.md +++ b/docs-translations/zh-CN/api/app.md @@ -32,7 +32,7 @@ app.on('window-all-closed', function() { 当所有的窗口都被关闭时触发。 -这个时间仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`, +这个事件仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`, 或者开发者调用了 `app.quit()` ,Electron 将会先尝试关闭所有的窗口再触发 `will-quit` 事件, 在这种情况下 `window-all-closed` 不会被触发。 diff --git a/docs-translations/zh-CN/tutorial/mac-app-store-submission-guide.md b/docs-translations/zh-CN/tutorial/mac-app-store-submission-guide.md index e7c7af817aa9..bf326169fb72 100644 --- a/docs-translations/zh-CN/tutorial/mac-app-store-submission-guide.md +++ b/docs-translations/zh-CN/tutorial/mac-app-store-submission-guide.md @@ -1,26 +1,36 @@ # Mac App Store 应用提交向导 -自从 v0.34.0, Electron 就允许提交应用包到 Mac App Store -(MAS) . 这个向导提供的信息有 : 如何提交应用和 MAS 构建的限制. +自从 v0.34.0,Electron 就允许提交应用包到 Mac App Store +(MAS)。这个向导提供的信息有: 如何提交应用和 MAS 构建的限制。 -__注意:__ 从 v0.36.0,当应用成为沙箱之后,会有一个 bug 阻止 GPU 进程开启 , 所以在这个 bug 修复之前,建议使用 v0.35.x .更多查看 [issue #3871][issue-3871] . - -__注意:__ 提交应用到 Mac App Store 需要参加 [Apple Developer -Program][developer-program] , 这需要花钱. +__注意:__ 提交应用到 Mac App Store 需要参加 [Apple Developer +Program][developer-program],这需要额外花费。 ## 如何提交 -下面步骤介绍了一个简单的提交应用到商店方法.然而,这些步骤不能保证你的应用被 Apple 接受;你仍然需要阅读 Apple 的 [Submitting Your App][submitting-your-app] 关于如何满足 Mac App Store 要求的向导. +下面步骤介绍了一个简单的提交应用到商店方法。然而,这些步骤不能保证你的应用被 Apple 接受;你仍然需要阅读 Apple 的 [Submitting Your App][submitting-your-app] 关于如何满足 Mac App Store 要求的向导。 ### 获得证书 -为了提交应用到商店,首先需要从 Apple 获得一个证书.可以遵循 [existing guides][nwjs-guide]. +为了提交应用到商店,首先需要从 Apple 获得一个证书。可以遵循 [现有向导][nwjs-guide]。 -### App 签名 +### 软件签名 -获得证书之后,你可以使用 [Application Distribution](application-distribution.md) 打包你的应用, 然后前往提交你的应用.这个步骤基本上和其他程序一样,但是这 key 一个个的标识 Electron 的每个依赖. +获得证书之后,你可以使用 [应用部署](application-distribution.md) 打包你的应用,之后进行提交。 -首先,你需要准备2个授权文件 . +首先,你需要在软件包内的 `Info.plist` 中增添一项 `ElectronTeamID`: + +```xml + + + ... + ElectronTeamID + TEAM_ID + + +``` + +之后,你需要准备2个授权文件。 `child.plist`: @@ -46,67 +56,76 @@ Program][developer-program] , 这需要花钱. com.apple.security.app-sandbox + com.apple.security.application-groups + TEAM_ID.your.bundle.id ``` -然后使用下面的脚本标识你的应用 : +请注意上述 `TEAM_ID` 对应开发者账户的 Team ID,`your.bundle.id` 对应软件打包时使用的 Bundle ID。 + +然后使用下面的脚本签名你的应用: ```bash #!/bin/bash -# Name of your app. +# 应用名称 APP="YourApp" -# The path of you app to sign. -APP_PATH="/path/to/YouApp.app" -# The path to the location you want to put the signed package. +# 应用路径 +APP_PATH="/path/to/YourApp.app" +# 生成安装包路径 RESULT_PATH="~/Desktop/$APP.pkg" -# The name of certificates you requested. +# 开发者应用签名证书 APP_KEY="3rd Party Mac Developer Application: Company Name (APPIDENTITY)" INSTALLER_KEY="3rd Party Mac Developer Installer: Company Name (APPIDENTITY)" +# 授权文件路径 +CHILD_PLIST="/path/to/child.plist" +PARENT_PLIST="/path/to/parent.plist" FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/" -codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/" -if [ -d "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A" ]; then - # Signing a non-MAS build. - codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Mantle.framework/Versions/A" - codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/ReactiveCocoa.framework/Versions/A" - codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A" -fi -codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libnode.dylib" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/Contents/MacOS/$APP Helper EH" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/Contents/MacOS/$APP Helper NP" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/" +codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacOS/$APP" +codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH" productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH" ``` -如果你是 OS X 下的应用沙箱使用新手,应当仔细阅读 Apple 的 [Enabling App Sandbox][enable-app-sandbox] 来有一点基础,然后向授权文件添加你的应用需要的许可 keys . -### 上传你的应用并检查提交 +如果你是 OS X 下的应用沙箱使用新手,应当仔细阅读 Apple 的 [Enabling App Sandbox][enable-app-sandbox] 了解一些基础,然后在授权文件 (entitlements files) 内添加你的应用需要的许可。 -在签名应用之后,可以使用应用 Loader 来上传到 iTunes 链接处理 , 确保在上传之前你已经 [created a record][create-record]. 然后你能 [submit your app for review][submit-for-review]. +### 上传你的应用并检查提交 -## MAS构建限制 +在签名应用之后,你可以使用 Application Loader 上传软件到 iTunes Connect 进行处理。请确保在上传之前你已经 [创建应用记录][create-record],再 [提交进行审核][submit-for-review]。 -为了让你的应用沙箱满足所有条件,在 MAS 构建的时候,下面的模块被禁用了 : +## MAS 构建限制 + +为了让你的应用满足沙箱的所有条件,在 MAS 构建的时候,下面的模块已被禁用: * `crashReporter` * `autoUpdater` 并且下面的行为也改变了: -* 一些机子的视频采集功能无效. -* 某些特征不可访问. -* Apps 不可识别 DNS 改变. +* 一些视频采集功能无效。 +* 某些辅助功能无法访问。 +* 应用无法检测 DNS 变化。 -也由于应用沙箱的使用方法,应用可以访问的资源被严格限制了 ; 阅读更多信息 [App Sandboxing][app-sandboxing] . +也由于应用沙箱的使用方法,应用可以访问的资源被严格限制了;阅读更多信息 [App Sandboxing][app-sandboxing]。 ## Electron 使用的加密算法 -取决于你所在地方的国家和地区 , Mac App Store 或许需要记录你应用的加密算法 , 甚至要求你提交一个 U.S 加密注册(ERN) 许可的复印件. +取决于你所在地方的国家和地区,Mac App Store 或许需要记录你应用的加密算法,甚至要求你提交一个 U.S. 加密注册 (ERN) 许可的复印件。 -Electron 使用下列加密算法: +Electron 使用下列加密算法: * AES - [NIST SP 800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf), [NIST SP 800-38D](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf), [RFC 3394](http://www.ietf.org/rfc/rfc3394.txt) * HMAC - [FIPS 198-1](http://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf) @@ -132,9 +151,9 @@ Electron 使用下列加密算法: * RC5 - http://people.csail.mit.edu/rivest/Rivest-rc5rev.pdf * RIPEMD - [ISO/IEC 10118-3](http://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2FIEC%2010118-3:2004) -如何获取 ERN 许可, 可看这篇文章: [How to legally +如何获取 ERN 许可, 可看这篇文章:[How to legally submit an app to Apple’s App Store when it uses encryption (or how to obtain an -ERN)][ern-tutorial]. +ERN)][ern-tutorial]。 [developer-program]: https://developer.apple.com/support/compare-memberships/ [submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html @@ -144,4 +163,4 @@ ERN)][ern-tutorial]. [submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html [app-sandboxing]: https://developer.apple.com/app-sandboxing/ [issue-3871]: https://github.com/electron/electron/issues/3871 -[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ \ No newline at end of file +[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ diff --git a/docs-translations/zh-CN/tutorial/quick-start.md b/docs-translations/zh-CN/tutorial/quick-start.md index d267d3146051..6ea9d6d5525c 100644 --- a/docs-translations/zh-CN/tutorial/quick-start.md +++ b/docs-translations/zh-CN/tutorial/quick-start.md @@ -1,7 +1,6 @@ # 快速入门 -## 简介 -Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作是专注于桌面应用而不是 web 服务器的,io.js 的一个变体。 +Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作一个专注于桌面应用的 Node.js 的变体,而不是 Web 服务器。 这不意味着 Electron 是绑定了 GUI 库的 JavaScript。相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。 @@ -14,13 +13,13 @@ Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造 在一般浏览器中,网页通常会在沙盒环境下运行,并且不允许访问原生资源。然而,Electron 用户拥有在网页中调用 io.js 的 APIs 的能力,可以与底层操作系统直接交互。 ## 主进程与渲染进程的区别 -主进程使用 BrowserWindow 实例创建网页。每个 BrowserWindow 实例都在自己的渲染进程里运行着一个网页。当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。 +主进程使用 `BrowserWindow` 实例创建页面。每个 `BrowserWindow` 实例都在自己的渲染进程里运行页面。当一个 `BrowserWindow` 实例被销毁后,相应的渲染进程也会被终止。 -主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的网页。 +主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的页面。 -由于在网页里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在网页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。 +由于在页面里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。 -在 Electron,我们提供用于在主进程与渲染进程之间通讯的 [ipc][1] 模块。并且也有一个远程进程调用风格的通讯模块 [remote][2]。 +在 Electron,我们提供几种方法用于主进程和渲染进程之间的通讯。像 [ipcRenderer][1] 和 [ipcMain][2] 模块用于发送消息, [remote][3] 模块用于 RPC 方式通讯。这些内容都可以在一个 FAQ 中查看 [how to share data between web pages][4]。 # 打造你第一个 Electron 应用 大体上,一个 Electron 应用的目录结构如下: @@ -42,53 +41,72 @@ your-app/ `main.js` 应该用于创建窗口和处理系统事件,一个典型的例子如下: ```javascript -var app = require('app'); // 控制应用生命周期的模块。 -var BrowserWindow = require('browser-window'); // 创建原生浏览器窗口的模块 +const electron = require('electron'); +// 控制应用生命周期的模块。 +const {app} = electron; +// 创建原生浏览器窗口的模块。 +const {BrowserWindow} = electron; -// 保持一个对于 window 对象的全局引用,不然,当 JavaScript 被 GC, -// window 会被自动地关闭 -var mainWindow = null; +// 保持一个对于 window 对象的全局引用,如果你不这样做, +// 当 JavaScript 对象被垃圾回收, window 会被自动地关闭 +let mainWindow; -// 当所有窗口被关闭了,退出。 -app.on('window-all-closed', function() { - // 在 OS X 上,通常用户在明确地按下 Cmd + Q 之前 - // 应用会保持活动状态 - if (process.platform != 'darwin') { +function createWindow() { + // 创建浏览器窗口。 + mainWindow = new BrowserWindow({width: 800, height: 600}); + + // 加载应用的 index.html。 + mainWindow.loadURL(`file://${__dirname}/index.html`); + + // 启用开发工具。 + mainWindow.webContents.openDevTools(); + + // 当 window 被关闭,这个事件会被触发。 + mainWindow.on('closed', () => { + // 取消引用 window 对象,如果你的应用支持多窗口的话, + // 通常会把多个 window 对象存放在一个数组里面, + // 与此同时,你应该删除相应的元素。 + mainWindow = null; + }); +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.on('ready', createWindow); + +// Quit when all windows are closed. +app.on('window-all-closed', () => { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { app.quit(); } }); -// 当 Electron 完成了初始化并且准备创建浏览器窗口的时候 -// 这个方法就被调用 -app.on('ready', function() { - // 创建浏览器窗口。 - mainWindow = new BrowserWindow({width: 800, height: 600}); - - // 加载应用的 index.html - mainWindow.loadURL('file://' + __dirname + '/index.html'); - - // 打开开发工具 - mainWindow.openDevTools(); - - // 当 window 被关闭,这个事件会被发出 - mainWindow.on('closed', function() { - // 取消引用 window 对象,如果你的应用支持多窗口的话, - // 通常会把多个 window 对象存放在一个数组里面, - // 但这次不是。 - mainWindow = null; - }); +app.on('activate', () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (mainWindow === null) { + createWindow(); + } }); + +// In this file you can include the rest of your app's specific main process +// code. You can also put them in separate files and require them here. ``` 最后,你想展示的 `index.html` : ```html + Hello World!

Hello World!

- We are using io.js + We are using node , + Chrome , and Electron . @@ -97,8 +115,9 @@ app.on('ready', function() { # 运行你的应用 一旦你创建了最初的 `main.js`, `index.html` 和 `package.json` 这几个文件,你可能会想尝试在本地运行并测试,看看是不是和期望的那样正常运行。 -## electron-prebuild -如果你已经用 `npm` 全局安装了 `electron-prebuilt`,你只需要按照如下方式直接运行你的应用: +## electron-prebuilt +[electron-prebuilt][5] 是一个 `npm` 模块,包含所使用的 Electron 预编译版本。 +如果你已经用 `npm` 全局安装了它,你只需要按照如下方式直接运行你的应用: ```bash electron . ``` @@ -121,13 +140,32 @@ $ ./electron/electron your-app/ ```bash $ ./Electron.app/Contents/MacOS/Electron your-app/ ``` -`Electron.app` 里面是 Electron 发布包,你可以在[这里][3]下载到。 +`Electron.app` 里面是 Electron 发布包,你可以在 [这里][6] 下载到。 # 以发行版本运行 -在你完成了你的应用后,你可以按照[应用部署][4]指导发布一个版本,并且以已经打包好的形式运行应用。 +在你完成了你的应用后,你可以按照 [应用部署][7] 指导发布一个版本,并且以已经打包好的形式运行应用。 +# 参照下面例子 +复制并且运行这个库 [electron/electron-quick-start][8]。 - [1]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/ipc-main-process.md - [2]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/remote.md - [3]: https://github.com/electron/electron/releases - [4]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/tutorial/application-distribution.md +*注意:*运行时需要你的系统已经安装了 [Git][9] 和 [Node.js][10](包含 [npm][11])。 + +```bash +# Clone the repository +$ git clone https://github.com/electron/electron-quick-start +# Go into the repository +$ cd electron-quick-start +# Install dependencies and run the app +$ npm install && npm start +``` + [1]: https://github.com/electron/electron/blob/v1.1.3/docs/api/ipc-renderer.md + [2]: https://github.com/electron/electron/blob/v1.1.3/docs/api/ipc-main.md + [3]: https://github.com/electron/electron/blob/v1.1.3/docs/api/remote.md + [4]: https://github.com/electron/electron/blob/v1.1.3/docs/faq/electron-faq.md#how-to-share-data-between-web-pages + [5]: https://github.com/electron-userland/electron-prebuilt + [6]: https://github.com/electron/electron/releases + [7]: https://github.com/electron/electron/blob/v1.1.3/docs/tutorial/application-distribution.md + [8]: https://github.com/electron/electron-quick-start + [9]: https://git-scm.com/ + [10]: https://nodejs.org/en/download/ + [11]: https://www.npmjs.com/ diff --git a/docs/api/app.md b/docs/api/app.md index becd6039c757..b7ce6ce02fa9 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -346,6 +346,7 @@ You can request the following paths by the name: * `music` Directory for a user's music. * `pictures` Directory for a user's pictures. * `videos` Directory for a user's videos. +* `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin. ### `app.setPath(name, path)` @@ -561,6 +562,12 @@ Imports the certificate in pkcs12 format into the platform certificate store. `callback` is called with the `result` of import operation, a value of `0` indicates success while any other value indicates failure according to chromium [net_error_list](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h). +### `app.disableHardwareAcceleration()` + +Disables hardware acceleration for current app. + +This method can only be called before app is ready. + ### `app.commandLine.appendSwitch(switch[, value])` Append a switch (with optional `value`) to Chromium's command line. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index ecdbece771b7..8b850b5c4388 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -175,9 +175,13 @@ The `webPreferences` option is an object that can have following properties: * `scrollBounce` Boolean - Enables scroll bounce (rubber banding) effect on OS X. Default is `false`. * `blinkFeatures` String - A list of feature strings separated by `,`, like - `CSSVariables,KeyboardEventKey`. The full list of supported feature strings - can be found in the [setFeatureEnabledFromString][blink-feature-string] - function. + `CSSVariables,KeyboardEventKey` to enable. The full list of supported feature + strings can be found in the [RuntimeEnabledFeatures.in][blink-feature-string] + file. +* `disableBlinkFeatures` String - A list of feature strings separated by `,`, + like `CSSVariables,KeyboardEventKey` to disable. The full list of supported + feature strings can be found in the + [RuntimeEnabledFeatures.in][blink-feature-string] file. * `defaultFontFamily` Object - Sets the default font for the font-family. * `standard` String - Defaults to `Times New Roman`. * `serif` String - Defaults to `Times New Roman`. @@ -380,7 +384,11 @@ Find a window according to its ID. Adds DevTools extension located at `path`, and returns extension's name. The extension will be remembered so you only need to call this API once, this -API is not for programming use. +API is not for programming use. If you try to add an extension that has already +been loaded, this method will not return and instead log a warning to the +console. + +Method will also not return if the extension's manifest is missing or incomplete. ### `BrowserWindow.removeDevToolsExtension(name)` @@ -877,7 +885,7 @@ The `flags` is an array that can include following `String`s: ### `win.showDefinitionForSelection()` _OS X_ -Shows pop-up dictionary that searches the selected word on the page. +Same as `webContents.showDefinitionForSelection()`. ### `win.setIcon(icon)` _Windows_ _Linux_ @@ -924,10 +932,14 @@ Returns whether the window is visible on all workspaces. **Note:** This API always returns false on Windows. -### `win.setIgnoreMouseEvents(ignore)` _OS X_ +### `win.setIgnoreMouseEvents(ignore)` * `ignore` Boolean -Ignore all moused events that happened in the window. +Makes the window ignore all mouse events. -[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=576 +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. + +[blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in diff --git a/docs/api/download-item.md b/docs/api/download-item.md index 14d1931e4468..778f68b7bc36 100644 --- a/docs/api/download-item.md +++ b/docs/api/download-item.md @@ -2,49 +2,70 @@ > Control file downloads from remote sources. -`DownloadItem` is an EventEmitter that represents a download item in Electron. -It is used in `will-download` event of `Session` module, and allows users to +`DownloadItem` is an `EventEmitter` that represents a download item in Electron. +It is used in `will-download` event of `Session` class, and allows users to control the download item. ```javascript // In the main process. win.webContents.session.on('will-download', (event, item, webContents) => { // Set the save path, making Electron not to prompt a save dialog. - item.setSavePath('/tmp/save.pdf'); - console.log(item.getMimeType()); - console.log(item.getFilename()); - console.log(item.getTotalBytes()); - item.on('updated', () => { - console.log('Received bytes: ' + item.getReceivedBytes()); - }); - item.on('done', (e, state) => { - if (state === 'completed') { - console.log('Download successfully'); - } else { - console.log('Download is cancelled or interrupted that can\'t be resumed'); + item.setSavePath('/tmp/save.pdf') + + item.on('updated', (event, state) => { + if (state === 'interrupted') { + console.log('Download is interrupted but can be resumed') + } else if (state === 'progressing') { + if (item.isPaused()) { + console.log('Download is paused') + } else { + console.log(`Received bytes: ${item.getReceivedBytes()}`) + } } - }); -}); + }) + item.once('done', (event, state) => { + if (state === 'completed') { + console.log('Download successfully') + } else { + console.log(`Download failed: ${state}`) + } + }) +}) ``` ## Events ### Event: 'updated' -Emits when the `downloadItem` gets updated. - -### Event: 'done' +Returns: * `event` Event * `state` String - * `completed` - The download completed successfully. - * `cancelled` - The download has been cancelled. - * `interrupted` - An error broke the connection with the file server. -Emits when the download is in a terminal state. This includes a completed +Emitted when the download has been updated and is not done. + +The `state` can be one of following: + +* `progressing` - The download is in-progress. +* `interrupted` - The download has interrupted and can be resumed. + +### Event: 'done' + +Returns: + +* `event` Event +* `state` String + +Emitted when the download is in a terminal state. This includes a completed download, a cancelled download(via `downloadItem.cancel()`), and interrupted download that can't be resumed. +The `state` can be one of following: + +* `completed` - The download completed successfully. +* `cancelled` - The download has been cancelled. +* `interrupted` - The download has interrupted and can not resume. + ## Methods The `downloadItem` object has the following methods: @@ -61,10 +82,18 @@ routine to determine the save path(Usually prompts a save dialog). Pauses the download. +### `downloadItem.isPaused()` + +Returns whether the download is paused. + ### `downloadItem.resume()` Resumes the download that has been paused. +### `downloadItem.canResume()` + +Resumes whether the download can resume. + ### `downloadItem.cancel()` Cancels the download operation. @@ -102,3 +131,14 @@ Returns a `Integer` represents the received bytes of the download item. Returns a `String` represents the Content-Disposition field from the response header. + +### `downloadItem.getState()` + +Returns current state as `String`. + +Possible values are: + +* `progressing` - The download is in-progress. +* `completed` - The download completed successfully. +* `cancelled` - The download has been cancelled. +* `interrupted` - The download has interrupted. diff --git a/docs/api/frameless-window.md b/docs/api/frameless-window.md index f3017037210d..66c42588b2b5 100644 --- a/docs/api/frameless-window.md +++ b/docs/api/frameless-window.md @@ -14,8 +14,8 @@ To create a frameless window, you need to set `frame` to `false` in ```javascript -const {BrowserWindow} = require('electron'); -let win = new BrowserWindow({width: 800, height: 600, frame: false}); +const {BrowserWindow} = require('electron') +let win = new BrowserWindow({width: 800, height: 600, frame: false}) ``` ### Alternatives on OS X @@ -28,7 +28,7 @@ the window controls ("traffic lights") for standard window actions. You can do so by specifying the new `titleBarStyle` option: ```javascript -let win = new BrowserWindow({titleBarStyle: 'hidden'}); +let win = new BrowserWindow({titleBarStyle: 'hidden'}) ``` ## Transparent window @@ -37,7 +37,7 @@ By setting the `transparent` option to `true`, you can also make the frameless window transparent: ```javascript -let win = new BrowserWindow({transparent: true, frame: false}); +let win = new BrowserWindow({transparent: true, frame: false}) ``` ### Limitations @@ -59,6 +59,16 @@ let win = new BrowserWindow({transparent: true, frame: false}); Linux. * On Mac the native window shadow will not be shown on a transparent window. +## Click-through window + +To create a click-through window, i.e. making the window ignore all mouse +events, you can call the [win.setIgnoreMouseEvents(ignore)][ignore-mouse-events] +API: + +```javascript +win.setIgnoreMouseEvents(true) +``` + ## Draggable region By default, the frameless window is non-draggable. Apps need to specify @@ -108,3 +118,5 @@ On some platforms, the draggable area will be treated as a non-client frame, so when you right click on it a system menu will pop up. To make the context menu behave correctly on all platforms you should never use a custom context menu on draggable areas. + +[ignore-mouse-events]: browser-window.md#winsetignoremouseeventsignore diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index b67032271e61..c12b14cc0bc5 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -46,7 +46,9 @@ The `role` property can have following values: * `cut` * `copy` * `paste` +* `pasteandmatchstyle` * `selectall` +* `delete` * `minimize` - Minimize current window * `close` - Close current window diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 5244b71dfb16..d61f57ee4b9a 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -103,6 +103,12 @@ Creates an empty `nativeImage` instance. Creates a new `nativeImage` instance from a file located at `path`. +```javascript +const nativeImage = require('electron').nativeImage; + +let image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); +``` + ### `nativeImage.createFromBuffer(buffer[, scaleFactor])` * `buffer` [Buffer][buffer] @@ -121,12 +127,6 @@ Creates a new `nativeImage` instance from `dataURL`. The following methods are available on instances of `nativeImage`: -```javascript -const nativeImage = require('electron').nativeImage; - -let image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); -``` - ### `image.toPng()` Returns a [Buffer][buffer] that contains the image's `PNG` encoded data. diff --git a/docs/api/remote.md b/docs/api/remote.md index 52f7208b8b0b..998aa6c47215 100644 --- a/docs/api/remote.md +++ b/docs/api/remote.md @@ -80,7 +80,7 @@ exports.withLocalCallback = () => { ```javascript // renderer process -const mapNumbers = require('remote').require('./mapNumbers'); +const mapNumbers = require('electron').remote.require('./mapNumbers'); const withRendererCb = mapNumbers.withRendererCallback(x => x + 1); diff --git a/docs/api/screen.md b/docs/api/screen.md index 7dd6203316d2..440c99ac3f03 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -9,44 +9,42 @@ emitted (by invoking or requiring it). **Note:** In the renderer / DevTools, `window.screen` is a reserved DOM property, so writing `let {screen} = require('electron')` will not work. -In our examples below, we use `electronScreen` as the variable name instead. + An example of creating a window that fills the whole screen: ```javascript -const {app, BrowserWindow, screen: electronScreen} = require('electron'); +const electron = require('electron') +const {app, BrowserWindow} = electron -let win; +let win app.on('ready', () => { - const {width, height} = electronScreen.getPrimaryDisplay().workAreaSize; - win = new BrowserWindow({width, height}); + const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize + win = new BrowserWindow({width, height}) }); ``` Another example of creating a window in the external display: ```javascript -const {app, BrowserWindow, screen: electronScreen} = require('electron'); +const electron = require('electron') +const {app, BrowserWindow} = require('electron') -let win; +let win app.on('ready', () => { - let displays = electronScreen.getAllDisplays(); - let externalDisplay = null; - for (let i in displays) { - if (displays[i].bounds.x !== 0 || displays[i].bounds.y !== 0) { - externalDisplay = displays[i]; - break; - } - } + let displays = electron.screen.getAllDisplays() + let externalDisplay = displays.find((display) => { + return display.bounds.x !== 0 || display.bounds.y !== 0 + }) if (externalDisplay) { win = new BrowserWindow({ x: externalDisplay.bounds.x + 50, y: externalDisplay.bounds.y + 50 - }); + }) } -}); +}) ``` ## The `Display` object diff --git a/docs/api/session.md b/docs/api/session.md index 43107e98e920..030b2c7542ea 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -206,7 +206,7 @@ Sets the proxy settings. When `pacScript` and `proxyRules` are provided together, the `proxyRules` option is ignored and `pacScript` configuration is applied. -The `proxyRules` has to follow the rules bellow: +The `proxyRules` has to follow the rules below: ``` proxyRules = schemeProxies[";"] @@ -547,3 +547,22 @@ 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/tray.md b/docs/api/tray.md index 4c4891ef8960..421d4fcf63dc 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -29,12 +29,12 @@ __Platform limitations:__ * When app indicator is used on Linux, the `click` event is ignored. * On Linux in order for changes made to individual `MenuItem`s to take effect, you have to call `setContextMenu` again. For example: -* On Windows it is recommended to use `ICO` icons to get best visual effects. ```javascript contextMenu.items[2].checked = false; appIcon.setContextMenu(contextMenu); ``` +* On Windows it is recommended to use `ICO` icons to get best visual effects. If you want to keep exact same behaviors on all platforms, you should not rely on the `click` event and always attach a context menu to the tray icon. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index c3f1f82366c7..60c736e0041c 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -288,6 +288,15 @@ a meta tag: ``` +### Event: 'update-target-url' + +Returns: + +* `event` Event +* `url` String + +Emitted when mouse moves over a link or the keyboard moves the focus to a link. + ### Event: 'cursor-changed' Returns: @@ -948,6 +957,10 @@ win.webContents.on('did-finish-load', () => { }); ``` +### `webContents.showDefinitionForSelection()` _OS X_ + +Shows pop-up dictionary that searches the selected word on the page. + ## Instance Properties `WebContents` objects also have the following properties: diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index f99bb4fdb5a1..c210bd8b9cb0 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -205,7 +205,17 @@ If "on", the guest page will be allowed to open new windows. A list of strings which specifies the blink features to be enabled separated by `,`. The full list of supported feature strings can be found in the -[setFeatureEnabledFromString][blink-feature-string] function. +[RuntimeEnabledFeatures.in][blink-feature-string] file. + +### `disableblinkfeatures` + +```html + +``` + +A list of strings which specifies the blink features to be disabled separated by `,`. +The full list of supported feature strings can be found in the +[RuntimeEnabledFeatures.in][blink-feature-string] file. ## Methods @@ -480,6 +490,10 @@ Sends an input `event` to the page. See [webContents.sendInputEvent](web-contents.md##webcontentssendinputeventevent) for detailed description of `event` object. +### `.showDefinitionForSelection()` _OS X_ + +Shows pop-up dictionary that searches the selected word on the page. + ### `.getWebContents()` Returns the [WebContents](web-contents.md) associated with this `webview`. @@ -782,6 +796,14 @@ Emitted when a page's theme color changes. This is usually due to encountering a ``` +### Event: 'update-target-url' + +Returns: + +* `url` String + +Emitted when mouse moves over a link or the keyboard moves the focus to a link. + ### Event: 'devtools-opened' Emitted when DevTools is opened. @@ -794,4 +816,4 @@ Emitted when DevTools is closed. Emitted when DevTools is focused / opened. -[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=527 +[blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in diff --git a/docs/development/build-system-overview.md b/docs/development/build-system-overview.md index bc27c1932fa6..4336092b7213 100644 --- a/docs/development/build-system-overview.md +++ b/docs/development/build-system-overview.md @@ -8,7 +8,7 @@ be found in the `.gyp` and `.gypi` files. Following `gyp` files contain the main rules for building Electron: -* `atom.gyp` defines how Electron itself is built. +* `electron.gyp` defines how Electron itself is built. * `common.gypi` adjusts the build configurations of Node to make it build together with Chromium. * `vendor/brightray/brightray.gyp` defines how `brightray` is built and diff --git a/docs/faq/electron-faq.md b/docs/faq/electron-faq.md index cefc5a54a2c7..1e96d47c2107 100644 --- a/docs/faq/electron-faq.md +++ b/docs/faq/electron-faq.md @@ -30,7 +30,7 @@ use HTML5 APIs which are already available in browsers. Good candidates are Or you can use the IPC system, which is specific to Electron, to store objects in the main process as a global variable, and then to access them from the -renderers through the `remote` module: +renderers through the `remote` property of `electron` module: ```javascript // In the main process. @@ -41,12 +41,12 @@ global.sharedObject = { ```javascript // In page 1. -require('remote').getGlobal('sharedObject').someProperty = 'new value'; +require('electron').remote.getGlobal('sharedObject').someProperty = 'new value'; ``` ```javascript // In page 2. -console.log(require('remote').getGlobal('sharedObject').someProperty); +console.log(require('electron').remote.getGlobal('sharedObject').someProperty); ``` ## My app's window/tray disappeared after a few minutes. diff --git a/docs/tutorial/mac-app-store-submission-guide.md b/docs/tutorial/mac-app-store-submission-guide.md index d240cc13371b..b2c08e8849f9 100644 --- a/docs/tutorial/mac-app-store-submission-guide.md +++ b/docs/tutorial/mac-app-store-submission-guide.md @@ -120,6 +120,24 @@ If you are new to app sandboxing under OS X, you should also read through Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then add keys for the permissions needed by your app to the entitlements files. +Apart from manually signing your app, you can also choose to use the +[electron-osx-sign][electron-osx-sign] module to do the job. + +#### Sign Native Modules + +Native modules used in your app also need to be signed. If using +electron-osx-sign, be sure to include the path to the built binaries in the +argument list: + +```bash +electron-osx-sign YourApp.app YourApp.app/Contents/Resources/app/node_modules/nativemodule/build/release/nativemodule +``` + +Also note that native modules may have intermediate files produced which should +not be included (as they would also need to be signed). If you use +[electron-packager][electron-packager], add `--ignore=.+\.o$` to build step to +ignore these files. + ### Upload Your App After signing your app, you can use Application Loader to upload it to iTunes @@ -189,6 +207,8 @@ ERN)][ern-tutorial]. [nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps [enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html [create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html +[electron-osx-sign]: https://github.com/electron-userland/electron-osx-sign +[electron-packager]: https://github.com/electron-userland/electron-packager [submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html [app-sandboxing]: https://developer.apple.com/app-sandboxing/ [ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ diff --git a/docs/tutorial/using-pepper-flash-plugin.md b/docs/tutorial/using-pepper-flash-plugin.md index 76c89d6fdd43..9ff6ebf00eb2 100644 --- a/docs/tutorial/using-pepper-flash-plugin.md +++ b/docs/tutorial/using-pepper-flash-plugin.md @@ -41,6 +41,8 @@ app.on('ready', () => { }); ``` +The path to the system wide Pepper Flash plugin can also be obtained by calling `app.getPath('pepperFlashSystemPlugin')`. + ## Enable Flash Plugin in a `` Tag Add `plugins` attribute to `` tag. diff --git a/docs/tutorial/windows-store-guide.md b/docs/tutorial/windows-store-guide.md index ccd3c1cff3a1..6378ccb25a28 100644 --- a/docs/tutorial/windows-store-guide.md +++ b/docs/tutorial/windows-store-guide.md @@ -5,7 +5,7 @@ Windows Platform. The new `.appx` format does not only enable a number of new powerful APIs like Cortana or Push Notifications, but through the Windows Store, also simplifies installation and updating. -Microsoft [developed a tool that compiles Electron apps as `.appx` packages](http://github.com/catalystcode/electron-windows-store), +Microsoft [developed a tool that compiles Electron apps as `.appx` packages][electron-windows-store], enabling developers to use some of the goodies found in the new application model. This guide explains how to use it - and what the capabilities and limitations of an Electron AppX package are. @@ -22,19 +22,17 @@ installation and uninstallation. In addition, the exe is launched inside the appx model - meaning that it can use many of the APIs available to the Universal Windows Platform. To gain even more -capabilities, an Electron app can pair up with an invisible UWP app launched -together with the `exe` - sort of launched as a sidekick to run tasks in the -background, receive push notifications, or to communicate with other UWP +capabilities, an Electron app can pair up with an invisible UWP background task +launched together with the `exe` - sort of launched as a sidekick to run tasks +in the background, receive push notifications, or to communicate with other UWP applications. To compile any existing Electron app, ensure that you have the following requirements: -* Windows 10 Anniversary Update - Enterprise Edition (This is build 14316 and up - - as of May 2016, it's part of the Windows Insiders Preview) -* A machine with 64 bit (x64) processor, Hardware-Assisted Virtualization, and - Second Level Address Translation (SLAT) -* The Windows 10 SDK, [downloadable here](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) +* Windows 10 Anniversary Update (until the update is released to the general public, +developers can use the Windows Insider Preview) +* The Windows 10 SDK, [downloadable here][windows-sdk] * At least Node 4 (to check, run `node -v`) Then, go and install the `electron-windows-store` CLI: @@ -43,32 +41,11 @@ Then, go and install the `electron-windows-store` CLI: npm install -g electron-windows-store ``` -## Setup and Preparation +## Step 1: Package Your Electron Application -Before running the CLI for the first time, you will have to setup the "Windows -Desktop App Converter". This will take a few minutes, but don't worry - you only -have to do this once. Download and Desktop App Converter from -[here](https://www.microsoft.com/en-us/download/details.aspx?id=51691). You will -receive two files: `DesktopAppConverter.zip` and `BaseImage-14316.wim`. - -1. Unzip `DesktopAppConverter.zip`. From an elevated PowerShell (opened with - "run as Administrator", ensure that your systems execution policy allows us to - run everything we intend to run by calling `Set-ExecutionPolicy bypass`. -2. Then, run the installation of the Desktop App Converter, passing in the - location of the Windows base Image (downloaded as `BaseImage-14316.wim`), by - calling `.\DesktopAppConverter.ps1 -Setup -BaseImage .\BaseImage-14316.wim`. -3. If running the above command prompts you for a reboot, please restart your - machine and run the above command again after a successful restart. - -Once installation succeeded, you can move on to compiling your Electron app. - -## Package Your Electron Application - -Package the application using -[electron-packager](https://github.com/electron-userland/electron-packager) -(or a similar tool). Make sure to remove `node_modules` that you don't need in -your final application, since any module you don't actually need will just -increase your application's size. +Package the application using [electron-packager][electron-packager] (or a similar tool). +Make sure to remove `node_modules` that you don't need in your final application, since +any module you don't actually need will just increase your application's size. The output should look roughly like this: @@ -101,7 +78,7 @@ The output should look roughly like this: └── xinput1_3.dll ``` -## Running the Command Line Tool +## Step 2: Running electron-windows-store From an elevated PowerShell (run it "as Administrator"), run `electron-windows-store` with the required parameters, passing both the input @@ -130,13 +107,54 @@ Finally, the tool can be used to create a trusted certificate on your computer to sign the new AppX pacakge. With the signed AppX package, the CLI can also automatically install the package on your machine. -## Using the AppX Package +## Step 3: Using the AppX Package Since the Windows Anniversary Update (codenamed Windows Redstone) has not been released to consumers yet, you won't be able to release your app to the Windows Store until later this year - but you can already use the `Add-AppxPackage` -[PowerShell Cmdlet to install it on machines](https://technet.microsoft.com/en-us/library/hh856048.aspx) +[PowerShell Cmdlet to install it on machines][add-appxpackage] in developer or enterprise environments. Another important limitation is that the compiled AppX package still contains a win32 executable - and will therefore not run on Xbox, HoloLens, or Phones. + +## Optional: Add UWP Features using a BackgroundTask +You can pair your Electron app up with an invisible UWP background task that +gets to make full use of Windows 10 features - like push notifications, +Cortana integration, or live tiles. + +To check out how an Electron app that uses a background task to send toast +notifications and live tiles, [check out the Microsoft-provided sample][background-task]. + +## Optional: Convert using Container Virtualiziation + +To generate the AppX package, the `electron-windows-store` CLI uses a template +that should work for most Electron apps. However, if you are using a custom +installer, or should you experience any trouble with the generated package, you +can attempt to create a package using compilation with a Windows Container - in +that mode, the CLI will install and run your application in blank Windows Container +to determine what modifications your application is exactly doing to the operating +system. + +Before running the CLI for the, you will have to setup the "Windows Desktop App +Converter". This will take a few minutes, but don't worry - you only have to do +this once. Download and Desktop App Converter from [here][app-converter]. +You will receive two files: `DesktopAppConverter.zip` and `BaseImage-14316.wim`. + +1. Unzip `DesktopAppConverter.zip`. From an elevated PowerShell (opened with + "run as Administrator", ensure that your systems execution policy allows us to + run everything we intend to run by calling `Set-ExecutionPolicy bypass`. +2. Then, run the installation of the Desktop App Converter, passing in the + location of the Windows base Image (downloaded as `BaseImage-14316.wim`), by + calling `.\DesktopAppConverter.ps1 -Setup -BaseImage .\BaseImage-14316.wim`. +3. If running the above command prompts you for a reboot, please restart your + machine and run the above command again after a successful restart. + +Once installation succeeded, you can move on to compiling your Electron app. + +[windows-sdk]: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk +[app-converter]: https://www.microsoft.com/en-us/download/details.aspx?id=51691 +[add-appxpackage]: https://technet.microsoft.com/en-us/library/hh856048.aspx +[electron-packager]: https://github.com/electron-userland/electron-packager +[electron-windows-store]: https://github.com/catalystcode/electron-windows-store +[background-task]: https://github.com/felixrieseberg/electron-uwp-background \ No newline at end of file diff --git a/electron.gyp b/electron.gyp index 055cb0c209ac..994cdcea899b 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.2.1', + 'version%': '1.2.2', }, 'includes': [ 'filenames.gypi', diff --git a/filenames.gypi b/filenames.gypi index 5f1fdc3b141f..2b87e36fa4d2 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -63,6 +63,8 @@ 'lib/renderer/api/remote.js', 'lib/renderer/api/screen.js', 'lib/renderer/api/web-frame.js', + 'lib/renderer/extensions/i18n.js', + 'lib/renderer/extensions/storage.js', ], 'js2c_sources': [ 'lib/common/asar.js', diff --git a/lib/browser/api/browser-window.js b/lib/browser/api/browser-window.js index ec61229368d1..6ac31e737b8c 100644 --- a/lib/browser/api/browser-window.js +++ b/lib/browser/api/browser-window.js @@ -151,6 +151,9 @@ Object.assign(BrowserWindow.prototype, { }, inspectServiceWorker () { return this.webContents.inspectServiceWorker() + }, + showDefinitionForSelection () { + return this.webContents.showDefinitionForSelection() } }) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index f50e0ed84e52..4d2737d0fa98 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -11,6 +11,7 @@ rolesMap = { cut: 'cut', copy: 'copy', paste: 'paste', + pasteandmatchstyle: 'pasteAndMatchStyle', selectall: 'selectAll', minimize: 'minimize', close: 'close', diff --git a/lib/browser/api/protocol.js b/lib/browser/api/protocol.js index b146931a2b48..4daf4edaccd6 100644 --- a/lib/browser/api/protocol.js +++ b/lib/browser/api/protocol.js @@ -1,5 +1,5 @@ -const {app} = require('electron') -const {createProtocolObject, registerStandardSchemes} = process.atomBinding('protocol') +const {app, session} = require('electron') +const {registerStandardSchemes} = process.atomBinding('protocol') exports.registerStandardSchemes = function (schemes) { if (app.isReady()) { @@ -10,7 +10,7 @@ exports.registerStandardSchemes = function (schemes) { } app.once('ready', function () { - let protocol = createProtocolObject() + let protocol = session.defaultSession.protocol for (let method in protocol) { exports[method] = protocol[method].bind(protocol) } diff --git a/lib/browser/api/session.js b/lib/browser/api/session.js index dc1c529878fb..4cfb27f76d63 100644 --- a/lib/browser/api/session.js +++ b/lib/browser/api/session.js @@ -3,6 +3,7 @@ const electron = require('electron') const bindings = process.atomBinding('session') const PERSIST_PREFIX = 'persist:' +const Session = new EventEmitter() // Wrapper of binding.fromPartition that checks for ready event. const fromPartition = function (partition, persist) { @@ -14,7 +15,7 @@ const fromPartition = function (partition, persist) { } // Returns the Session from |partition| string. -exports.fromPartition = function (partition = '') { +Session.fromPartition = function (partition = '') { if (partition === '') return exports.defaultSession if (partition.startsWith(PERSIST_PREFIX)) { @@ -25,7 +26,7 @@ exports.fromPartition = function (partition = '') { } // Returns the default session. -Object.defineProperty(exports, 'defaultSession', { +Object.defineProperty(Session, 'defaultSession', { enumerable: true, get: function () { return fromPartition('', false) @@ -35,6 +36,9 @@ Object.defineProperty(exports, 'defaultSession', { const wrapSession = function (session) { // Session is an EventEmitter. Object.setPrototypeOf(session, EventEmitter.prototype) + Session.emit('session-created', session) } bindings._setWrapSession(wrapSession) + +module.exports = Session diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index ed7bfe0b8bdf..a3b4009aae77 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -10,6 +10,14 @@ session const binding = process.atomBinding('web_contents') const debuggerBinding = process.atomBinding('debugger') +const WebContents = new EventEmitter() +WebContents.create = (options = {}) => { + return binding.create(options) +} +WebContents.fromId = (id) => { + return binding.fromId(id) +} + let nextId = 0 const getNextId = function () { return ++nextId @@ -223,6 +231,8 @@ const wrapWebContents = function (webContents) { this._printToPDF(printingSetting, callback) } + + WebContents.emit('web-contents-created', webContents) } binding._setWrapWebContents(wrapWebContents) @@ -235,12 +245,4 @@ const wrapDebugger = function (webContentsDebugger) { debuggerBinding._setWrapDebugger(wrapDebugger) -module.exports = { - create (options = {}) { - return binding.create(options) - }, - - fromId (id) { - return binding.fromId(id) - } -} +module.exports = WebContents diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js index 775a31d2d03e..ea9dc15f2c71 100644 --- a/lib/browser/chrome-extension.js +++ b/lib/browser/chrome-extension.js @@ -1,4 +1,5 @@ -const {app, ipcMain, protocol, webContents, BrowserWindow} = require('electron') +const {app, ipcMain, session, webContents, BrowserWindow} = require('electron') +const {getAllWebContents} = process.atomBinding('web_contents') const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllBrowserWindow() const fs = require('fs') @@ -20,10 +21,27 @@ const generateExtensionIdFromName = function (name) { // Create or get manifest object from |srcDirectory|. const getManifestFromPath = function (srcDirectory) { - const manifest = JSON.parse(fs.readFileSync(path.join(srcDirectory, 'manifest.json'))) + let manifest + let manifestContent + + try { + manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')) + } catch (readError) { + console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`) + console.warn(readError.stack || readError) + throw readError + } + + try { + manifest = JSON.parse(manifestContent) + } catch (parseError) { + console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`) + console.warn(parseError.stack || parseError) + throw parseError + } + if (!manifestNameMap[manifest.name]) { const extensionId = generateExtensionIdFromName(manifest.name) - console.log(extensionId) manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest Object.assign(manifest, { srcDirectory: srcDirectory, @@ -38,6 +56,8 @@ const getManifestFromPath = function (srcDirectory) { }) }) return manifest + } else if (manifest && manifest.name) { + console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`) } } @@ -72,13 +92,13 @@ const removeBackgroundPages = function (manifest) { } // Dispatch tabs events. -const hookWindowForTabEvents = function (win) { - const tabId = win.webContents.id +const hookWebContentsForTabEvents = function (webContents) { + const tabId = webContents.id for (const page of objectValues(backgroundPages)) { page.webContents.sendToAll('CHROME_TABS_ONCREATED', tabId) } - win.once('closed', () => { + webContents.once('destroyed', () => { for (const page of objectValues(backgroundPages)) { page.webContents.sendToAll('CHROME_TABS_ONREMOVED', tabId) } @@ -105,6 +125,10 @@ ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) page.webContents.sendToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo) }) +ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) { + event.returnValue = manifestMap[extensionId] +}) + ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message) { const page = backgroundPages[extensionId] if (!page) { @@ -212,6 +236,15 @@ const loadDevToolsExtensions = function (win, manifests) { win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`) } +webContents.on('web-contents-created', function (webContents) { + if (webContents.getType() === 'remote') return + + hookWebContentsForTabEvents(webContents) + webContents.on('devtools-opened', function () { + loadDevToolsExtensions(webContents, objectValues(manifestMap)) + }) +}) + // The persistent path of "DevTools Extensions" preference file. let loadedExtensionsPath = null @@ -261,10 +294,12 @@ app.once('ready', function () { } }) } - protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) { - if (error) { - console.error(`Unable to register chrome-extension protocol: ${error}`) - } + session.on('session-created', function (ses) { + ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) { + if (error) { + console.error(`Unable to register chrome-extension protocol: ${error}`) + } + }) }) // Load persisted extensions. @@ -286,12 +321,15 @@ app.once('ready', function () { BrowserWindow.addDevToolsExtension = function (srcDirectory) { const manifest = getManifestFromPath(srcDirectory) if (manifest) { - for (const win of BrowserWindow.getAllWindows()) { - loadDevToolsExtensions(win, [manifest]) + for (const webContents of getAllWebContents()) { + if (webContents.getType() !== 'remote') { + loadDevToolsExtensions(webContents, [manifest]) + } } return manifest.name } } + BrowserWindow.removeDevToolsExtension = function (name) { const manifest = manifestNameMap[name] if (!manifest) return @@ -301,14 +339,4 @@ app.once('ready', function () { delete manifestMap[manifest.extensionId] delete manifestNameMap[name] } - - // Load extensions automatically when devtools is opened. - const init = BrowserWindow.prototype._init - BrowserWindow.prototype._init = function () { - init.call(this) - hookWindowForTabEvents(this) - this.webContents.on('devtools-opened', () => { - loadDevToolsExtensions(this, objectValues(manifestMap)) - }) - } }) diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index 6876a8d22c12..fdd426c229f6 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -4,9 +4,9 @@ const ipcMain = require('electron').ipcMain const webContents = require('electron').webContents // Doesn't exist in early initialization. -var webViewManager = null +let webViewManager = null -var supportedWebViewEvents = [ +const supportedWebViewEvents = [ 'load-commit', 'did-finish-load', 'did-fail-load', @@ -36,32 +36,33 @@ var supportedWebViewEvents = [ 'media-started-playing', 'media-paused', 'found-in-page', - 'did-change-theme-color' + 'did-change-theme-color', + 'update-target-url' ] -var nextInstanceId = 0 -var guestInstances = {} -var embedderElementsMap = {} -var reverseEmbedderElementsMap = {} +let nextInstanceId = 0 +const guestInstances = {} +const embedderElementsMap = {} +const reverseEmbedderElementsMap = {} // Moves the last element of array to the first one. -var moveLastToFirst = function (list) { +const moveLastToFirst = function (list) { return list.unshift(list.pop()) } // Generate guestInstanceId. -var getNextInstanceId = function () { +const getNextInstanceId = function () { return ++nextInstanceId } // Create a new guest instance. -var createGuest = function (embedder, params) { - var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners +const createGuest = function (embedder, params) { if (webViewManager == null) { webViewManager = process.atomBinding('web_view_manager') } - id = getNextInstanceId(embedder) - guest = webContents.create({ + + const id = getNextInstanceId(embedder) + const guest = webContents.create({ isGuest: true, partition: params.partition, embedder: embedder @@ -72,37 +73,32 @@ var createGuest = function (embedder, params) { } // Destroy guest when the embedder is gone or navigated. - destroyEvents = ['will-destroy', 'crashed', 'did-navigate'] - destroy = function () { + const destroyEvents = ['will-destroy', 'crashed', 'did-navigate'] + const destroy = function () { if (guestInstances[id] != null) { - return destroyGuest(embedder, id) + destroyGuest(embedder, id) } } - for (i = 0, len = destroyEvents.length; i < len; i++) { - event = destroyEvents[i] + for (const event of destroyEvents) { embedder.once(event, destroy) - // Users might also listen to the crashed event, so We must ensure the guest + // Users might also listen to the crashed event, so we must ensure the guest // is destroyed before users' listener gets called. It is done by moving our // listener to the first one in queue. - listeners = embedder._events[event] + const listeners = embedder._events[event] if (Array.isArray(listeners)) { moveLastToFirst(listeners) } } guest.once('destroyed', function () { - var j, len1, results - results = [] - for (j = 0, len1 = destroyEvents.length; j < len1; j++) { - event = destroyEvents[j] - results.push(embedder.removeListener(event, destroy)) + for (const event of destroyEvents) { + embedder.removeListener(event, destroy) } - return results }) // Init guest web view after attached. guest.once('did-attach', function () { - var opts + let opts params = this.attachParams delete this.attachParams this.viewInstanceId = params.instanceId @@ -135,13 +131,12 @@ var createGuest = function (embedder, params) { }) // Dispatch events to embedder. - fn = function (event) { - return guest.on(event, function (_, ...args) { + const fn = function (event) { + guest.on(event, function (_, ...args) { embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(args)) }) } - for (j = 0, len1 = supportedWebViewEvents.length; j < len1; j++) { - event = supportedWebViewEvents[j] + for (const event of supportedWebViewEvents) { fn(event) } @@ -154,12 +149,13 @@ var createGuest = function (embedder, params) { guest.on('size-changed', function (_, ...args) { embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args)) }) + return id } // Attach the guest to an element of embedder. -var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) { - var guest, key, oldGuestInstanceId, ref1, webPreferences +const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) { + let guest, key, oldGuestInstanceId, ref1, webPreferences guest = guestInstances[guestInstanceId].guest // Destroy the old guest when attaching. @@ -181,7 +177,8 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params plugins: params.plugins, zoomFactor: params.zoomFactor, webSecurity: !params.disablewebsecurity, - blinkFeatures: params.blinkfeatures + blinkFeatures: params.blinkfeatures, + disableBlinkFeatures: params.disableblinkfeatures } if (params.preload) { @@ -194,12 +191,12 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params } // Destroy an existing guest instance. -var destroyGuest = function (embedder, id) { - var key +const destroyGuest = function (embedder, id) { webViewManager.removeGuest(embedder, id) guestInstances[id].guest.destroy() delete guestInstances[id] - key = reverseEmbedderElementsMap[id] + + const key = reverseEmbedderElementsMap[id] if (key != null) { delete reverseEmbedderElementsMap[id] return delete embedderElementsMap[key] @@ -219,18 +216,18 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, id) { }) ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, id, params) { - var ref1 - return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0 + const guestInstance = guestInstances[id] + return guestInstance != null ? guestInstance.guest.setSize(params) : void 0 }) // Returns WebContents from its guest id. exports.getGuest = function (id) { - var ref1 - return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0 + const guestInstance = guestInstances[id] + return guestInstance != null ? guestInstance.guest : void 0 } // Returns the embedder of the guest. exports.getEmbedder = function (id) { - var ref1 - return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0 + const guestInstance = guestInstances[id] + return guestInstance != null ? guestInstance.embedder : void 0 } diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js index a3d60bee8add..776ab91fec96 100644 --- a/lib/browser/guest-window-manager.js +++ b/lib/browser/guest-window-manager.js @@ -1,14 +1,13 @@ 'use strict' -const ipcMain = require('electron').ipcMain -const BrowserWindow = require('electron').BrowserWindow +const {BrowserWindow, ipcMain, webContents} = require('electron') -var hasProp = {}.hasOwnProperty -var frameToGuest = {} +const hasProp = {}.hasOwnProperty +const frameToGuest = {} // Copy attribute of |parent| to |child| if it is not defined in |child|. -var mergeOptions = function (child, parent) { - var key, value +const mergeOptions = function (child, parent) { + let key, value for (key in parent) { if (!hasProp.call(parent, key)) continue value = parent[key] @@ -24,7 +23,7 @@ var mergeOptions = function (child, parent) { } // Merge |options| with the |embedder|'s window's options. -var mergeBrowserWindowOptions = function (embedder, options) { +const mergeBrowserWindowOptions = function (embedder, options) { if (embedder.browserWindowOptions != null) { // Inherit the original options if it is a BrowserWindow. mergeOptions(options, embedder.browserWindowOptions) @@ -45,9 +44,8 @@ var mergeBrowserWindowOptions = function (embedder, options) { } // Create a new guest created by |embedder| with |options|. -var createGuest = function (embedder, url, frameName, options) { - var closedByEmbedder, closedByUser, guest, guestId, ref1 - guest = frameToGuest[frameName] +const createGuest = function (embedder, url, frameName, options) { + let guest = frameToGuest[frameName] if (frameName && (guest != null)) { guest.loadURL(url) return guest.id @@ -57,24 +55,26 @@ var createGuest = function (embedder, url, frameName, options) { if (options.webPreferences == null) { options.webPreferences = {} } - options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0 + options.webPreferences.openerId = embedder.id guest = new BrowserWindow(options) guest.loadURL(url) // When |embedder| is destroyed we should also destroy attached guest, and if // guest is closed by user then we should prevent |embedder| from double // closing guest. - guestId = guest.id - closedByEmbedder = function () { + const guestId = guest.webContents.id + + const closedByEmbedder = function () { guest.removeListener('closed', closedByUser) - return guest.destroy() + guest.destroy() } - closedByUser = function () { + const closedByUser = function () { embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId) - return embedder.removeListener('render-view-deleted', closedByEmbedder) + embedder.removeListener('render-view-deleted', closedByEmbedder) } embedder.once('render-view-deleted', closedByEmbedder) guest.once('closed', closedByUser) + if (frameName) { frameToGuest[frameName] = guest guest.frameName = frameName @@ -82,7 +82,22 @@ var createGuest = function (embedder, url, frameName, options) { delete frameToGuest[frameName] }) } - return guest.id + + return guestId +} + +const getGuestWindow = function (guestId) { + const guestContents = webContents.fromId(guestId) + if (guestContents == null) return + + let guestWindow = BrowserWindow.fromWebContents(guestContents) + if (guestWindow == null) { + const hostContents = guestContents.hostWebContents + if (hostContents != null) { + guestWindow = BrowserWindow.fromWebContents(hostContents) + } + } + return guestWindow } // Routed window.open messages. @@ -97,28 +112,26 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, fr }) ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) { - var ref1 - (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0 + const guestWindow = getGuestWindow(guestId) + if (guestWindow != null) guestWindow.destroy() }) ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) { - var ref1 - event.returnValue = (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0 + const guestWindow = getGuestWindow(guestId) + event.returnValue = guestWindow != null ? guestWindow[method].apply(guestWindow, args) : null }) ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) { - var guestContents, ref1, ref2, sourceId - sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0 - if (sourceId == null) { - return - } - guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0 - if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') { - guestContents != null ? guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0 + const guestContents = webContents.fromId(guestId) + if (guestContents == null) return + + if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') { + const sourceId = event.sender.id + guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) } }) ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) { - var ref1, ref2 - (ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0 + const guestContents = webContents.fromId(guestId) + if (guestContents != null) guestContents[method].apply(guestContents, args) }) diff --git a/lib/renderer/chrome-api.js b/lib/renderer/chrome-api.js index 61fce14c5ba9..fdc16f7375d2 100644 --- a/lib/renderer/chrome-api.js +++ b/lib/renderer/chrome-api.js @@ -99,6 +99,8 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) { }) chrome.runtime = { + id: extensionId, + getURL: function (path) { return url.format({ protocol: 'chrome-extension', @@ -181,12 +183,7 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) { onMessage: chrome.runtime.onMessage } - chrome.storage = { - sync: { - get () {}, - set () {} - } - } + chrome.storage = require('./extensions/storage.js') chrome.pageAction = { show () {}, @@ -197,4 +194,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) { setPopup () {}, getPopup () {} } + + chrome.i18n = require('./extensions/i18n.js').setup(extensionId) } diff --git a/lib/renderer/extensions/i18n.js b/lib/renderer/extensions/i18n.js new file mode 100644 index 000000000000..d0279601f43d --- /dev/null +++ b/lib/renderer/extensions/i18n.js @@ -0,0 +1,84 @@ +// Implementation of chrome.i18n.getMessage +// https://developer.chrome.com/extensions/i18n#method-getMessage +// +// Does not implement predefined messages: +// https://developer.chrome.com/extensions/i18n#overview-predefined + +const {ipcRenderer} = require('electron') +const fs = require('fs') +const path = require('path') + +let metadata + +const getExtensionMetadata = (extensionId) => { + if (!metadata) { + metadata = ipcRenderer.sendSync('CHROME_I18N_MANIFEST', extensionId) + } + return metadata +} + +const getMessagesPath = (extensionId, language) => { + const metadata = getExtensionMetadata(extensionId) + const defaultLocale = metadata.default_locale || 'en' + const localesDirectory = path.join(metadata.srcDirectory, '_locales') + let messagesPath = path.join(localesDirectory, language, 'messages.json') + if (!fs.statSyncNoException(messagesPath)) { + messagesPath = path.join(localesDirectory, defaultLocale, 'messages.json') + } + return messagesPath +} + +const getMessages = (extensionId, language) => { + try { + const messagesPath = getMessagesPath(extensionId, language) + return JSON.parse(fs.readFileSync(messagesPath)) || {} + } catch (error) { + return {} + } +} + +const getLanguage = () => { + return navigator.language.replace(/-.*$/, '').toLowerCase() +} + +const replaceNumberedSubstitutions = (message, substitutions) => { + return message.replace(/\$(\d+)/, (_, number) => { + const index = parseInt(number, 10) - 1 + return substitutions[index] || '' + }) +} + +const replacePlaceholders = (message, placeholders, substitutions) => { + if (typeof substitutions === 'string') { + substitutions = [substitutions] + } + if (!Array.isArray(substitutions)) { + substitutions = [] + } + + if (placeholders) { + Object.keys(placeholders).forEach((name) => { + let {content} = placeholders[name] + content = replaceNumberedSubstitutions(content, substitutions) + message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content) + }) + } + + return replaceNumberedSubstitutions(message, substitutions) +} + +const getMessage = (extensionId, messageName, substitutions) => { + const messages = getMessages(extensionId, getLanguage()) + if (messages.hasOwnProperty(messageName)) { + const {message, placeholders} = messages[messageName] + return replacePlaceholders(message, placeholders, substitutions) + } +} + +exports.setup = (extensionId) => { + return { + getMessage (messageName, substitutions) { + return getMessage(extensionId, messageName, substitutions) + } + } +} diff --git a/lib/renderer/extensions/storage.js b/lib/renderer/extensions/storage.js new file mode 100644 index 000000000000..7b880f08310e --- /dev/null +++ b/lib/renderer/extensions/storage.js @@ -0,0 +1,59 @@ +const getStorage = () => { + const data = window.localStorage.getItem('__chrome.storage.sync__') + if (data != null) { + return JSON.parse(data) + } else { + return {} + } +} + +const setStorage = (storage) => { + const json = JSON.stringify(storage) + window.localStorage.setItem('__chrome.storage.sync__', json) +} + +module.exports = { + sync: { + get (keys, callback) { + const storage = getStorage() + if (keys == null) return storage + + let defaults = {} + switch (typeof keys) { + case 'string': + keys = [keys] + break + case 'object': + if (!Array.isArray(keys)) { + defaults = keys + keys = Object.keys(keys) + } + break + } + if (keys.length === 0) return {} + + let items = {} + keys.forEach(function (key) { + var value = storage[key] + if (value == null) value = defaults[key] + items[key] = value + }) + + setTimeout(function () { + callback(items) + }) + }, + + set (items, callback) { + const storage = getStorage() + + Object.keys(items).forEach(function (name) { + storage[name] = items[name] + }) + + setStorage(storage) + + setTimeout(callback) + } + } +} diff --git a/lib/renderer/override.js b/lib/renderer/override.js index 5667e5e16f55..ced2318e7188 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -25,7 +25,13 @@ var BrowserWindowProxy = (function () { } function BrowserWindowProxy (guestId1) { - this.guestId = guestId1 + Object.defineProperty(this, 'guestId', { + configurable: false, + enumerable: true, + writeable: false, + value: guestId1 + }) + this.closed = false ipcRenderer.once('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + this.guestId, () => { BrowserWindowProxy.remove(this.guestId) diff --git a/lib/renderer/web-view/guest-view-internal.js b/lib/renderer/web-view/guest-view-internal.js index 7432444c6da6..c4741fc1b77f 100644 --- a/lib/renderer/web-view/guest-view-internal.js +++ b/lib/renderer/web-view/guest-view-internal.js @@ -27,15 +27,16 @@ var WEB_VIEW_EVENTS = { 'crashed': [], 'gpu-crashed': [], 'plugin-crashed': ['name', 'version'], - 'media-started-playing': [], - 'media-paused': [], - 'did-change-theme-color': ['themeColor'], 'destroyed': [], 'page-title-updated': ['title', 'explicitSet'], 'page-favicon-updated': ['favicons'], 'enter-html-full-screen': [], 'leave-html-full-screen': [], - 'found-in-page': ['result'] + 'media-started-playing': [], + 'media-paused': [], + 'found-in-page': ['result'], + 'did-change-theme-color': ['themeColor'], + 'update-target-url': ['url'] } var DEPRECATED_EVENTS = { diff --git a/lib/renderer/web-view/web-view-attributes.js b/lib/renderer/web-view/web-view-attributes.js index c76e0d26f953..e918578900cd 100644 --- a/lib/renderer/web-view/web-view-attributes.js +++ b/lib/renderer/web-view/web-view-attributes.js @@ -264,6 +264,13 @@ class BlinkFeaturesAttribute extends WebViewAttribute { } } +// Attribute that specifies the blink features to be disabled. +class DisableBlinkFeaturesAttribute extends WebViewAttribute { + constructor (webViewImpl) { + super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl) + } +} + // Sets up all of the webview attributes. WebViewImpl.prototype.setupWebViewAttributes = function () { this.attributes = {} @@ -278,6 +285,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () { this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this) this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) + this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH] autosizeAttributes.forEach((attribute) => { diff --git a/lib/renderer/web-view/web-view-constants.js b/lib/renderer/web-view/web-view-constants.js index 5501f4f3599b..96ee289831e9 100644 --- a/lib/renderer/web-view/web-view-constants.js +++ b/lib/renderer/web-view/web-view-constants.js @@ -16,6 +16,7 @@ module.exports = { ATTRIBUTE_PRELOAD: 'preload', ATTRIBUTE_USERAGENT: 'useragent', ATTRIBUTE_BLINKFEATURES: 'blinkfeatures', + ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures', // Internal attribute. ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid', diff --git a/lib/renderer/web-view/web-view.js b/lib/renderer/web-view/web-view.js index e9d300aabbcb..82ed62dd3a5d 100644 --- a/lib/renderer/web-view/web-view.js +++ b/lib/renderer/web-view/web-view.js @@ -379,7 +379,8 @@ var registerWebViewElement = function () { 'downloadURL', 'inspectServiceWorker', 'print', - 'printToPDF' + 'printToPDF', + 'showDefinitionForSelection' ] nonblockMethods = [ 'insertCSS', diff --git a/package.json b/package.json index 6f873786e3f0..824cae71107b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "1.2.1", + "version": "1.2.2", "devDependencies": { "asar": "^0.11.0", "request": "*", diff --git a/script/lib/config.py b/script/lib/config.py index 43423e514368..dcdb7e2cfe23 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 = '8f2a0aa0a9dd107e4d574cc9d552781c55c86bab' +LIBCHROMIUMCONTENT_COMMIT = '7ae12b2856b81f5bfa1290108f72c3e4b5c13c52' PLATFORM = { 'cygwin': 'win32', diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 8af2fb826574..5048f3cd4a93 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -858,12 +858,27 @@ describe('browser-window module', function () { w.loadURL('about:blank') }) + it('throws errors for missing manifest.json files', function () { + assert.throws(function () { + BrowserWindow.addDevToolsExtension(path.join(__dirname, 'does-not-exist')) + }, /ENOENT: no such file or directory/) + }) + + it('throws errors for invalid manifest.json files', function () { + assert.throws(function () { + BrowserWindow.addDevToolsExtension(path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest')) + }, /Unexpected token }/) + }) + describe('when the devtools is docked', function () { it('creates the extension', function (done) { w.webContents.openDevTools({mode: 'bottom'}) ipcMain.once('answer', function (event, message) { - assert.equal(message, 'extension loaded') + 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'}) done() }) }) @@ -873,14 +888,54 @@ describe('browser-window module', function () { it('creates the extension', function (done) { w.webContents.openDevTools({mode: 'undocked'}) - ipcMain.once('answer', function (event, message) { - assert.equal(message, 'extension loaded') + ipcMain.once('answer', function (event, message, extensionId) { + assert.equal(message.runtimeId, 'foo') + assert.equal(message.tabId, w.webContents.id) done() }) }) }) }) + it('works when used with partitions', function (done) { + this.timeout(10000) + + if (w != null) { + w.destroy() + } + w = new BrowserWindow({ + show: false, + webPreferences: { + partition: 'temp' + } + }) + + var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo') + BrowserWindow.removeDevToolsExtension('foo') + BrowserWindow.addDevToolsExtension(extensionPath) + + w.webContents.on('devtools-opened', function () { + var showPanelIntevalId = setInterval(function () { + if (w && w.devToolsWebContents) { + w.devToolsWebContents.executeJavaScript('(' + (function () { + var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id + WebInspector.inspectorView.showPanel(lastPanelId) + }).toString() + ')()') + } else { + clearInterval(showPanelIntevalId) + } + }, 100) + }) + + w.loadURL('about:blank') + w.webContents.openDevTools({mode: 'bottom'}) + + ipcMain.once('answer', function (event, message) { + assert.equal(message.runtimeId, 'foo') + done() + }) + }) + it('serializes the registered extensions on quit', function () { var extensionName = 'foo' var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName) diff --git a/spec/api-protocol-spec.js b/spec/api-protocol-spec.js index 803511917104..57e8638ee6d3 100644 --- a/spec/api-protocol-spec.js +++ b/spec/api-protocol-spec.js @@ -919,6 +919,23 @@ describe('protocol module', function () { }) w.loadURL(origin) }) + }), + + it('can have fetch working in it', function (done) { + const content = '' + const handler = function (request, callback) { + callback({data: content, mimeType: 'text/html'}) + } + protocol.registerStringProtocol(standardScheme, handler, function (error) { + if (error) return done(error) + w.webContents.on('crashed', function () { + done('WebContents crashed') + }) + w.webContents.on('did-finish-load', function () { + done() + }) + w.loadURL(origin) + }) }) }) }) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index d655788c79b3..9529ec5e0072 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -16,8 +16,15 @@ 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 tempProtocol = session.fromPartition(partitionName).protocol + const protocol = session.defaultSession.protocol beforeEach(function () { + if (w != null) { + w.destroy() + } w = new BrowserWindow({ show: false, width: 400, @@ -26,7 +33,10 @@ describe('session module', function () { }) afterEach(function () { - w.destroy() + if (w != null) { + w.destroy() + } + w = null }) describe('session.cookies', function () { @@ -262,4 +272,43 @@ describe('session module', function () { }) }) }) + + describe('session.protocol', function () { + beforeEach(function () { + if (w != null) { + w.destroy() + } + w = new BrowserWindow({ + show: false, + width: 400, + height: 400, + webPreferences: { + partition: partitionName + } + }) + }) + + it('handles requests from a partition', function (done) { + var handler = function (error, callback) { + callback({ + data: 'test' + }) + } + tempProtocol.registerStringProtocol(protocolName, handler, function (error) { + if (error) { + return done(error) + } + protocol.isProtocolHandled(protocolName, function (result) { + assert.equal(result, false) + tempProtocol.isProtocolHandled(protocolName, function (result) { + assert.equal(result, true) + w.webContents.on('did-finish-load', function () { + done() + }) + w.loadURL(protocolName + "://fake-host") + }) + }) + }) + }) + }) }) diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index cd046ab29abf..68836fb927ed 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -1,12 +1,11 @@ - const assert = require('assert') const http = require('http') const path = require('path') const ws = require('ws') +const url = require('url') const remote = require('electron').remote -const BrowserWindow = remote.require('electron').BrowserWindow -const session = remote.require('electron').session +const {BrowserWindow, session, webContents} = remote const isCI = remote.getGlobal('isCi') @@ -165,6 +164,12 @@ describe('chromium feature', function () { var b = window.open('about:blank', '', 'show=no') assert.equal(b.closed, false) assert.equal(b.constructor.name, 'BrowserWindowProxy') + + // Check that guestId is not writeable + assert(b.guestId) + b.guestId = 'anotherValue' + assert.notEqual(b.guestId, 'anoterValue') + b.close() }) @@ -235,7 +240,7 @@ describe('chromium feature', function () { else targetURL = 'file://' + fixtures + '/pages/base-page.html' b = window.open(targetURL) - BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () { + webContents.fromId(b.guestId).once('did-finish-load', function () { assert.equal(b.location, targetURL) b.close() done() @@ -246,10 +251,10 @@ describe('chromium feature', function () { // Load a page that definitely won't redirect var b b = window.open('about:blank') - BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () { + webContents.fromId(b.guestId).once('did-finish-load', function () { // When it loads, redirect b.location = 'file://' + fixtures + '/pages/base-page.html' - BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () { + webContents.fromId(b.guestId).once('did-finish-load', function () { // After our second redirect, cleanup and callback b.close() done() @@ -308,7 +313,7 @@ describe('chromium feature', function () { } window.addEventListener('message', listener) b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no') - BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () { + webContents.fromId(b.guestId).once('did-finish-load', function () { b.postMessage('testing', '*') }) }) @@ -327,6 +332,25 @@ describe('chromium feature', function () { window.addEventListener('message', listener) b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no') }) + + it('supports windows opened from a ', function (done) { + const webview = new WebView() + webview.addEventListener('console-message', function (e) { + webview.remove() + assert.equal(e.message, 'message') + done() + }) + webview.allowpopups = true + webview.src = url.format({ + pathname: `${fixtures}/pages/webview-opener-postMessage.html`, + protocol: 'file', + query: { + p: `${fixtures}/pages/window-opener-postMessage.html` + }, + slashes: true + }) + document.body.appendChild(webview) + }) }) describe('creating a Uint8Array under browser side', function () { diff --git a/spec/fixtures/devtools-extensions/bad-manifest/manifest.json b/spec/fixtures/devtools-extensions/bad-manifest/manifest.json new file mode 100644 index 000000000000..5c34318c2147 --- /dev/null +++ b/spec/fixtures/devtools-extensions/bad-manifest/manifest.json @@ -0,0 +1 @@ +} diff --git a/spec/fixtures/devtools-extensions/foo/_locales/en/messages.json b/spec/fixtures/devtools-extensions/foo/_locales/en/messages.json new file mode 100644 index 000000000000..fe5653c2b411 --- /dev/null +++ b/spec/fixtures/devtools-extensions/foo/_locales/en/messages.json @@ -0,0 +1,10 @@ +{ + "foo": { + "message": "foo - $BAZ$ ($2)", + "placeholders": { + "baz": { + "content": "$1" + } + } + } +} diff --git a/spec/fixtures/devtools-extensions/foo/index.html b/spec/fixtures/devtools-extensions/foo/index.html index 4e7439422e47..b10288227aca 100644 --- a/spec/fixtures/devtools-extensions/foo/index.html +++ b/spec/fixtures/devtools-extensions/foo/index.html @@ -4,8 +4,22 @@ diff --git a/spec/fixtures/devtools-extensions/foo/manifest.json b/spec/fixtures/devtools-extensions/foo/manifest.json index 413853e8d351..b88ab65a1ea8 100644 --- a/spec/fixtures/devtools-extensions/foo/manifest.json +++ b/spec/fixtures/devtools-extensions/foo/manifest.json @@ -1,5 +1,6 @@ { "name": "foo", "version": "1.0", - "devtools_page": "foo.html" + "devtools_page": "foo.html", + "default_locale": "en" } diff --git a/spec/fixtures/pages/webview-devtools.html b/spec/fixtures/pages/webview-devtools.html new file mode 100644 index 000000000000..c16dbb2f8f14 --- /dev/null +++ b/spec/fixtures/pages/webview-devtools.html @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/spec/fixtures/pages/webview-opener-postMessage.html b/spec/fixtures/pages/webview-opener-postMessage.html new file mode 100644 index 000000000000..e926e32bc079 --- /dev/null +++ b/spec/fixtures/pages/webview-opener-postMessage.html @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/spec/static/index.html b/spec/static/index.html index e31fcffebb89..61647c03d47c 100644 --- a/spec/static/index.html +++ b/spec/static/index.html @@ -50,6 +50,18 @@ var Mocha = require('mocha'); var mocha = new Mocha(); + + if (isCi) { + mocha.grep = function () { + try { + throw new Error('A spec contains a call to it.only or describe.only and should be reverted.') + } catch (error) { + console.error(error.stack || error) + } + ipcRenderer.send('process.exit', 1) + } + } + mocha.ui('bdd').reporter(isCi ? 'tap' : 'html'); var query = Mocha.utils.parseQuery(window.location.search || ''); diff --git a/spec/webview-spec.js b/spec/webview-spec.js index b1fa4405ada8..be0271e85aa6 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -912,4 +912,25 @@ describe(' tag', function () { w.loadURL('file://' + fixtures + '/pages/webview-visibilitychange.html') }) + + it('loads devtools extensions registered on the parent window', function (done) { + this.timeout(10000) + + w = new BrowserWindow({ + show: false + }) + + BrowserWindow.removeDevToolsExtension('foo') + + var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo') + BrowserWindow.addDevToolsExtension(extensionPath) + + w.loadURL('file://' + fixtures + '/pages/webview-devtools.html') + + ipcMain.once('answer', function (event, message) { + assert.equal(message.runtimeId, 'foo') + assert.notEqual(message.tabId, w.webContents.id) + done() + }) + }) }) diff --git a/vendor/brightray b/vendor/brightray index 9ccab3c9a87c..097d14b39f83 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 9ccab3c9a87cc57b4a37c4a03e17b589ac81d6ae +Subproject commit 097d14b39f83a945373c111dec46ad078c6f5f39