diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 87f960a87824..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))); diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index ba274deb9ccd..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, 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 de4de9de59d9..db8facfe0953 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -12,7 +12,7 @@ #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" @@ -173,17 +173,10 @@ void RegisterStandardSchemes( base::JoinString(schemes, ",")); } -mate::Handle CreateProtocol(v8::Isolate* isolate) { - auto browser_context = static_cast( - atom::AtomBrowserMainParts::Get()->browser_context()); - return atom::api::Protocol::Create(isolate, browser_context); -} - 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 6a18f0967b53..6cb9efb9614f 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -744,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(); } @@ -1287,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) @@ -1365,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 f8e6710a5c58..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); 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 b50de48dc54b..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); diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 73aa3017eb3f..8de4b109373a 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -524,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]; @@ -555,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() { 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/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/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/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/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/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/quick-start.md b/docs-translations/zh-CN/tutorial/quick-start.md index 9faddf1bb09e..6ea9d6d5525c 100644 --- a/docs-translations/zh-CN/tutorial/quick-start.md +++ b/docs-translations/zh-CN/tutorial/quick-start.md @@ -49,24 +49,24 @@ const {BrowserWindow} = electron; // 保持一个对于 window 对象的全局引用,如果你不这样做, // 当 JavaScript 对象被垃圾回收, window 会被自动地关闭 -let win; +let mainWindow; function createWindow() { // 创建浏览器窗口。 - win = new BrowserWindow({width: 800, height: 600}); + mainWindow = new BrowserWindow({width: 800, height: 600}); // 加载应用的 index.html。 - win.loadURL(`file://${__dirname}/index.html`); + mainWindow.loadURL(`file://${__dirname}/index.html`); // 启用开发工具。 - win.webContents.openDevTools(); + mainWindow.webContents.openDevTools(); // 当 window 被关闭,这个事件会被触发。 - win.on('closed', () => { + mainWindow.on('closed', () => { // 取消引用 window 对象,如果你的应用支持多窗口的话, // 通常会把多个 window 对象存放在一个数组里面, // 与此同时,你应该删除相应的元素。 - win = null; + mainWindow = null; }); } @@ -87,7 +87,7 @@ app.on('window-all-closed', () => { 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 (win === null) { + if (mainWindow === null) { createWindow(); } }); @@ -146,7 +146,7 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/ 在你完成了你的应用后,你可以按照 [应用部署][7] 指导发布一个版本,并且以已经打包好的形式运行应用。 # 参照下面例子 -复制并且运行这个库 [atom/electron-quick-start][8]。 +复制并且运行这个库 [electron/electron-quick-start][8]。 *注意:*运行时需要你的系统已经安装了 [Git][9] 和 [Node.js][10](包含 [npm][11])。 @@ -168,4 +168,4 @@ $ npm install && npm start [8]: https://github.com/electron/electron-quick-start [9]: https://git-scm.com/ [10]: https://nodejs.org/en/download/ - [11]: https://www.npmjs.com/ \ No newline at end of file + [11]: https://www.npmjs.com/ diff --git a/docs/api/app.md b/docs/api/app.md index 1f72dba2ca0b..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)` 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/session.md b/docs/api/session.md index e7affddde932..030b2c7542ea 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -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/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/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/filenames.gypi b/filenames.gypi index 891d28fada49..2b87e36fa4d2 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -63,7 +63,8 @@ 'lib/renderer/api/remote.js', 'lib/renderer/api/screen.js', 'lib/renderer/api/web-frame.js', - 'lib/renderer/extensions/storage.js' + 'lib/renderer/extensions/i18n.js', + 'lib/renderer/extensions/storage.js', ], 'js2c_sources': [ 'lib/common/asar.js', 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 d3c76ce797be..97bf292f9e23 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') @@ -81,13 +82,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) } @@ -114,6 +115,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) { @@ -221,6 +226,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 @@ -270,10 +284,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. @@ -295,12 +311,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 @@ -310,14 +329,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/renderer/chrome-api.js b/lib/renderer/chrome-api.js index c614b8f1e468..fdc16f7375d2 100644 --- a/lib/renderer/chrome-api.js +++ b/lib/renderer/chrome-api.js @@ -194,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/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index e3e0fea595ad..c19bb460cab7 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -859,11 +859,13 @@ describe('browser-window module', function () { }) describe('when the devtools is docked', function () { - it('creates the extension', function (done) { + it.only('creates the extension', function (done) { w.webContents.openDevTools({mode: 'bottom'}) ipcMain.once('answer', function (event, message) { 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() }) @@ -876,12 +878,52 @@ describe('browser-window module', function () { 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-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/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 0a6b74f8eda5..b10288227aca 100644 --- a/spec/fixtures/devtools-extensions/foo/index.html +++ b/spec/fixtures/devtools-extensions/foo/index.html @@ -13,6 +13,8 @@ testStorage(function (items) { var message = JSON.stringify({ runtimeId: chrome.runtime.id, + tabId: chrome.devtools.inspectedWindow.tabId, + i18nString: chrome.i18n.getMessage('foo', ['bar', 'baz']), storageItems: items }) var sendMessage = `require('electron').ipcRenderer.send('answer', ${message})` 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/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() + }) + }) })