commit
70aa706524
108 changed files with 1485 additions and 594 deletions
|
@ -1 +1 @@
|
||||||
v5.10.0
|
v6.1.0
|
||||||
|
|
|
@ -110,6 +110,8 @@ int GetPathConstant(const std::string& name) {
|
||||||
return chrome::DIR_USER_PICTURES;
|
return chrome::DIR_USER_PICTURES;
|
||||||
else if (name == "videos")
|
else if (name == "videos")
|
||||||
return chrome::DIR_USER_VIDEOS;
|
return chrome::DIR_USER_VIDEOS;
|
||||||
|
else if (name == "pepperFlashSystemPlugin")
|
||||||
|
return chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN;
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -261,13 +263,14 @@ void App::OnContinueUserActivity(
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void App::OnLogin(LoginHandler* login_handler) {
|
void App::OnLogin(LoginHandler* login_handler,
|
||||||
|
const base::DictionaryValue& request_details) {
|
||||||
v8::Locker locker(isolate());
|
v8::Locker locker(isolate());
|
||||||
v8::HandleScope handle_scope(isolate());
|
v8::HandleScope handle_scope(isolate());
|
||||||
bool prevent_default = Emit(
|
bool prevent_default = Emit(
|
||||||
"login",
|
"login",
|
||||||
WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
|
WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
|
||||||
login_handler->request(),
|
request_details,
|
||||||
login_handler->auth_info(),
|
login_handler->auth_info(),
|
||||||
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
|
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
|
||||||
|
|
||||||
|
@ -447,6 +450,15 @@ bool App::Relaunch(mate::Arguments* js_args) {
|
||||||
return relauncher::RelaunchApp(argv);
|
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)
|
#if defined(USE_NSS_CERTS)
|
||||||
void App::ImportCertificate(
|
void App::ImportCertificate(
|
||||||
const base::DictionaryValue& options,
|
const base::DictionaryValue& options,
|
||||||
|
@ -528,7 +540,9 @@ void App::BuildPrototype(
|
||||||
#endif
|
#endif
|
||||||
.SetMethod("makeSingleInstance", &App::MakeSingleInstance)
|
.SetMethod("makeSingleInstance", &App::MakeSingleInstance)
|
||||||
.SetMethod("releaseSingleInstance", &App::ReleaseSingleInstance)
|
.SetMethod("releaseSingleInstance", &App::ReleaseSingleInstance)
|
||||||
.SetMethod("relaunch", &App::Relaunch);
|
.SetMethod("relaunch", &App::Relaunch)
|
||||||
|
.SetMethod("disableHardwareAcceleration",
|
||||||
|
&App::DisableHardwareAcceleration);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|
|
@ -70,7 +70,8 @@ class App : public AtomBrowserClient::Delegate,
|
||||||
void OnActivate(bool has_visible_windows) override;
|
void OnActivate(bool has_visible_windows) override;
|
||||||
void OnWillFinishLaunching() override;
|
void OnWillFinishLaunching() override;
|
||||||
void OnFinishLaunching() 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)
|
#if defined(OS_MACOSX)
|
||||||
void OnContinueUserActivity(
|
void OnContinueUserActivity(
|
||||||
bool* prevent_default,
|
bool* prevent_default,
|
||||||
|
@ -111,7 +112,7 @@ class App : public AtomBrowserClient::Delegate,
|
||||||
const ProcessSingleton::NotificationCallback& callback);
|
const ProcessSingleton::NotificationCallback& callback);
|
||||||
void ReleaseSingleInstance();
|
void ReleaseSingleInstance();
|
||||||
bool Relaunch(mate::Arguments* args);
|
bool Relaunch(mate::Arguments* args);
|
||||||
|
void DisableHardwareAcceleration(mate::Arguments* args);
|
||||||
#if defined(USE_NSS_CERTS)
|
#if defined(USE_NSS_CERTS)
|
||||||
void ImportCertificate(const base::DictionaryValue& options,
|
void ImportCertificate(const base::DictionaryValue& options,
|
||||||
const net::CompletionCallback& callback);
|
const net::CompletionCallback& callback);
|
||||||
|
|
|
@ -25,6 +25,9 @@ struct Converter<content::DownloadItem::DownloadState> {
|
||||||
content::DownloadItem::DownloadState state) {
|
content::DownloadItem::DownloadState state) {
|
||||||
std::string download_state;
|
std::string download_state;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
case content::DownloadItem::IN_PROGRESS:
|
||||||
|
download_state = "progressing";
|
||||||
|
break;
|
||||||
case content::DownloadItem::COMPLETE:
|
case content::DownloadItem::COMPLETE:
|
||||||
download_state = "completed";
|
download_state = "completed";
|
||||||
break;
|
break;
|
||||||
|
@ -85,7 +88,7 @@ void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
|
||||||
// Destroy the item once item is downloaded.
|
// Destroy the item once item is downloaded.
|
||||||
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
|
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
|
||||||
} else {
|
} else {
|
||||||
Emit("updated");
|
Emit("updated", item->GetState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,10 +102,18 @@ void DownloadItem::Pause() {
|
||||||
download_item_->Pause();
|
download_item_->Pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DownloadItem::IsPaused() const {
|
||||||
|
return download_item_->IsPaused();
|
||||||
|
}
|
||||||
|
|
||||||
void DownloadItem::Resume() {
|
void DownloadItem::Resume() {
|
||||||
download_item_->Resume();
|
download_item_->Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DownloadItem::CanResume() const {
|
||||||
|
return download_item_->CanResume();
|
||||||
|
}
|
||||||
|
|
||||||
void DownloadItem::Cancel() {
|
void DownloadItem::Cancel() {
|
||||||
download_item_->Cancel(true);
|
download_item_->Cancel(true);
|
||||||
download_item_->Remove();
|
download_item_->Remove();
|
||||||
|
@ -141,6 +152,14 @@ const GURL& DownloadItem::GetURL() const {
|
||||||
return download_item_->GetURL();
|
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) {
|
void DownloadItem::SetSavePath(const base::FilePath& path) {
|
||||||
save_path_ = path;
|
save_path_ = path;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +174,9 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
|
||||||
mate::ObjectTemplateBuilder(isolate, prototype)
|
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||||
.MakeDestroyable()
|
.MakeDestroyable()
|
||||||
.SetMethod("pause", &DownloadItem::Pause)
|
.SetMethod("pause", &DownloadItem::Pause)
|
||||||
|
.SetMethod("isPaused", &DownloadItem::IsPaused)
|
||||||
.SetMethod("resume", &DownloadItem::Resume)
|
.SetMethod("resume", &DownloadItem::Resume)
|
||||||
|
.SetMethod("canResume", &DownloadItem::CanResume)
|
||||||
.SetMethod("cancel", &DownloadItem::Cancel)
|
.SetMethod("cancel", &DownloadItem::Cancel)
|
||||||
.SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes)
|
.SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes)
|
||||||
.SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes)
|
.SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes)
|
||||||
|
@ -164,6 +185,8 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("getFilename", &DownloadItem::GetFilename)
|
.SetMethod("getFilename", &DownloadItem::GetFilename)
|
||||||
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
|
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
|
||||||
.SetMethod("getURL", &DownloadItem::GetURL)
|
.SetMethod("getURL", &DownloadItem::GetURL)
|
||||||
|
.SetMethod("getState", &DownloadItem::GetState)
|
||||||
|
.SetMethod("isDone", &DownloadItem::IsDone)
|
||||||
.SetMethod("setSavePath", &DownloadItem::SetSavePath)
|
.SetMethod("setSavePath", &DownloadItem::SetSavePath)
|
||||||
.SetMethod("getSavePath", &DownloadItem::GetSavePath);
|
.SetMethod("getSavePath", &DownloadItem::GetSavePath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
||||||
v8::Local<v8::ObjectTemplate> prototype);
|
v8::Local<v8::ObjectTemplate> prototype);
|
||||||
|
|
||||||
void Pause();
|
void Pause();
|
||||||
|
bool IsPaused() const;
|
||||||
void Resume();
|
void Resume();
|
||||||
|
bool CanResume() const;
|
||||||
void Cancel();
|
void Cancel();
|
||||||
int64_t GetReceivedBytes() const;
|
int64_t GetReceivedBytes() const;
|
||||||
int64_t GetTotalBytes() const;
|
int64_t GetTotalBytes() const;
|
||||||
|
@ -36,6 +38,8 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
||||||
std::string GetFilename() const;
|
std::string GetFilename() const;
|
||||||
std::string GetContentDisposition() const;
|
std::string GetContentDisposition() const;
|
||||||
const GURL& GetURL() const;
|
const GURL& GetURL() const;
|
||||||
|
content::DownloadItem::DownloadState GetState() const;
|
||||||
|
bool IsDone() const;
|
||||||
void SetSavePath(const base::FilePath& path);
|
void SetSavePath(const base::FilePath& path);
|
||||||
base::FilePath GetSavePath() const;
|
base::FilePath GetSavePath() const;
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,12 @@
|
||||||
#include "atom/browser/net/url_request_fetch_job.h"
|
#include "atom/browser/net/url_request_fetch_job.h"
|
||||||
#include "atom/browser/net/url_request_string_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/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/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 "native_mate/dictionary.h"
|
||||||
#include "url/url_util.h"
|
#include "url/url_util.h"
|
||||||
|
|
||||||
|
@ -158,21 +162,21 @@ namespace {
|
||||||
|
|
||||||
void RegisterStandardSchemes(
|
void RegisterStandardSchemes(
|
||||||
const std::vector<std::string>& schemes) {
|
const std::vector<std::string>& 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);
|
url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT);
|
||||||
}
|
policy->RegisterWebSafeScheme(scheme);
|
||||||
|
}
|
||||||
|
|
||||||
mate::Handle<atom::api::Protocol> CreateProtocol(v8::Isolate* isolate) {
|
auto command_line = base::CommandLine::ForCurrentProcess();
|
||||||
auto browser_context = static_cast<atom::AtomBrowserContext*>(
|
command_line->AppendSwitchASCII(atom::switches::kStandardSchemes,
|
||||||
atom::AtomBrowserMainParts::Get()->browser_context());
|
base::JoinString(schemes, ","));
|
||||||
return atom::api::Protocol::Create(isolate, browser_context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
v8::Local<v8::Context> context, void* priv) {
|
v8::Local<v8::Context> context, void* priv) {
|
||||||
v8::Isolate* isolate = context->GetIsolate();
|
v8::Isolate* isolate = context->GetIsolate();
|
||||||
mate::Dictionary dict(isolate, exports);
|
mate::Dictionary dict(isolate, exports);
|
||||||
dict.SetMethod("createProtocolObject", base::Bind(&CreateProtocol, isolate));
|
|
||||||
dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes);
|
dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "atom/browser/api/trackable_object.h"
|
||||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||||
#include "base/callback.h"
|
#include "base/callback.h"
|
||||||
#include "base/containers/scoped_ptr_hash_map.h"
|
#include "base/containers/scoped_ptr_hash_map.h"
|
||||||
|
@ -16,7 +17,10 @@
|
||||||
#include "native_mate/arguments.h"
|
#include "native_mate/arguments.h"
|
||||||
#include "native_mate/dictionary.h"
|
#include "native_mate/dictionary.h"
|
||||||
#include "native_mate/handle.h"
|
#include "native_mate/handle.h"
|
||||||
#include "native_mate/wrappable.h"
|
|
||||||
|
namespace base {
|
||||||
|
class DictionaryValue;
|
||||||
|
}
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
class URLRequest;
|
class URLRequest;
|
||||||
|
@ -30,10 +34,10 @@ class AtomURLRequestJobFactory;
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
class Protocol : public mate::Wrappable<Protocol> {
|
class Protocol : public mate::TrackableObject<Protocol> {
|
||||||
public:
|
public:
|
||||||
using Handler =
|
using Handler =
|
||||||
base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
|
base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
|
||||||
using CompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
using CompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||||
using BooleanCallback = base::Callback<void(bool)>;
|
using BooleanCallback = base::Callback<void(bool)>;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "atom/browser/api/atom_api_cookies.h"
|
#include "atom/browser/api/atom_api_cookies.h"
|
||||||
#include "atom/browser/api/atom_api_download_item.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/api/atom_api_web_request.h"
|
||||||
#include "atom/browser/atom_browser_context.h"
|
#include "atom/browser/atom_browser_context.h"
|
||||||
#include "atom/browser/atom_browser_main_parts.h"
|
#include "atom/browser/atom_browser_main_parts.h"
|
||||||
|
@ -462,6 +463,14 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
||||||
return v8::Local<v8::Value>::New(isolate, cookies_);
|
return v8::Local<v8::Value>::New(isolate, cookies_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> 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<v8::Value>::New(isolate, protocol_);
|
||||||
|
}
|
||||||
|
|
||||||
v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
|
v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
|
||||||
if (web_request_.IsEmpty()) {
|
if (web_request_.IsEmpty()) {
|
||||||
auto handle = atom::api::WebRequest::Create(isolate, browser_context());
|
auto handle = atom::api::WebRequest::Create(isolate, browser_context());
|
||||||
|
@ -512,6 +521,7 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("allowNTLMCredentialsForDomains",
|
.SetMethod("allowNTLMCredentialsForDomains",
|
||||||
&Session::AllowNTLMCredentialsForDomains)
|
&Session::AllowNTLMCredentialsForDomains)
|
||||||
.SetProperty("cookies", &Session::Cookies)
|
.SetProperty("cookies", &Session::Cookies)
|
||||||
|
.SetProperty("protocol", &Session::Protocol)
|
||||||
.SetProperty("webRequest", &Session::WebRequest);
|
.SetProperty("webRequest", &Session::WebRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,10 +81,12 @@ class Session: public mate::TrackableObject<Session>,
|
||||||
void ClearHostResolverCache(mate::Arguments* args);
|
void ClearHostResolverCache(mate::Arguments* args);
|
||||||
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
||||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||||
|
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
||||||
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
|
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
|
||||||
|
|
||||||
// Cached object.
|
// Cached object.
|
||||||
v8::Global<v8::Value> cookies_;
|
v8::Global<v8::Value> cookies_;
|
||||||
|
v8::Global<v8::Value> protocol_;
|
||||||
v8::Global<v8::Value> web_request_;
|
v8::Global<v8::Value> web_request_;
|
||||||
|
|
||||||
// The X-DevTools-Emulate-Network-Conditions-Client-Id.
|
// The X-DevTools-Emulate-Network-Conditions-Client-Id.
|
||||||
|
|
|
@ -389,6 +389,11 @@ void WebContents::ActivateContents(content::WebContents* source) {
|
||||||
Emit("activate");
|
Emit("activate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::UpdateTargetURL(content::WebContents* source,
|
||||||
|
const GURL& url) {
|
||||||
|
Emit("update-target-url", url);
|
||||||
|
}
|
||||||
|
|
||||||
bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
|
bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
|
||||||
return type_ == BROWSER_WINDOW;
|
return type_ == BROWSER_WINDOW;
|
||||||
}
|
}
|
||||||
|
@ -739,6 +744,15 @@ int WebContents::GetID() const {
|
||||||
return web_contents()->GetRenderProcessHost()->GetID();
|
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 {
|
bool WebContents::Equal(const WebContents* web_contents) const {
|
||||||
return GetID() == web_contents->GetID();
|
return GetID() == web_contents->GetID();
|
||||||
}
|
}
|
||||||
|
@ -1089,6 +1103,14 @@ void WebContents::StopFindInPage(content::StopFindAction action) {
|
||||||
web_contents()->StopFinding(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() {
|
void WebContents::Focus() {
|
||||||
web_contents()->Focus();
|
web_contents()->Focus();
|
||||||
}
|
}
|
||||||
|
@ -1274,6 +1296,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
|
.SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
|
||||||
.SetMethod("setSize", &WebContents::SetSize)
|
.SetMethod("setSize", &WebContents::SetSize)
|
||||||
.SetMethod("isGuest", &WebContents::IsGuest)
|
.SetMethod("isGuest", &WebContents::IsGuest)
|
||||||
|
.SetMethod("getType", &WebContents::GetType)
|
||||||
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
|
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
|
||||||
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
|
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
|
||||||
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
|
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
|
||||||
|
@ -1284,6 +1307,8 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("_printToPDF", &WebContents::PrintToPDF)
|
.SetMethod("_printToPDF", &WebContents::PrintToPDF)
|
||||||
.SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
|
.SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
|
||||||
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
|
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
|
||||||
|
.SetMethod("showDefinitionForSelection",
|
||||||
|
&WebContents::ShowDefinitionForSelection)
|
||||||
.SetProperty("id", &WebContents::ID)
|
.SetProperty("id", &WebContents::ID)
|
||||||
.SetProperty("session", &WebContents::Session)
|
.SetProperty("session", &WebContents::Session)
|
||||||
.SetProperty("hostWebContents", &WebContents::HostWebContents)
|
.SetProperty("hostWebContents", &WebContents::HostWebContents)
|
||||||
|
@ -1350,6 +1375,8 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents);
|
dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents);
|
||||||
dict.SetMethod("fromId",
|
dict.SetMethod("fromId",
|
||||||
&mate::TrackableObject<atom::api::WebContents>::FromWeakMapID);
|
&mate::TrackableObject<atom::api::WebContents>::FromWeakMapID);
|
||||||
|
dict.SetMethod("getAllWebContents",
|
||||||
|
&mate::TrackableObject<atom::api::WebContents>::GetAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -59,6 +59,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
v8::Local<v8::ObjectTemplate> prototype);
|
v8::Local<v8::ObjectTemplate> prototype);
|
||||||
|
|
||||||
int GetID() const;
|
int GetID() const;
|
||||||
|
std::string GetType() const;
|
||||||
bool Equal(const WebContents* web_contents) const;
|
bool Equal(const WebContents* web_contents) const;
|
||||||
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
||||||
void DownloadURL(const GURL& url);
|
void DownloadURL(const GURL& url);
|
||||||
|
@ -116,6 +117,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
void ReplaceMisspelling(const base::string16& word);
|
void ReplaceMisspelling(const base::string16& word);
|
||||||
uint32_t FindInPage(mate::Arguments* args);
|
uint32_t FindInPage(mate::Arguments* args);
|
||||||
void StopFindInPage(content::StopFindAction action);
|
void StopFindInPage(content::StopFindAction action);
|
||||||
|
void ShowDefinitionForSelection();
|
||||||
|
|
||||||
// Focus.
|
// Focus.
|
||||||
void Focus();
|
void Focus();
|
||||||
|
@ -182,6 +184,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
const gfx::Rect& pos) override;
|
const gfx::Rect& pos) override;
|
||||||
void CloseContents(content::WebContents* source) override;
|
void CloseContents(content::WebContents* source) override;
|
||||||
void ActivateContents(content::WebContents* contents) override;
|
void ActivateContents(content::WebContents* contents) override;
|
||||||
|
void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
|
||||||
bool IsPopupOrPanel(const content::WebContents* source) const override;
|
bool IsPopupOrPanel(const content::WebContents* source) const override;
|
||||||
void HandleKeyboardEvent(
|
void HandleKeyboardEvent(
|
||||||
content::WebContents* source,
|
content::WebContents* source,
|
||||||
|
|
|
@ -623,12 +623,6 @@ void Window::UnhookAllWindowMessages() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
|
||||||
void Window::ShowDefinitionForSelection() {
|
|
||||||
window_->ShowDefinitionForSelection();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TOOLKIT_VIEWS)
|
#if defined(TOOLKIT_VIEWS)
|
||||||
void Window::SetIcon(mate::Handle<NativeImage> icon) {
|
void Window::SetIcon(mate::Handle<NativeImage> icon) {
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
@ -760,10 +754,6 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage)
|
.SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage)
|
||||||
.SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages)
|
.SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages)
|
||||||
#endif
|
#endif
|
||||||
#if defined(OS_MACOSX)
|
|
||||||
.SetMethod("showDefinitionForSelection",
|
|
||||||
&Window::ShowDefinitionForSelection)
|
|
||||||
#endif
|
|
||||||
#if defined(TOOLKIT_VIEWS)
|
#if defined(TOOLKIT_VIEWS)
|
||||||
.SetMethod("setIcon", &Window::SetIcon)
|
.SetMethod("setIcon", &Window::SetIcon)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -169,10 +169,6 @@ class Window : public mate::TrackableObject<Window>,
|
||||||
void UnhookAllWindowMessages();
|
void UnhookAllWindowMessages();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
|
||||||
void ShowDefinitionForSelection();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TOOLKIT_VIEWS)
|
#if defined(TOOLKIT_VIEWS)
|
||||||
void SetIcon(mate::Handle<NativeImage> icon);
|
void SetIcon(mate::Handle<NativeImage> icon);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -169,6 +169,14 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
|
||||||
if (process_type != "renderer")
|
if (process_type != "renderer")
|
||||||
return;
|
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.
|
// The registered service worker schemes.
|
||||||
if (!g_custom_service_worker_schemes.empty())
|
if (!g_custom_service_worker_schemes.empty())
|
||||||
command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes,
|
command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes,
|
||||||
|
|
|
@ -31,10 +31,13 @@ void HandleExternalProtocolInUI(
|
||||||
if (!web_contents)
|
if (!web_contents)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
|
|
||||||
auto callback = base::Bind(&OnOpenExternal, escaped_url);
|
|
||||||
auto permission_helper =
|
auto permission_helper =
|
||||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
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);
|
permission_helper->RequestOpenExternalPermission(callback, has_user_gesture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,8 +151,12 @@ void Browser::DidFinishLaunching() {
|
||||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
|
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Browser::RequestLogin(LoginHandler* login_handler) {
|
void Browser::RequestLogin(
|
||||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
|
LoginHandler* login_handler,
|
||||||
|
std::unique_ptr<base::DictionaryValue> request_details) {
|
||||||
|
FOR_EACH_OBSERVER(BrowserObserver,
|
||||||
|
observers_,
|
||||||
|
OnLogin(login_handler, *(request_details.get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Browser::NotifyAndShutdown() {
|
void Browser::NotifyAndShutdown() {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
class DictionaryValue;
|
||||||
class FilePath;
|
class FilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +166,8 @@ class Browser : public WindowListObserver {
|
||||||
void DidFinishLaunching();
|
void DidFinishLaunching();
|
||||||
|
|
||||||
// Request basic auth login.
|
// Request basic auth login.
|
||||||
void RequestLogin(LoginHandler* login_handler);
|
void RequestLogin(LoginHandler* login_handler,
|
||||||
|
std::unique_ptr<base::DictionaryValue> request_details);
|
||||||
|
|
||||||
void AddObserver(BrowserObserver* obs) {
|
void AddObserver(BrowserObserver* obs) {
|
||||||
observers_.AddObserver(obs);
|
observers_.AddObserver(obs);
|
||||||
|
|
|
@ -49,7 +49,8 @@ class BrowserObserver {
|
||||||
virtual void OnFinishLaunching() {}
|
virtual void OnFinishLaunching() {}
|
||||||
|
|
||||||
// The browser requests HTTP login.
|
// 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)
|
#if defined(OS_MACOSX)
|
||||||
// The browser wants to resume a user activity via handoff. (OS X only)
|
// The browser wants to resume a user activity via handoff. (OS X only)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "atom/browser/login_handler.h"
|
#include "atom/browser/login_handler.h"
|
||||||
|
|
||||||
#include "atom/browser/browser.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/browser_thread.h"
|
||||||
#include "content/public/browser/render_frame_host.h"
|
#include "content/public/browser/render_frame_host.h"
|
||||||
#include "content/public/browser/resource_dispatcher_host.h"
|
#include "content/public/browser/resource_dispatcher_host.h"
|
||||||
|
@ -37,11 +39,18 @@ LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
|
||||||
render_frame_id_(0) {
|
render_frame_id_(0) {
|
||||||
content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
|
content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
|
||||||
&render_process_host_id_, &render_frame_id_);
|
&render_process_host_id_, &render_frame_id_);
|
||||||
|
|
||||||
|
// Fill request details on IO thread.
|
||||||
|
std::unique_ptr<base::DictionaryValue> request_details(
|
||||||
|
new base::DictionaryValue);
|
||||||
|
FillRequestDetails(request_details.get(), request_);
|
||||||
|
|
||||||
BrowserThread::PostTask(
|
BrowserThread::PostTask(
|
||||||
BrowserThread::UI, FROM_HERE,
|
BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&Browser::RequestLogin,
|
base::Bind(&Browser::RequestLogin,
|
||||||
base::Unretained(Browser::Get()),
|
base::Unretained(Browser::Get()),
|
||||||
base::RetainedRef(make_scoped_refptr(this))));
|
base::RetainedRef(make_scoped_refptr(this)),
|
||||||
|
base::Passed(&request_details)));
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginHandler::~LoginHandler() {
|
LoginHandler::~LoginHandler() {
|
||||||
|
|
|
@ -36,7 +36,6 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate {
|
||||||
void Login(const base::string16& username, const base::string16& password);
|
void Login(const base::string16& username, const base::string16& password);
|
||||||
|
|
||||||
const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
|
const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
|
||||||
const net::URLRequest* request() const { return request_; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~LoginHandler() override;
|
~LoginHandler() override;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "atom/browser/atom_browser_context.h"
|
#include "atom/browser/atom_browser_context.h"
|
||||||
#include "atom/browser/atom_browser_main_parts.h"
|
#include "atom/browser/atom_browser_main_parts.h"
|
||||||
|
#include "atom/browser/browser.h"
|
||||||
#include "atom/browser/window_list.h"
|
#include "atom/browser/window_list.h"
|
||||||
#include "atom/common/api/api_messages.h"
|
#include "atom/common/api/api_messages.h"
|
||||||
#include "atom/common/native_mate_converters/file_path_converter.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.
|
// For normal window, use white as default background.
|
||||||
SetBackgroundColor("#FFFF");
|
SetBackgroundColor("#FFFF");
|
||||||
}
|
}
|
||||||
std::string title("Electron");
|
std::string title(Browser::Get()->GetName());
|
||||||
options.Get(options::kTitle, &title);
|
options.Get(options::kTitle, &title);
|
||||||
SetTitle(title);
|
SetTitle(title);
|
||||||
|
|
||||||
|
@ -281,9 +282,6 @@ bool NativeWindow::IsDocumentEdited() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindow::SetIgnoreMouseEvents(bool ignore) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindow::SetMenu(ui::MenuModel* menu) {
|
void NativeWindow::SetMenu(ui::MenuModel* menu) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,10 +335,6 @@ void NativeWindow::CapturePage(const gfx::Rect& rect,
|
||||||
kBGRA_8888_SkColorType);
|
kBGRA_8888_SkColorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindow::ShowDefinitionForSelection() {
|
|
||||||
NOTIMPLEMENTED();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindow::SetAutoHideMenuBar(bool auto_hide) {
|
void NativeWindow::SetAutoHideMenuBar(bool auto_hide) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
virtual std::string GetRepresentedFilename();
|
virtual std::string GetRepresentedFilename();
|
||||||
virtual void SetDocumentEdited(bool edited);
|
virtual void SetDocumentEdited(bool edited);
|
||||||
virtual bool IsDocumentEdited();
|
virtual bool IsDocumentEdited();
|
||||||
virtual void SetIgnoreMouseEvents(bool ignore);
|
virtual void SetIgnoreMouseEvents(bool ignore) = 0;
|
||||||
virtual void SetMenu(ui::MenuModel* menu);
|
virtual void SetMenu(ui::MenuModel* menu);
|
||||||
virtual bool HasModalDialog();
|
virtual bool HasModalDialog();
|
||||||
virtual gfx::NativeWindow GetNativeWindow() = 0;
|
virtual gfx::NativeWindow GetNativeWindow() = 0;
|
||||||
|
@ -179,9 +179,6 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
virtual void CapturePage(const gfx::Rect& rect,
|
virtual void CapturePage(const gfx::Rect& rect,
|
||||||
const CapturePageCallback& callback);
|
const CapturePageCallback& callback);
|
||||||
|
|
||||||
// Show popup dictionary.
|
|
||||||
virtual void ShowDefinitionForSelection();
|
|
||||||
|
|
||||||
// Toggle the menu bar.
|
// Toggle the menu bar.
|
||||||
virtual void SetAutoHideMenuBar(bool auto_hide);
|
virtual void SetAutoHideMenuBar(bool auto_hide);
|
||||||
virtual bool IsMenuBarAutoHide();
|
virtual bool IsMenuBarAutoHide();
|
||||||
|
|
|
@ -83,7 +83,6 @@ class NativeWindowMac : public NativeWindow {
|
||||||
void SetProgressBar(double progress) override;
|
void SetProgressBar(double progress) override;
|
||||||
void SetOverlayIcon(const gfx::Image& overlay,
|
void SetOverlayIcon(const gfx::Image& overlay,
|
||||||
const std::string& description) override;
|
const std::string& description) override;
|
||||||
void ShowDefinitionForSelection() override;
|
|
||||||
|
|
||||||
void SetVisibleOnAllWorkspaces(bool visible) override;
|
void SetVisibleOnAllWorkspaces(bool visible) override;
|
||||||
bool IsVisibleOnAllWorkspaces() override;
|
bool IsVisibleOnAllWorkspaces() override;
|
||||||
|
@ -139,6 +138,9 @@ class NativeWindowMac : public NativeWindow {
|
||||||
// The presentation options before entering kiosk mode.
|
// The presentation options before entering kiosk mode.
|
||||||
NSApplicationPresentationOptions kiosk_options_;
|
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.
|
// Force showing the buttons for frameless window.
|
||||||
bool force_show_buttons_;
|
bool force_show_buttons_;
|
||||||
|
|
||||||
|
|
|
@ -193,10 +193,16 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||||
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
||||||
shell_->NotifyWindowEnterFullScreen();
|
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
|
// Restore the native toolbar immediately after entering fullscreen, if we do
|
||||||
// this before leaving fullscreen, traffic light buttons will be jumping.
|
// this before leaving fullscreen, traffic light buttons will be jumping.
|
||||||
if (shell_->should_hide_native_toolbar_in_fullscreen()) {
|
if (shell_->should_hide_native_toolbar_in_fullscreen()) {
|
||||||
NSWindow* window = shell_->GetNativeWindow();
|
|
||||||
base::scoped_nsobject<NSToolbar> toolbar(
|
base::scoped_nsobject<NSToolbar> toolbar(
|
||||||
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
||||||
[toolbar setShowsBaselineSeparator:NO];
|
[toolbar setShowsBaselineSeparator:NO];
|
||||||
|
@ -209,6 +215,11 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
- (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.
|
// Turn off the style for toolbar.
|
||||||
if (shell_->should_hide_native_toolbar_in_fullscreen())
|
if (shell_->should_hide_native_toolbar_in_fullscreen())
|
||||||
shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
|
shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
|
||||||
|
@ -513,10 +524,6 @@ NativeWindowMac::NativeWindowMac(
|
||||||
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
||||||
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
||||||
|
|
||||||
// Disable zoom button if window is not resizable.
|
|
||||||
if (!maximizable)
|
|
||||||
SetMaximizable(false);
|
|
||||||
|
|
||||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||||
|
|
||||||
|
@ -544,6 +551,12 @@ NativeWindowMac::NativeWindowMac(
|
||||||
}];
|
}];
|
||||||
|
|
||||||
InstallView();
|
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() {
|
NativeWindowMac::~NativeWindowMac() {
|
||||||
|
@ -781,6 +794,8 @@ void NativeWindowMac::Center() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowMac::SetTitle(const std::string& title) {
|
void NativeWindowMac::SetTitle(const std::string& title) {
|
||||||
|
title_ = title;
|
||||||
|
|
||||||
// We don't want the title to show in transparent or frameless window.
|
// We don't want the title to show in transparent or frameless window.
|
||||||
if (transparent() || !has_frame())
|
if (transparent() || !has_frame())
|
||||||
return;
|
return;
|
||||||
|
@ -789,7 +804,7 @@ void NativeWindowMac::SetTitle(const std::string& title) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NativeWindowMac::GetTitle() {
|
std::string NativeWindowMac::GetTitle() {
|
||||||
return base::SysNSStringToUTF8([window_ title]);
|
return title_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowMac::FlashFrame(bool flash) {
|
void NativeWindowMac::FlashFrame(bool flash) {
|
||||||
|
@ -920,15 +935,6 @@ void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay,
|
||||||
const std::string& description) {
|
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) {
|
void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible) {
|
||||||
SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
|
SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,21 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
|
||||||
(modifiers == (Modifiers::AltKey | Modifiers::IsRight));
|
(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 {
|
class NativeWindowClientView : public views::ClientView {
|
||||||
public:
|
public:
|
||||||
NativeWindowClientView(views::Widget* widget,
|
NativeWindowClientView(views::Widget* widget,
|
||||||
|
@ -298,10 +313,19 @@ void NativeWindowViews::CloseImmediately() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowViews::Focus(bool focus) {
|
void NativeWindowViews::Focus(bool focus) {
|
||||||
if (focus)
|
if (focus) {
|
||||||
|
#if defined(OS_WIN)
|
||||||
window_->Activate();
|
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();
|
window_->Deactivate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeWindowViews::IsFocused() {
|
bool NativeWindowViews::IsFocused() {
|
||||||
|
@ -648,6 +672,26 @@ bool NativeWindowViews::HasShadow() {
|
||||||
return wm::GetShadowType(GetNativeWindow()) != wm::SHADOW_TYPE_NONE;
|
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) {
|
void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
|
||||||
if (menu_model == nullptr) {
|
if (menu_model == nullptr) {
|
||||||
// Remove accelerators
|
// Remove accelerators
|
||||||
|
|
|
@ -91,6 +91,7 @@ class NativeWindowViews : public NativeWindow,
|
||||||
void SetBackgroundColor(const std::string& color_name) override;
|
void SetBackgroundColor(const std::string& color_name) override;
|
||||||
void SetHasShadow(bool has_shadow) override;
|
void SetHasShadow(bool has_shadow) override;
|
||||||
bool HasShadow() override;
|
bool HasShadow() override;
|
||||||
|
void SetIgnoreMouseEvents(bool ignore) override;
|
||||||
void SetMenu(ui::MenuModel* menu_model) override;
|
void SetMenu(ui::MenuModel* menu_model) override;
|
||||||
gfx::NativeWindow GetNativeWindow() override;
|
gfx::NativeWindow GetNativeWindow() override;
|
||||||
void SetOverlayIcon(const gfx::Image& overlay,
|
void SetOverlayIcon(const gfx::Image& overlay,
|
||||||
|
|
|
@ -71,18 +71,13 @@ bool MatchesFilterCondition(net::URLRequest* request,
|
||||||
|
|
||||||
// Overloaded by multiple types to fill the |details| object.
|
// Overloaded by multiple types to fill the |details| object.
|
||||||
void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
|
void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
|
||||||
|
FillRequestDetails(details, request);
|
||||||
details->SetInteger("id", request->identifier());
|
details->SetInteger("id", request->identifier());
|
||||||
details->SetString("url", request->url().spec());
|
|
||||||
details->SetString("method", request->method());
|
|
||||||
details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000);
|
details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000);
|
||||||
auto info = content::ResourceRequestInfo::ForRequest(request);
|
auto info = content::ResourceRequestInfo::ForRequest(request);
|
||||||
details->SetString("resourceType",
|
details->SetString("resourceType",
|
||||||
info ? ResourceTypeToString(info->GetResourceType())
|
info ? ResourceTypeToString(info->GetResourceType())
|
||||||
: "other");
|
: "other");
|
||||||
std::unique_ptr<base::ListValue> list(new base::ListValue);
|
|
||||||
GetUploadData(list.get(), request);
|
|
||||||
if (!list->empty())
|
|
||||||
details->Set("uploadData", std::move(list));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToDictionary(base::DictionaryValue* details,
|
void ToDictionary(base::DictionaryValue* details,
|
||||||
|
|
|
@ -44,7 +44,7 @@ void HandlerCallback(const BeforeStartCallback& before_start,
|
||||||
|
|
||||||
void AskForOptions(v8::Isolate* isolate,
|
void AskForOptions(v8::Isolate* isolate,
|
||||||
const JavaScriptHandler& handler,
|
const JavaScriptHandler& handler,
|
||||||
net::URLRequest* request,
|
std::unique_ptr<base::DictionaryValue> request_details,
|
||||||
const BeforeStartCallback& before_start,
|
const BeforeStartCallback& before_start,
|
||||||
const ResponseCallback& callback) {
|
const ResponseCallback& callback) {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
@ -53,7 +53,7 @@ void AskForOptions(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||||
v8::Context::Scope context_scope(context);
|
v8::Context::Scope context_scope(context);
|
||||||
handler.Run(
|
handler.Run(
|
||||||
request,
|
*(request_details.get()),
|
||||||
mate::ConvertToV8(isolate,
|
mate::ConvertToV8(isolate,
|
||||||
base::Bind(&HandlerCallback, before_start, callback)));
|
base::Bind(&HandlerCallback, before_start, callback)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#ifndef ATOM_BROWSER_NET_JS_ASKER_H_
|
#ifndef ATOM_BROWSER_NET_JS_ASKER_H_
|
||||||
#define 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/callback.h"
|
||||||
#include "base/memory/ref_counted.h"
|
#include "base/memory/ref_counted.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
using JavaScriptHandler =
|
using JavaScriptHandler =
|
||||||
base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
|
base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ using ResponseCallback =
|
||||||
// Ask handler for options in UI thread.
|
// Ask handler for options in UI thread.
|
||||||
void AskForOptions(v8::Isolate* isolate,
|
void AskForOptions(v8::Isolate* isolate,
|
||||||
const JavaScriptHandler& handler,
|
const JavaScriptHandler& handler,
|
||||||
net::URLRequest* request,
|
std::unique_ptr<base::DictionaryValue> request_details,
|
||||||
const BeforeStartCallback& before_start,
|
const BeforeStartCallback& before_start,
|
||||||
const ResponseCallback& callback);
|
const ResponseCallback& callback);
|
||||||
|
|
||||||
|
@ -67,12 +68,15 @@ class JsAsker : public RequestJob {
|
||||||
private:
|
private:
|
||||||
// RequestJob:
|
// RequestJob:
|
||||||
void Start() override {
|
void Start() override {
|
||||||
|
std::unique_ptr<base::DictionaryValue> request_details(
|
||||||
|
new base::DictionaryValue);
|
||||||
|
FillRequestDetails(request_details.get(), RequestJob::request());
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
content::BrowserThread::UI, FROM_HERE,
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&internal::AskForOptions,
|
base::Bind(&internal::AskForOptions,
|
||||||
isolate_,
|
isolate_,
|
||||||
handler_,
|
handler_,
|
||||||
RequestJob::request(),
|
base::Passed(&request_details),
|
||||||
base::Bind(&JsAsker::BeforeStartInUI,
|
base::Bind(&JsAsker::BeforeStartInUI,
|
||||||
weak_factory_.GetWeakPtr()),
|
weak_factory_.GetWeakPtr()),
|
||||||
base::Bind(&JsAsker::OnResponse,
|
base::Bind(&JsAsker::OnResponse,
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>electron.icns</string>
|
<string>electron.icns</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.2.1</string>
|
<string>1.2.2</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.2.1</string>
|
<string>1.2.2</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.developer-tools</string>
|
<string>public.app-category.developer-tools</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|
|
@ -56,8 +56,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,2,1,0
|
FILEVERSION 1,2,2,0
|
||||||
PRODUCTVERSION 1,2,1,0
|
PRODUCTVERSION 1,2,2,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -74,12 +74,12 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "GitHub, Inc."
|
VALUE "CompanyName", "GitHub, Inc."
|
||||||
VALUE "FileDescription", "Electron"
|
VALUE "FileDescription", "Electron"
|
||||||
VALUE "FileVersion", "1.2.1"
|
VALUE "FileVersion", "1.2.2"
|
||||||
VALUE "InternalName", "electron.exe"
|
VALUE "InternalName", "electron.exe"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||||
VALUE "OriginalFilename", "electron.exe"
|
VALUE "OriginalFilename", "electron.exe"
|
||||||
VALUE "ProductName", "Electron"
|
VALUE "ProductName", "Electron"
|
||||||
VALUE "ProductVersion", "1.2.1"
|
VALUE "ProductVersion", "1.2.2"
|
||||||
VALUE "SquirrelAwareVersion", "1"
|
VALUE "SquirrelAwareVersion", "1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
|
@ -33,7 +33,7 @@ Role kRolesMap[] = {
|
||||||
{ @selector(copy:), "copy" },
|
{ @selector(copy:), "copy" },
|
||||||
{ @selector(paste:), "paste" },
|
{ @selector(paste:), "paste" },
|
||||||
{ @selector(delete:), "delete" },
|
{ @selector(delete:), "delete" },
|
||||||
{ @selector(pasteAndMatchStyle:), "paste-and-match-style" },
|
{ @selector(pasteAndMatchStyle:), "pasteandmatchstyle" },
|
||||||
{ @selector(selectAll:), "selectall" },
|
{ @selector(selectAll:), "selectall" },
|
||||||
{ @selector(performMiniaturize:), "minimize" },
|
{ @selector(performMiniaturize:), "minimize" },
|
||||||
{ @selector(performClose:), "close" },
|
{ @selector(performClose:), "close" },
|
||||||
|
|
|
@ -54,7 +54,7 @@ TaskbarHost::~TaskbarHost() {
|
||||||
|
|
||||||
bool TaskbarHost::SetThumbarButtons(
|
bool TaskbarHost::SetThumbarButtons(
|
||||||
HWND window, const std::vector<ThumbarButton>& buttons) {
|
HWND window, const std::vector<ThumbarButton>& buttons) {
|
||||||
if (buttons.size() > kMaxButtonsCount || !InitailizeTaskbar())
|
if (buttons.size() > kMaxButtonsCount || !InitializeTaskbar())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
callback_map_.clear();
|
callback_map_.clear();
|
||||||
|
@ -118,7 +118,7 @@ bool TaskbarHost::SetThumbarButtons(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
||||||
if (!InitailizeTaskbar())
|
if (!InitializeTaskbar())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
HRESULT r;
|
HRESULT r;
|
||||||
|
@ -133,7 +133,7 @@ bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
||||||
|
|
||||||
bool TaskbarHost::SetOverlayIcon(
|
bool TaskbarHost::SetOverlayIcon(
|
||||||
HWND window, const gfx::Image& overlay, const std::string& text) {
|
HWND window, const gfx::Image& overlay, const std::string& text) {
|
||||||
if (!InitailizeTaskbar())
|
if (!InitializeTaskbar())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
base::win::ScopedHICON icon(
|
base::win::ScopedHICON icon(
|
||||||
|
@ -152,7 +152,7 @@ bool TaskbarHost::HandleThumbarButtonEvent(int button_id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TaskbarHost::InitailizeTaskbar() {
|
bool TaskbarHost::InitializeTaskbar() {
|
||||||
if (FAILED(taskbar_.CreateInstance(CLSID_TaskbarList,
|
if (FAILED(taskbar_.CreateInstance(CLSID_TaskbarList,
|
||||||
nullptr,
|
nullptr,
|
||||||
CLSCTX_INPROC_SERVER)) ||
|
CLSCTX_INPROC_SERVER)) ||
|
||||||
|
|
|
@ -44,8 +44,8 @@ class TaskbarHost {
|
||||||
bool HandleThumbarButtonEvent(int button_id);
|
bool HandleThumbarButtonEvent(int button_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initailize the taskbar object.
|
// Initialize the taskbar object.
|
||||||
bool InitailizeTaskbar();
|
bool InitializeTaskbar();
|
||||||
|
|
||||||
using CallbackMap = std::map<int, base::Closure>;
|
using CallbackMap = std::map<int, base::Closure>;
|
||||||
CallbackMap callback_map_;
|
CallbackMap callback_map_;
|
||||||
|
|
|
@ -166,6 +166,13 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
|
||||||
command_line->AppendSwitchASCII(::switches::kEnableBlinkFeatures,
|
command_line->AppendSwitchASCII(::switches::kEnableBlinkFeatures,
|
||||||
blink_features);
|
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.
|
// The initial visibility state.
|
||||||
NativeWindow* window = NativeWindow::FromWebContents(web_contents);
|
NativeWindow* window = NativeWindow::FromWebContents(web_contents);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#define ATOM_MAJOR_VERSION 1
|
#define ATOM_MAJOR_VERSION 1
|
||||||
#define ATOM_MINOR_VERSION 2
|
#define ATOM_MINOR_VERSION 2
|
||||||
#define ATOM_PATCH_VERSION 1
|
#define ATOM_PATCH_VERSION 2
|
||||||
|
|
||||||
#define ATOM_VERSION_IS_RELEASE 1
|
#define ATOM_VERSION_IS_RELEASE 1
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#ifndef ATOM_COMMON_CHROME_VERSION_H_
|
#ifndef ATOM_COMMON_CHROME_VERSION_H_
|
||||||
#define 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
|
#define CHROME_VERSION "v" CHROME_VERSION_STRING
|
||||||
|
|
||||||
#endif // ATOM_COMMON_CHROME_VERSION_H_
|
#endif // ATOM_COMMON_CHROME_VERSION_H_
|
||||||
|
|
|
@ -22,22 +22,6 @@
|
||||||
|
|
||||||
namespace mate {
|
namespace mate {
|
||||||
|
|
||||||
// static
|
|
||||||
v8::Local<v8::Value> Converter<const net::URLRequest*>::ToV8(
|
|
||||||
v8::Isolate* isolate, const net::URLRequest* val) {
|
|
||||||
std::unique_ptr<base::DictionaryValue> 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<base::ListValue> 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
|
// static
|
||||||
v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
|
v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
|
||||||
v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
|
v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
|
||||||
|
@ -69,6 +53,19 @@ v8::Local<v8::Value> Converter<scoped_refptr<net::X509Certificate>>::ToV8(
|
||||||
|
|
||||||
namespace atom {
|
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<base::ListValue> list(new base::ListValue);
|
||||||
|
GetUploadData(list.get(), request);
|
||||||
|
if (!list->empty())
|
||||||
|
details->Set("uploadData", std::move(list));
|
||||||
|
}
|
||||||
|
|
||||||
void GetUploadData(base::ListValue* upload_data_list,
|
void GetUploadData(base::ListValue* upload_data_list,
|
||||||
const net::URLRequest* request) {
|
const net::URLRequest* request) {
|
||||||
const net::UploadDataStream* upload_data = request->get_upload();
|
const net::UploadDataStream* upload_data = request->get_upload();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "native_mate/converter.h"
|
#include "native_mate/converter.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
class DictionaryValue;
|
||||||
class ListValue;
|
class ListValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +21,6 @@ class X509Certificate;
|
||||||
|
|
||||||
namespace mate {
|
namespace mate {
|
||||||
|
|
||||||
template<>
|
|
||||||
struct Converter<const net::URLRequest*> {
|
|
||||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
|
||||||
const net::URLRequest* val);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct Converter<const net::AuthChallengeInfo*> {
|
struct Converter<const net::AuthChallengeInfo*> {
|
||||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
|
@ -42,6 +37,9 @@ struct Converter<scoped_refptr<net::X509Certificate>> {
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
void FillRequestDetails(base::DictionaryValue* details,
|
||||||
|
const net::URLRequest* request);
|
||||||
|
|
||||||
void GetUploadData(base::ListValue* upload_data_list,
|
void GetUploadData(base::ListValue* upload_data_list,
|
||||||
const net::URLRequest* request);
|
const net::URLRequest* request);
|
||||||
|
|
||||||
|
|
|
@ -107,8 +107,12 @@ const char kOpenerID[] = "openerId";
|
||||||
const char kScrollBounce[] = "scrollBounce";
|
const char kScrollBounce[] = "scrollBounce";
|
||||||
|
|
||||||
// Enable blink features.
|
// Enable blink features.
|
||||||
|
// TODO(kevinsawicki) Rename to enableBlinkFeatures in 2.0
|
||||||
const char kBlinkFeatures[] = "blinkFeatures";
|
const char kBlinkFeatures[] = "blinkFeatures";
|
||||||
|
|
||||||
|
// Disable blink features.
|
||||||
|
const char kDisableBlinkFeatures[] = "disableBlinkFeatures";
|
||||||
|
|
||||||
} // namespace options
|
} // namespace options
|
||||||
|
|
||||||
namespace switches {
|
namespace switches {
|
||||||
|
@ -125,6 +129,9 @@ const char kPpapiFlashVersion[] = "ppapi-flash-version";
|
||||||
// Disable HTTP cache.
|
// Disable HTTP cache.
|
||||||
const char kDisableHttpCache[] = "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.
|
// Register schemes to handle service worker.
|
||||||
const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes";
|
const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes";
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ extern const char kExperimentalCanvasFeatures[];
|
||||||
extern const char kOpenerID[];
|
extern const char kOpenerID[];
|
||||||
extern const char kScrollBounce[];
|
extern const char kScrollBounce[];
|
||||||
extern const char kBlinkFeatures[];
|
extern const char kBlinkFeatures[];
|
||||||
|
extern const char kDisableBlinkFeatures[];
|
||||||
|
|
||||||
} // namespace options
|
} // namespace options
|
||||||
|
|
||||||
|
@ -70,6 +71,7 @@ extern const char kEnablePlugins[];
|
||||||
extern const char kPpapiFlashPath[];
|
extern const char kPpapiFlashPath[];
|
||||||
extern const char kPpapiFlashVersion[];
|
extern const char kPpapiFlashVersion[];
|
||||||
extern const char kDisableHttpCache[];
|
extern const char kDisableHttpCache[];
|
||||||
|
extern const char kStandardSchemes[];
|
||||||
extern const char kRegisterServiceWorkerSchemes[];
|
extern const char kRegisterServiceWorkerSchemes[];
|
||||||
extern const char kSSLVersionFallbackMin[];
|
extern const char kSSLVersionFallbackMin[];
|
||||||
extern const char kCipherSuiteBlacklist[];
|
extern const char kCipherSuiteBlacklist[];
|
||||||
|
|
|
@ -122,6 +122,16 @@ bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||||
AtomRendererClient::AtomRendererClient()
|
AtomRendererClient::AtomRendererClient()
|
||||||
: node_bindings_(NodeBindings::Create(false)),
|
: node_bindings_(NodeBindings::Create(false)),
|
||||||
atom_bindings_(new AtomBindings) {
|
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<std::string> 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() {
|
AtomRendererClient::~AtomRendererClient() {
|
||||||
|
|
|
@ -8,11 +8,14 @@ const url = require('url')
|
||||||
|
|
||||||
// Parse command line options.
|
// Parse command line options.
|
||||||
const argv = process.argv.slice(1)
|
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++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
if (argv[i] === '--version' || argv[i] === '-v') {
|
if (argv[i] === '--version' || argv[i] === '-v') {
|
||||||
option.version = true
|
option.version = true
|
||||||
break
|
break
|
||||||
|
} else if (argv[i] === '--abi') {
|
||||||
|
option.abi = true
|
||||||
|
break
|
||||||
} else if (argv[i].match(/^--app=/)) {
|
} else if (argv[i].match(/^--app=/)) {
|
||||||
option.file = argv[i].split('=')[1]
|
option.file = argv[i].split('=')[1]
|
||||||
break
|
break
|
||||||
|
@ -201,7 +204,7 @@ app.once('ready', () => {
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Quit',
|
label: 'Quit ' + app.getName(),
|
||||||
accelerator: 'Command+Q',
|
accelerator: 'Command+Q',
|
||||||
click () { app.quit() }
|
click () { app.quit() }
|
||||||
}
|
}
|
||||||
|
@ -304,6 +307,9 @@ if (option.file && !option.webdriver) {
|
||||||
} else if (option.version) {
|
} else if (option.version) {
|
||||||
console.log('v' + process.versions.electron)
|
console.log('v' + process.versions.electron)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
|
} else if (option.abi) {
|
||||||
|
console.log(process.versions.modules)
|
||||||
|
process.exit(0)
|
||||||
} else if (option.help) {
|
} else if (option.help) {
|
||||||
const helpMessage = `Electron ${process.versions.electron} - Build cross platform desktop apps with JavaScript, HTML, and CSS
|
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.
|
-h, --help Print this usage message.
|
||||||
-i, --interactive Open a REPL to the main process.
|
-i, --interactive Open a REPL to the main process.
|
||||||
-r, --require Module to preload (option can be repeated)
|
-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)
|
console.log(helpMessage)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
} else if (option.interactive) {
|
} else if (option.interactive) {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
`ipcRenderer`는 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다:
|
`ipcRenderer`는 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다:
|
||||||
|
|
||||||
### `ipcMain.on(channel, listener)`
|
### `ipcRenderer.on(channel, listener)`
|
||||||
|
|
||||||
* `channel` String
|
* `channel` String
|
||||||
* `listener` Function
|
* `listener` Function
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
`channel`에 대해 이벤트를 리스닝합니다. 새로운 메시지가 도착하면 `listener`가
|
`channel`에 대해 이벤트를 리스닝합니다. 새로운 메시지가 도착하면 `listener`가
|
||||||
`listener(event, args...)` 형식으로 호출됩니다.
|
`listener(event, args...)` 형식으로 호출됩니다.
|
||||||
|
|
||||||
### `ipcMain.once(channel, listener)`
|
### `ipcRenderer.once(channel, listener)`
|
||||||
|
|
||||||
* `channel` String
|
* `channel` String
|
||||||
* `listener` Function
|
* `listener` Function
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
이벤트에 대해 한 번만 작동하는 `listener` 함수를 등록합니다. 이 `listener`는 등록된
|
이벤트에 대해 한 번만 작동하는 `listener` 함수를 등록합니다. 이 `listener`는 등록된
|
||||||
후 `channel`에 보내지는 메시지에 한해 호출됩니다. 호출된 이후엔 리스너가 삭제됩니다.
|
후 `channel`에 보내지는 메시지에 한해 호출됩니다. 호출된 이후엔 리스너가 삭제됩니다.
|
||||||
|
|
||||||
### `ipcMain.removeListener(channel, listener)`
|
### `ipcRenderer.removeListener(channel, listener)`
|
||||||
|
|
||||||
* `channel` String
|
* `channel` String
|
||||||
* `listener` Function
|
* `listener` Function
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
지정한 `channel`에 대한 리스너를 저장하는 배열에서 지정한 `listener`를 삭제합니다.
|
지정한 `channel`에 대한 리스너를 저장하는 배열에서 지정한 `listener`를 삭제합니다.
|
||||||
|
|
||||||
### `ipcMain.removeAllListeners(channel)`
|
### `ipcRenderer.removeAllListeners(channel)`
|
||||||
|
|
||||||
* `channel` String (optional)
|
* `channel` String (optional)
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ exports.withLocalCallback = () => {
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 렌더러 프로세스
|
// 렌더러 프로세스
|
||||||
const mapNumbers = require('remote').require('./mapNumbers');
|
const mapNumbers = require('electron').remote.require('./mapNumbers');
|
||||||
|
|
||||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,15 @@ Returns:
|
||||||
<meta name='theme-color' content='#ff0000'>
|
<meta name='theme-color' content='#ff0000'>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Event: 'update-target-url'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
* `event` Event
|
||||||
|
* `url` URL
|
||||||
|
|
||||||
|
마우스나 키보드를 사용해 링크에 포커스를 할 때 발생하는 이벤트입니다.
|
||||||
|
|
||||||
### Event: 'cursor-changed'
|
### Event: 'cursor-changed'
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -591,6 +600,12 @@ CSS 코드를 현재 웹 페이지에 삽입합니다.
|
||||||
|
|
||||||
웹 페이지에서 `replaceMisspelling` 편집 커맨드를 실행합니다.
|
웹 페이지에서 `replaceMisspelling` 편집 커맨드를 실행합니다.
|
||||||
|
|
||||||
|
### `webContents.insertText(text)`
|
||||||
|
|
||||||
|
* `text` String
|
||||||
|
|
||||||
|
포커스된 요소에 `text`를 삽입합니다.
|
||||||
|
|
||||||
### `webContents.findInPage(text[, options])`
|
### `webContents.findInPage(text[, options])`
|
||||||
|
|
||||||
* `text` String - 찾을 콘텐츠, 반드시 공백이 아니여야 합니다.
|
* `text` String - 찾을 콘텐츠, 반드시 공백이 아니여야 합니다.
|
||||||
|
|
|
@ -117,25 +117,7 @@ MyApp.app/Contents
|
||||||
|
|
||||||
또한 Electron 소스 코드를 다시 빌드할 때 어플리케이션 이름을 변경할 수 있습니다.
|
또한 Electron 소스 코드를 다시 빌드할 때 어플리케이션 이름을 변경할 수 있습니다.
|
||||||
|
|
||||||
`GYP_DEFINES` 환경변수를 사용하여 다음과 같이 다시 빌드할 수 있습니다:
|
`atom.gyp` 파일을 수정하여 다음과 같이 다시 빌드할 수 있습니다:
|
||||||
|
|
||||||
__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
|
|
||||||
```
|
|
||||||
|
|
||||||
### grunt-build-atom-shell
|
### grunt-build-atom-shell
|
||||||
|
|
||||||
|
@ -175,16 +157,16 @@ Electron의 개발자로써, Electron을 매우 많은 시나리오에서도 작
|
||||||
|
|
||||||
3. 다음의 환경 변수들을 설정합니다:
|
3. 다음의 환경 변수들을 설정합니다:
|
||||||
|
|
||||||
* `ELECTRON_GITHUB_TOKEN` - GitHub에 릴리즈를 만들 수 있는 토큰.
|
* `ELECTRON_GITHUB_TOKEN` - GitHub에 릴리즈를 만들 수 있는 토큰.
|
||||||
* `ELECTRON_S3_ACCESS_KEY`, `ELECTRON_S3_BUCKET`, `ELECTRON_S3_SECRET_KEY` -
|
* `ELECTRON_S3_ACCESS_KEY`, `ELECTRON_S3_BUCKET`, `ELECTRON_S3_SECRET_KEY` -
|
||||||
node.js 헤더 뿐만 아니라 심볼을 업로드할 장소.
|
node.js 헤더 뿐만 아니라 심볼을 업로드할 장소.
|
||||||
* `ELECTRON_RELEASE` - `true`로 지정하고 업로드 부분이 실행되면, 설정되지 않은
|
* `ELECTRON_RELEASE` - `true`로 지정하고 업로드 부분이 실행되면, 설정되지 않은
|
||||||
부분을 남기고 `surf-build`가 CI-type 확인을 실행합니다. 모든 pull request를
|
부분을 남기고 `surf-build`가 CI-type 확인을 실행합니다. 모든 pull request를
|
||||||
실행할 때 적합합니다.
|
실행할 때 적합합니다.
|
||||||
* `CI` - `true` 또는 다른 것을 지정하면 실패합니다.
|
* `CI` - `true` 또는 다른 것을 지정하면 실패합니다.
|
||||||
* `GITHUB_TOKEN` - `ELECTRON_GITHUB_TOKEN`와 같게 설정
|
* `GITHUB_TOKEN` - `ELECTRON_GITHUB_TOKEN`와 같게 설정
|
||||||
* `SURF_TEMP` - Windows에선 `C:\Temp`로 설정하면 긴 경로 문제를 해결할 수 있습니다.
|
* `SURF_TEMP` - Windows에선 `C:\Temp`로 설정하면 긴 경로 문제를 해결할 수 있습니다.
|
||||||
* `TARGET_ARCH` - `ia32` 또는 `x64`를 지정.
|
* `TARGET_ARCH` - `ia32` 또는 `x64`를 지정.
|
||||||
|
|
||||||
4. Electron에 기여를 하는 기여자라면, _반드시_ `script/upload.py`에서 포크를 위해
|
4. Electron에 기여를 하는 기여자라면, _반드시_ `script/upload.py`에서 포크를 위해
|
||||||
`ELECTRON_REPO`를 설정해야 합니다. (`MYORG/electron`)
|
`ELECTRON_REPO`를 설정해야 합니다. (`MYORG/electron`)
|
||||||
|
|
|
@ -120,6 +120,21 @@ productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RES
|
||||||
문서를 참고하여 기본적인 개념을 이해해야 합니다. 그리고 자격(plist) 파일에
|
문서를 참고하여 기본적인 개념을 이해해야 합니다. 그리고 자격(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를
|
어플리케이션 서명을 완료한 후 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
|
[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
|
[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
|
[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
|
[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/
|
[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/
|
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||||
|
|
|
@ -41,7 +41,7 @@ API를 사용하여 low-level 수준으로 운영체제와 상호작용할 수
|
||||||
프로세스에서 그 작업을 처리할 수 있도록 메인 프로세스와 통신을 해야 합니다.
|
프로세스에서 그 작업을 처리할 수 있도록 메인 프로세스와 통신을 해야 합니다.
|
||||||
|
|
||||||
Electron에는 메인 프로세스와 렌더러 프로세스 사이에 통신을 할 수 있도록
|
Electron에는 메인 프로세스와 렌더러 프로세스 사이에 통신을 할 수 있도록
|
||||||
[ipc](../api/ipc-renderer.md) 모듈을 제공하고 있습니다.
|
[ipcRenderer](../api/ipc-renderer.md)와 [ipcMain](../api/ipc-main.md) 모듈을 제공하고 있습니다.
|
||||||
또는 [remote](../api/remote.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다.
|
또는 [remote](../api/remote.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다.
|
||||||
또한 FAQ에서 [다양한 객체를 공유하는 방법](share-data)도 소개하고 있습니다.
|
또한 FAQ에서 [다양한 객체를 공유하는 방법](share-data)도 소개하고 있습니다.
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
Пожалуйста, убедитесь, что вы используете документацию, которые соответствует вашей версии Electron.
|
Пожалуйста, убедитесь, что Вы используете документацию, которая соответствует вашей версии Electron.
|
||||||
Номер версии должен быть частью адреса страницы. Если это не так, вы
|
Номер версии должен быть частью адреса страницы. Если это не так, Вы
|
||||||
возможно, используете документацию ветки разработки, которая может содержать изменения api,
|
возможно, используете документацию ветки разработки, которая может содержать изменения api,
|
||||||
которые не совместимы с вашей версией Electron. Если это так,
|
которые не совместимы с вашей версией Electron. Если это так,
|
||||||
Вы можете переключиться на другую версию документации в списке
|
Вы можете переключиться на другую версию документации в списке
|
||||||
[доступные версии](http://electron.atom.io/docs/) на atom.io, или
|
[доступные версии](http://electron.atom.io/docs/) на [atom.io](atom.io), или
|
||||||
если вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и
|
если Вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и
|
||||||
выберите тег, который соответствует вашей версии.
|
выберите тег, который соответствует вашей версии.
|
||||||
|
|
||||||
## Руководства
|
## Руководства
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
* [Application Distribution](tutorial/application-distribution.md)
|
* [Application Distribution](tutorial/application-distribution.md)
|
||||||
* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md)
|
* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md)
|
||||||
* [Application Packaging](tutorial/application-packaging.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)
|
* [Отладка главного процесса](tutorial/debugging-main-process.md)
|
||||||
* [Использование Selenium и WebDriver](tutorial/using-selenium-and-webdriver.md)
|
* [Использование Selenium и WebDriver](tutorial/using-selenium-and-webdriver.md)
|
||||||
* [DevTools Extension](tutorial/devtools-extension.md)
|
* [DevTools Extension](tutorial/devtools-extension.md)
|
||||||
|
@ -22,8 +22,8 @@
|
||||||
## Учебники
|
## Учебники
|
||||||
|
|
||||||
* [Быстрый старт](tutorial/quick-start.md)
|
* [Быстрый старт](tutorial/quick-start.md)
|
||||||
* [Desktop Environment Integration](tutorial/desktop-environment-integration.md)
|
* [Интеграция рабочего окружения](tutorial/desktop-environment-integration.md)
|
||||||
* [Online/Offline Event Detection](tutorial/online-offline-events.md)
|
* [Определение Онлайн/Оффлайн состояния](tutorial/online-offline-events.md)
|
||||||
|
|
||||||
## API References
|
## API References
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
* [`<webview>` Tag](api/web-view-tag.md)
|
* [`<webview>` Tag](api/web-view-tag.md)
|
||||||
* [`window.open` Function](api/window-open.md)
|
* [`window.open` Function](api/window-open.md)
|
||||||
|
|
||||||
### Modules for the Main Process:
|
### Модули для Main Process:
|
||||||
|
|
||||||
* [app](api/app.md)
|
* [app](api/app.md)
|
||||||
* [autoUpdater](api/auto-updater.md)
|
* [autoUpdater](api/auto-updater.md)
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
* [remote](api/remote.md)
|
* [remote](api/remote.md)
|
||||||
* [webFrame](api/web-frame.md)
|
* [webFrame](api/web-frame.md)
|
||||||
|
|
||||||
### Modules for Both Processes:
|
### Модули для обоих процессов:
|
||||||
|
|
||||||
* [clipboard](api/clipboard.md)
|
* [clipboard](api/clipboard.md)
|
||||||
* [crashReporter](api/crash-reporter.md)
|
* [crashReporter](api/crash-reporter.md)
|
||||||
|
@ -72,11 +72,11 @@
|
||||||
## Разработка
|
## Разработка
|
||||||
|
|
||||||
* [Стиль кодирования](development/coding-style.md)
|
* [Стиль кодирования](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)
|
* [Technical Differences to NW.js (formerly node-webkit)](development/atom-shell-vs-node-webkit.md)
|
||||||
* [Обзор системы сборки](development/build-system-overview.md)
|
* [Обзор системы сборки](development/build-system-overview.md)
|
||||||
* [Инструкции по сборке (OS X)](development/build-instructions-osx.md)
|
* [Инструкции по сборке (OS X)](development/build-instructions-osx.md)
|
||||||
* [Инструкции по сборке (Windows)](development/build-instructions-windows.md)
|
* [Инструкции по сборке (Windows)](development/build-instructions-windows.md)
|
||||||
* [Инструкции по сборке (Linux)](development/build-instructions-linux.md)
|
* [Инструкции по сборке (Linux)](development/build-instructions-linux.md)
|
||||||
* [Настройка сервера символов для отладчика](development/setting-up-symbol-server.md)
|
* [Настройка сервера символов для отладчика](development/setting-up-symbol-server.md)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Распространение приложения
|
# Распространение приложения
|
||||||
|
|
||||||
Чтобы разпространять ваше приложение на Electron, папка с вашим приложением
|
Чтобы разпространять ваше приложение на Electron, папка с вашим приложением
|
||||||
должна называться `app` и находиться в папке ресурсов Electron (на OS X это
|
должна называться `app` и находиться в папке ресурсов Electron (в OS X это
|
||||||
`Electron.app/Contents/Resources/`, на Linux и Windows - `resources/`),
|
`Electron.app/Contents/Resources/`, в Linux и Windows - `resources/`),
|
||||||
вот так:
|
вот так:
|
||||||
|
|
||||||
На OS X:
|
Для OS X:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
electron/Electron.app/Contents/Resources/app/
|
electron/Electron.app/Contents/Resources/app/
|
||||||
|
@ -14,7 +14,7 @@ electron/Electron.app/Contents/Resources/app/
|
||||||
└── index.html
|
└── index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
На Windows и Linux:
|
Для Windows и Linux:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
electron/resources/app
|
electron/resources/app
|
||||||
|
@ -23,13 +23,13 @@ electron/resources/app
|
||||||
└── index.html
|
└── index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
Затем запустите `Electron.app` (или `electron` на Linux, `electron.exe` на Windows),
|
Затем запустите `Electron.app` (или `electron` в Linux, `electron.exe` в Windows),
|
||||||
и Electron запустится как ваше приложение. Теперь папка `electron` и есть дистрибутив,
|
и Electron запустится как ваше приложение. Теперь папка `electron` и есть дистрибутив,
|
||||||
который вы должны распространять пользователям.
|
который Вы должны распространять пользователям.
|
||||||
|
|
||||||
## Упаковка вашего приложения в файл
|
## Упаковка вашего приложения в файл
|
||||||
|
|
||||||
Если вы не хотите распространять исходные коды вашего проект, вы можете
|
Если Вы `не хотите` распространять исходные коды вашего проекта, Вы можете
|
||||||
упаковать его в архив [asar](https://github.com/atom/asar), чтобы не
|
упаковать его в архив [asar](https://github.com/atom/asar), чтобы не
|
||||||
показывать пользователям исходные коды.
|
показывать пользователям исходные коды.
|
||||||
|
|
||||||
|
@ -38,25 +38,26 @@ electron/resources/app
|
||||||
после чего Electron попробует считать ресурсы и запустить архив.
|
после чего Electron попробует считать ресурсы и запустить архив.
|
||||||
|
|
||||||
|
|
||||||
На OS X:
|
Для OS X:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
electron/Electron.app/Contents/Resources/
|
electron/Electron.app/Contents/Resources/
|
||||||
└── app.asar
|
└── app.asar
|
||||||
```
|
```
|
||||||
|
|
||||||
На Windows и Linux:
|
Для Windows и Linux:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
electron/resources/
|
electron/resources/
|
||||||
└── app.asar
|
└── app.asar
|
||||||
```
|
```
|
||||||
|
|
||||||
Больше деталей можна найти в [инстуркции по упаковке приложения](application-packaging.md).
|
Больше деталей можно найти в [инстуркции по упаковке приложения](application-packaging.md).
|
||||||
|
|
||||||
## Ребрендирование скачанных исполняемых файлов
|
## Ребрендирование скачанных исполняемых файлов
|
||||||
После того, как вы подключили ваше приложение к Electron,
|
|
||||||
вам наверняка захочеться ребрендировать его перед распространением.
|
После того, как Вы подключили ваше приложение к Electron,
|
||||||
|
Вам наверняка захочется ребрендировать его перед распространением.
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ electron/resources/
|
||||||
* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
|
* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
|
||||||
|
|
||||||
Вы таже можете переименовать приложение-помощник, чтобы оно не показывало `Electron Helper`,
|
Вы таже можете переименовать приложение-помощник, чтобы оно не показывало `Electron Helper`,
|
||||||
убедитесь, что вы переименовали его исполняемый файл.
|
убедитесь, что Вы переименовали его исполняемый файл.
|
||||||
|
|
||||||
Структура переименованного приложения выглядит примерно так:
|
Структура переименованного приложения выглядит примерно так:
|
||||||
|
|
||||||
|
@ -104,22 +105,22 @@ MyApp.app/Contents
|
||||||
## Rebranding by Rebuilding Electron from Source
|
## Rebranding by Rebuilding Electron from Source
|
||||||
|
|
||||||
Вы также можете ребрендировать Electron изменив имя продукта и собрав его
|
Вы также можете ребрендировать Electron изменив имя продукта и собрав его
|
||||||
из исходных кодов. Чтобы сделать это вам нужно изменить `atom.gyp` и полностью
|
из исходных кодов. Чтобы сделать это Вам нужно изменить `atom.gyp` и полностью
|
||||||
пересобрать Electron.
|
пересобрать Electron.
|
||||||
|
|
||||||
### grunt-build-atom-shell
|
### grunt-build-atom-shell
|
||||||
|
|
||||||
Проверка и пересборка кода Electron довольно сложная задача, так что мы
|
Проверка и пересборка кода Electron довольно сложная задача, так что мы
|
||||||
мы сделали файл-инструкцию для Grunt, который будет делать это автоматически:
|
сделали файл-инструкцию для Grunt, который будет делать это автоматически:
|
||||||
[grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell).
|
[grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell).
|
||||||
|
|
||||||
Этот файл автоматически просмотрит изменения в `.gyp` фалле, соберёт
|
Этот файл автоматически просмотрит изменения в `.gyp` файле, соберёт
|
||||||
Electron из исходных кодов и пересоберёт модули Node, чтобы всё подходило
|
Electron из исходных кодов и пересоберёт модули Node, чтобы всё подходило
|
||||||
под новое имя.
|
под новое имя.
|
||||||
|
|
||||||
## Инструменты
|
## Инструменты
|
||||||
|
|
||||||
Вы также можете использовать инструменты оттретьих лиц,
|
Вы также можете использовать инструменты от третьих лиц,
|
||||||
которые сделают работу за вас:
|
которые сделают работу за вас:
|
||||||
|
|
||||||
* [electron-packager](https://github.com/maxogden/electron-packager)
|
* [electron-packager](https://github.com/maxogden/electron-packager)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Упаковка приложения
|
# Упаковка приложения
|
||||||
|
|
||||||
Чтобы смягчить [проблемы](https://github.com/joyent/node/issues/6960) с длинными
|
Чтобы смягчить [проблемы](https://github.com/joyent/node/issues/6960) с длинными
|
||||||
именами под Windows, немного ускорить `require` и скрыть ваши исходные коды, вы
|
именами под Windows, немного ускорить `require` и скрыть ваши исходные коды, Вы
|
||||||
можете упаковать его в архив [asar][asar], немного поменяв исходный код.
|
можете упаковать его в архив [asar][asar], немного поменяв исходный код.
|
||||||
|
|
||||||
## Генерация архива `asar`
|
## Генерация архива `asar`
|
||||||
|
@ -25,16 +25,16 @@ $ asar pack your-app app.asar
|
||||||
|
|
||||||
## Использование архивов `asar`
|
## Использование архивов `asar`
|
||||||
|
|
||||||
В Electron есть два вида API: API Node, которые устанавливаются с помощью Node.Js и
|
В Electron есть два вида API: API Node, которые устанавливаются с помощью Node.JS и
|
||||||
веб API, которые предоставляюся Chromium. Оба предоставляют возможность считывать из
|
веб API, которые предоставляюся Chromium. Оба предоставляют возможность считывать из
|
||||||
архивов `asar`.
|
архивов `asar`.
|
||||||
|
|
||||||
### Node API
|
### Node API
|
||||||
|
|
||||||
С специальными патчами в Electron, части Node API вроде `fs.readFile` и `require`
|
Со специальными патчами в Electron, части Node API вроде `fs.readFile` и `require`
|
||||||
считают архивы `asar` виртуальными папками и файлы в них доступны как в обычных.
|
считают архивы `asar` виртуальными папками и файлы в них доступны как в обычных.
|
||||||
|
|
||||||
Например, у нас есть арихив `example.asar` в `/path/to`:
|
Например, у нас есть архив `example.asar` в `/path/to`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ asar list /path/to/example.asar
|
$ asar list /path/to/example.asar
|
||||||
|
@ -60,13 +60,13 @@ const fs = require('fs');
|
||||||
fs.readdirSync('/path/to/example.asar');
|
fs.readdirSync('/path/to/example.asar');
|
||||||
```
|
```
|
||||||
|
|
||||||
Ичпользуем модуль из архива:
|
Используем модуль из архива:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
require('/path/to/example.asar/dir/module.js');
|
require('/path/to/example.asar/dir/module.js');
|
||||||
```
|
```
|
||||||
|
|
||||||
Вы также можете показывать веб страницы из архива `asar` через `BrowserWindow`:
|
Вы также можете показывать веб-страницы из архива `asar` через `BrowserWindow`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const BrowserWindow = require('electron').BrowserWindow;
|
const BrowserWindow = require('electron').BrowserWindow;
|
||||||
|
@ -76,7 +76,7 @@ win.loadURL('file:///path/to/example.asar/static/index.html');
|
||||||
|
|
||||||
### Веб API
|
### Веб API
|
||||||
|
|
||||||
На веб страницах файлы запрашиваются с помощью протокола `file:`. Как и в Node API
|
На веб-страницах файлы запрашиваются с помощью протокола `file:`. Как и в Node API
|
||||||
архивы `asar` считаются за директории.
|
архивы `asar` считаются за директории.
|
||||||
|
|
||||||
Пример получения файла с помощью `$.get`:
|
Пример получения файла с помощью `$.get`:
|
||||||
|
@ -93,7 +93,7 @@ $.get('file:///path/to/example.asar/file.txt', function(data) {
|
||||||
|
|
||||||
### Использование архива `asar` в качестве обычного файла
|
### Использование архива `asar` в качестве обычного файла
|
||||||
|
|
||||||
Для случаев, когда вам, например, нужно проверить хэш-сумму архива `asar`,
|
Для случаев, когда Вам, например, нужно проверить хэш-сумму архива `asar`,
|
||||||
нужно использовать архив как файл. Для этой цели существует встроенный модуль
|
нужно использовать архив как файл. Для этой цели существует встроенный модуль
|
||||||
`original-fs`, который предоставляет доступ к `fs` без поддежки `asar`:
|
`original-fs`, который предоставляет доступ к `fs` без поддежки `asar`:
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ $.get('file:///path/to/example.asar/file.txt', function(data) {
|
||||||
var originalFs = require('original-fs');
|
var originalFs = require('original-fs');
|
||||||
originalFs.readFileSync('/path/to/example.asar');
|
originalFs.readFileSync('/path/to/example.asar');
|
||||||
```
|
```
|
||||||
|
|
||||||
Вы также можете выставить `process.noAsar` в `true`, чтобы выключить поддержку `asar`
|
Вы также можете выставить `process.noAsar` в `true`, чтобы выключить поддержку `asar`
|
||||||
в модуле `fs`:
|
в модуле `fs`:
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ fs.readFileSync('/path/to/example.asar');
|
||||||
## Ограничения Node API
|
## Ограничения Node API
|
||||||
|
|
||||||
Хотя мы и старались как могли, чтобы сделать `asar` максимально похожим на папки,
|
Хотя мы и старались как могли, чтобы сделать `asar` максимально похожим на папки,
|
||||||
всё ещё существуют некоторые ограничения из-за низкоуровневой натуры Node API.
|
всё ещё существуют некоторые ограничения из-за низкоуровневой архитектуры Node API.
|
||||||
|
|
||||||
### Архивы только для чтения
|
### Архивы только для чтения
|
||||||
|
|
||||||
|
@ -121,8 +122,8 @@ fs.readFileSync('/path/to/example.asar');
|
||||||
|
|
||||||
### Нельзя установить рабочую директорию в архиве
|
### Нельзя установить рабочую директорию в архиве
|
||||||
|
|
||||||
Хотя архивы `asar` и считаются папками, они ими на самом деле не являются,
|
Хотя архивы `asar` и считаются папками, они ими на самом деле не являются ими,
|
||||||
так что вы не можете установить в них рабочую директорию. Передача
|
так что Вы не можете установить в них рабочую директорию. Передача
|
||||||
архивов `asar` в качестве аргумента `cwd` некоторым API также может вызывать ошибки.
|
архивов `asar` в качестве аргумента `cwd` некоторым API также может вызывать ошибки.
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,18 +155,18 @@ API которым нужна распаковка:
|
||||||
`child_process.spawn` и `child_process.execFile`, но только `execFile` может
|
`child_process.spawn` и `child_process.execFile`, но только `execFile` может
|
||||||
исполнять файлы из архивов `asar`.
|
исполнять файлы из архивов `asar`.
|
||||||
|
|
||||||
Так вышло потому, что `exec` и `spawn` принимают `команду` а не `файл` как параметр,
|
Так вышло потому, что `exec` и `spawn` принимают `команду`, а не `файл` как параметр,
|
||||||
а `команды` исполняются в оболочке. Нет никакой реальной возможности проверить,
|
а `команды` исполняются в оболочке. Нет никакой реальной возможности проверить,
|
||||||
реален ли файл или находится в архиве `asar`, и даже если мы смогли бы проверить,
|
реален ли файл или находится в архиве `asar`, и даже если мы смогли бы проверить,
|
||||||
то неясно, как земенить путь к файлу без побочных эффектов.
|
то неясно, как земенить путь к файлу без побочных эффектов.
|
||||||
|
|
||||||
## Добавление распакованых файлов в архив `asar`
|
## Добавление распакованых файлов в архив `asar`
|
||||||
|
|
||||||
Как говорилось выше, некоторые API Node будут распаковывать файлв,
|
Как говорилось выше, некоторые API Node будут распаковывать файлы,
|
||||||
чтобы их использовать. Кроме увеличенного потребления ресурсов это также
|
чтобы их использовать. Кроме увеличенного потребления ресурсов это также
|
||||||
может вызвать предупрждения от антивирусов.
|
может вызвать предупреждения от антивирусов.
|
||||||
|
|
||||||
Чтобы обойти это, вы можете распаковать некоторые файлы, создавая архивы,
|
Чтобы обойти это, Вы можете распаковать некоторые файлы, создавая архивы,
|
||||||
с помощью опции `--unpack`. Пример показывает распаковку нативных модулей:
|
с помощью опции `--unpack`. Пример показывает распаковку нативных модулей:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
#Руководство по утверждению вашего приложения в App Store
|
#Руководство по утверждению вашего приложения в App Store
|
||||||
|
|
||||||
Начиная с версии v0.34.0 Electron позволяет вам сформировать данные для App Store к вашему приложению.
|
Начиная с версии v0.34.0 Electron позволяет Вам сформировать данные для App Store к вашему приложению.
|
||||||
Данное руководство представляет собой пошаговую инструкцию по созданию данных файлов.
|
Данное руководство представляет собой пошаговую инструкцию по созданию данных файлов.
|
||||||
|
|
||||||
Помните, что когда Вы подаете свое приложение на рассмотрение в App Store Вы должны обладать аккаунтом разработчика,
|
Помните, что когда Вы подаете свое приложение на рассмотрение в App Store Вы должны обладать аккаунтом разработчика,
|
||||||
|
@ -9,9 +9,7 @@
|
||||||
|
|
||||||
## Как отправить свое приложение на рассмотрение в App Store
|
## Как отправить свое приложение на рассмотрение в App Store
|
||||||
|
|
||||||
Последующие шаги подробно описывают, что и в какой последовательности Вам требуется сделать, но не гарантируют что Ваше приложение
|
Последующие шаги подробно описывают, что и в какой последовательности Вам требуется сделать, но не гарантируют, что Ваше приложение будет рассмотрено Apple. Мы также рекомендуем Вам прочитать официальную документацию по оформлению своего приложения и информации к нему, чтобы пройти проверку в App Store.
|
||||||
будет рассмотрено Apple. Мы так же рекомендуем вам прочитать официальную документацию по оформлению своего приложения и информации к нему
|
|
||||||
чтобы пройти проверку в App Store.
|
|
||||||
|
|
||||||
## Получение сертификата
|
## Получение сертификата
|
||||||
|
|
||||||
|
@ -24,8 +22,9 @@
|
||||||
|
|
||||||
Во-первых, нам нужно подготовить два файла:
|
Во-первых, нам нужно подготовить два файла:
|
||||||
|
|
||||||
child.plist:
|
`child.plist`:
|
||||||
|
|
||||||
|
```xml
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
|
@ -36,9 +35,11 @@
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
```
|
||||||
parent.plist:
|
|
||||||
|
`parent.plist`:
|
||||||
|
|
||||||
|
```xml
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
|
@ -49,8 +50,11 @@
|
||||||
<string>(allow mach-lookup (global-name-regex #"^org.chromium.Chromium.rohitfork.[0-9]+$"))</string>
|
<string>(allow mach-lookup (global-name-regex #"^org.chromium.Chromium.rohitfork.[0-9]+$"))</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
```
|
||||||
|
|
||||||
Затем подписываем свое приложение, с помощью специального сценария:
|
Затем подписываем свое приложение, с помощью специального сценария:
|
||||||
|
|
||||||
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Имя вашего приложения.
|
# Имя вашего приложения.
|
||||||
|
@ -79,42 +83,45 @@
|
||||||
codesign -s "$APP_KEY" -f --entitlements parent.plist "$APP_PATH"
|
codesign -s "$APP_KEY" -f --entitlements parent.plist "$APP_PATH"
|
||||||
|
|
||||||
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_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'
|
## Объяснение использования '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. Самое важное указать почему ваше приложение должно быть исключенно.
|
>которые предоставляются в Itunes Connect. Самое важное указать почему ваше приложение должно быть исключенно.
|
||||||
|
|
||||||
Вы можете объяснить, что ваше приложение построено на основе браузера Chromium, который использует Mach из-за его мульти-процесс архитектуры. Но есть еще вероятность, что ваше приложение не удалось проверить именно из-за этого.
|
Вы можете объяснить, что ваше приложение построено на основе браузера Chromium, который использует Mach из-за его мультипроцессной архитектуры. Но есть еще вероятность, что ваше приложение не удалось проверить именно из-за этого.
|
||||||
|
|
||||||
## Отправка приложения на проверку
|
## Отправка приложения на проверку
|
||||||
|
|
||||||
Следующие шаги описаны в официальной [документации](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html "Официальная статья по отправке приложения на проверку")
|
Следующие шаги описаны в официальной [документации](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
|
- crashReporter
|
||||||
- autoUpdater
|
- autoUpdater
|
||||||
и следующие проблемы были несколько изменены:
|
|
||||||
|
А также следующие проблемы были несколько изменены:
|
||||||
- Захват видео на некоторых машинах может не работать
|
- Захват видео на некоторых машинах может не работать
|
||||||
- Некоторые специалньые возможности могут не работать
|
- Некоторые специальные возможности могут не работать
|
||||||
- Приложения не будут в курсе изменения DNS
|
- Приложения не будут в курсе изменения DNS
|
||||||
Так же из за использования SandBox App Store некоторые возможности могут быть не доступны или ограничены, подробнее о ограничениях
|
|
||||||
|
Также из-за использования SandBox App Store некоторые возможности могут быть не доступны или ограничены, подробнее о ограничениях
|
||||||
Вы можете прочитать в [документации](https://developer.apple.com/app-sandboxing/ "Ссылка на ограничения в SandBox AppStore")
|
Вы можете прочитать в [документации](https://developer.apple.com/app-sandboxing/ "Ссылка на ограничения в SandBox AppStore")
|
||||||
|
|
||||||
# Криптографические алгоритмы котоыре использует Electron
|
# Криптографические алгоритмы которые использует Electron
|
||||||
|
|
||||||
Смотря в какой стране и городе Вы находитесь Apple может потребовать от Вас задокументировать алгоритмы криптографии котоыре вы используете
|
Смотря в какой стране и городе Вы находитесь, Apple может потребовать от Вас задокументировать алгоритмы криптографии которые Вы используете
|
||||||
и если потребуется то попросит предоставить Вас копию регистрации вашего алгоритма шифрования.
|
и если потребуется, то попросит Вас предоставить копию регистрации вашего алгоритма шифрования.
|
||||||
|
|
||||||
Electron использует следующие алгоритмы шифрования:
|
Electron использует следующие алгоритмы шифрования:
|
||||||
- AES - NIST SP 800-38A, NIST SP 800-38D, RFC 3394
|
- 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
|
- RC5 - http://people.csail.mit.edu/rivest/Rivest-rc5rev.pdf
|
||||||
- RIPEMD - ISO/IEC 10118-3
|
- RIPEMD - ISO/IEC 10118-3
|
||||||
|
|
||||||
Если Вы используете необычный алгоритм, то вот статья о том как получить разрешение на использование собственного алгоритма шифрования в
|
Если Вы используете необычный алгоритм, то вот статья о том, как получить разрешение на использование собственного алгоритма шифрования в
|
||||||
рамках закона США - [статья](https://pupeno.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ "Статья о том как получить разрешение на свой алгоритм шифрования")
|
рамках закона США - [статья](https://pupeno.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ "Статья о том как получить разрешение на свой алгоритм шифрования")
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Быстрый старт
|
# Быстрый старт
|
||||||
|
|
||||||
Electron позволяет вам делать приложения для рабочего стола на чистом JavaScript,
|
Electron позволяет Вам делать приложения для рабочего стола на чистом JavaScript,
|
||||||
предоставляя среду с богатым API. Можете представлять его как Node.js, который
|
предоставляя среду с богатым API. Можете представлять его как Node.js приложение, которое
|
||||||
ориентирован на рабочий стол, а не веб сервера.
|
ориентировано для рабочего стола, а не для веб сервера.
|
||||||
|
|
||||||
Это, однако, не значит, что Electron — лишь привязки к GUI билиотекам. На деле
|
Однако это не значит, что Electron — лишь привязка к GUI билиотекам. На деле
|
||||||
Electron использует веб-страницы как интерфейс, так что вы можете считать его
|
Electron использует веб-страницы как интерфейс, так что Вы можете считать его
|
||||||
небольшим Chroumium браузером, который контролируется с помощью JavaScript.
|
небольшим Chroumium браузером, который контролируется с помощью JavaScript.
|
||||||
|
|
||||||
### Главный процесс
|
### Главный процесс
|
||||||
|
@ -18,11 +18,11 @@ __главным процессом__. Скрипт, который работа
|
||||||
|
|
||||||
Так как Electron использует Chromium для показа веб-страниц,
|
Так как Electron использует Chromium для показа веб-страниц,
|
||||||
мульти-процессовая архитектура показа страниц Chromium тоже используется.
|
мульти-процессовая архитектура показа страниц Chromium тоже используется.
|
||||||
Каждая веб-страницы в Electron работает в своём собственном процессе,
|
Каждая веб-страница в Electron работает в своём собственном процессе,
|
||||||
который называется __процесс-рендерер__.
|
который называется __процесс-рендерер__.
|
||||||
|
|
||||||
В обычных браузерах веб-страницы обычно запускаются в "песочнице" и им недоступны
|
В обычных браузерах веб-страницы обычно запускаются в "песочнице" и им недоступны
|
||||||
реальные ресурсы компьютера. Пользователи Electron же могут использовать API
|
реальные ресурсы компьютера. Пользователи Electron напротив могут использовать API
|
||||||
Node.js на страницах, что допускает более низкоуровневую работу с операционной системой.
|
Node.js на страницах, что допускает более низкоуровневую работу с операционной системой.
|
||||||
|
|
||||||
### Разница мужду главным процессом и процессом-рендерером
|
### Разница мужду главным процессом и процессом-рендерером
|
||||||
|
@ -43,11 +43,11 @@ Node.js на страницах, что допускает более низко
|
||||||
В Electron есть несолько способов общения между процессам. Например, модули
|
В Electron есть несолько способов общения между процессам. Например, модули
|
||||||
[`ipcRenderer`](../api/ipc-renderer.md) и [`ipcMain`](../api/ipc-main.md) используются
|
[`ipcRenderer`](../api/ipc-renderer.md) и [`ipcMain`](../api/ipc-main.md) используются
|
||||||
для отправки сообщений, а [remote](../api/remote.md) - для коммуникации в RPC стиле.
|
для отправки сообщений, а [remote](../api/remote.md) - для коммуникации в RPC стиле.
|
||||||
В ЧАВО также есть пункт о том, [как разделять информацию между страницами][share-data]
|
В FAQ также есть пункт о том, [как разделять информацию между страницами][share-data]
|
||||||
|
|
||||||
## Первое приложение на Electron
|
## Первое приложение на Electron
|
||||||
|
|
||||||
Как правило, приложение Electron структурировано следующим образом::
|
Как правило, приложение Electron структурировано следующим образом:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
your-app/
|
your-app/
|
||||||
|
@ -56,9 +56,9 @@ your-app/
|
||||||
└── index.html
|
└── index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
Формат `package.json` точно такой же, как у модулей Node и сприпт, объявленый
|
Формат `package.json` точно такой же, как у модулей Node и скрипт, объявленый
|
||||||
как `main`, будет выполняться при запуске вашего приложения, работая в
|
как `main`, будет выполняться при запуске вашего приложения, работая в
|
||||||
главном процессе. Например, ваш `package.json` может выглядеть вот так:
|
главном процессе. Например, Ваш `package.json` может выглядеть вот так:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -82,12 +82,12 @@ const app = electron.app
|
||||||
// Модуль, создающий окно приложения.
|
// Модуль, создающий окно приложения.
|
||||||
const BrowserWindow = electron.BrowserWindow
|
const BrowserWindow = electron.BrowserWindow
|
||||||
|
|
||||||
// Удерживайте глобальное обращение к объекту окна, если вы так не сделаете, то
|
// Удерживайте глобальное обращение к объекту окна, если Вы так не сделаете, то
|
||||||
// окно само закроется после того, как объект будет собран сборщиком мусора.
|
// окно само закроется после того, как объект будет собран сборщиком мусора.
|
||||||
let mainWindow
|
let mainWindow
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow () {
|
||||||
// Создаём окно браузера.
|
// Создаём окно браузера
|
||||||
mainWindow = new BrowserWindow({width: 800, height: 600})
|
mainWindow = new BrowserWindow({width: 800, height: 600})
|
||||||
|
|
||||||
// и загружаем index.html приложения.
|
// и загружаем index.html приложения.
|
||||||
|
@ -98,9 +98,9 @@ function createWindow () {
|
||||||
|
|
||||||
// Будет выполнено, когда пользователь закроет окно
|
// Будет выполнено, когда пользователь закроет окно
|
||||||
mainWindow.on('closed', function () {
|
mainWindow.on('closed', function () {
|
||||||
//Убрать обращение на объект окна, обычно стоит хранить окна в массиве,
|
// Убрать обращение на объект окна, обычно стоит хранить окна в массиве,
|
||||||
//если ваше приложение поддерживает несколько, сейчас стоит удалить
|
// если ваше приложение поддерживает несколько, сейчас стоит удалить
|
||||||
//соответствующий элемент.
|
// соответствующий элемент.
|
||||||
mainWindow = null
|
mainWindow = null
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ app.on('ready', createWindow)
|
||||||
// Выйти, если все окна закрыты
|
// Выйти, если все окна закрыты
|
||||||
app.on('window-all-closed', function () {
|
app.on('window-all-closed', function () {
|
||||||
//На OS X приложение и его строка меню обычно остаются активными,
|
//На OS X приложение и его строка меню обычно остаются активными,
|
||||||
//пока пользователь не завершит их с помощью Cmd + Q.
|
//пока пользователь не завершит их с помощью `Cmd + Q`.
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
|
@ -129,12 +129,12 @@ app.on('activate', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
//В этот файл вы можете включить остальной код вашего главного процесса.
|
//В этот файл Вы можете включить остальной код вашего главного процесса.
|
||||||
//Вы также можете разложить его по отдельным файлам и подключить с помощью require.
|
//Вы также можете разложить его по отдельным файлам и подключить с помощью require.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Наконец, `index.html`, страница, которую вы хотите показать:
|
Наконец, `index.html`, страница, которую Вы хотите показать:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE 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
|
||||||
|
|
||||||
[`electron-prebuilt`](https://github.com/electron-userland/electron-prebuilt) — `npm` модуль,
|
[`electron-prebuilt`](https://github.com/electron-userland/electron-prebuilt) — `npm` модуль,
|
||||||
который содержит прекомпилированную версию Electron.
|
который содержит прекомпилированную версию Electron.
|
||||||
|
|
||||||
Если вы установили Electron глобально через `npm`, то вам нужно будет всего лишь
|
Если вы установили Electron глобально через `npm`, то Вам нужно будет всего лишь
|
||||||
запустить сдедующее в папке вашего проекта:
|
запустить сдедующее в папке вашего проекта:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
electron .
|
electron .
|
||||||
```
|
```
|
||||||
|
|
||||||
Если вы установили Electron локально, то выполните это:
|
Если Вы установили Electron локально, то выполните это:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./node_modules/.bin/electron .
|
./node_modules/.bin/electron .
|
||||||
|
@ -177,7 +177,7 @@ electron .
|
||||||
|
|
||||||
### Исполняемые файлы Electron, скачанные вручную
|
### Исполняемые файлы Electron, скачанные вручную
|
||||||
|
|
||||||
Если вы скачали Electron вручную, то вы можете использовать
|
Если Вы скачали Electron вручную, то Вы можете использовать
|
||||||
исполняемые файлы прямо в папке вашего проекта.
|
исполняемые файлы прямо в папке вашего проекта.
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
|
@ -198,27 +198,27 @@ $ ./electron/electron your-app/
|
||||||
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
||||||
```
|
```
|
||||||
|
|
||||||
`Electron.app` — часть реализного пакета Electron, вы можете скачать его
|
`Electron.app` — часть реализного пакета Electron, Вы можете скачать его
|
||||||
[тут](https://github.com/electron/electron/releases).
|
[тут](https://github.com/electron/electron/releases).
|
||||||
|
|
||||||
### Запустить как дистрибутив
|
### Запустить как дистрибутив
|
||||||
|
|
||||||
Когда вы закончили написание вашего приложения, вы можете создать
|
Когда Вы закончили написание вашего приложения, Вы можете создать
|
||||||
дистрибутив, следуя инструкциям [отсюда](./application-distribution.md) и
|
дистрибутив, следуя инструкциям [отсюда](./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)).
|
**Заметка**: Для запуска требуется [Git](https://git-scm.com) и [Node.js](https://nodejs.org/en/download/) (который включает в себя [npm](https://npmjs.org)).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Склонируйте репозиторий
|
# Клонируем репозиторий
|
||||||
$ git clone https://github.com/electron/electron-quick-start
|
$ git clone https://github.com/electron/electron-quick-start
|
||||||
# Перейдите в папку репозитория
|
# Переходим в папку скачанного репозитория
|
||||||
$ cd electron-quick-start
|
$ cd electron-quick-start
|
||||||
# Установите зависимости и запустите
|
# Устанавливаем зависимости и запускаем
|
||||||
$ npm install && npm start
|
$ npm install && npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
Платформы поддерживаемые Electron:
|
# Платформы поддерживаемые Electron:
|
||||||
|
|
||||||
#OS X
|
Следующие платформы поддерживаются Electron:
|
||||||
Поддерживает только 64-ых битные OS X. Минимально поддерживаемой версией является OS X 10.9
|
|
||||||
|
|
||||||
#Windows
|
### OS X
|
||||||
Поддерживаются операционные систем Windows 7 и выше, старые операционные системы не поддерживаются (и не работают).
|
|
||||||
Поддерживаются платформы на x86 и amd64 (64-разрядная) для Windows. Будьте внимательны, что ARM Windows не поддерживается на данный момент.
|
Поддерживает только 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
|
* Ubuntu 12.04 и выше
|
||||||
- Debian 8
|
* Fedora 21
|
||||||
|
* Debian 8
|
||||||
|
|
|
@ -32,7 +32,7 @@ app.on('window-all-closed', function() {
|
||||||
|
|
||||||
当所有的窗口都被关闭时触发。
|
当所有的窗口都被关闭时触发。
|
||||||
|
|
||||||
这个时间仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`,
|
这个事件仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`,
|
||||||
或者开发者调用了 `app.quit()` ,Electron 将会先尝试关闭所有的窗口再触发 `will-quit` 事件,
|
或者开发者调用了 `app.quit()` ,Electron 将会先尝试关闭所有的窗口再触发 `will-quit` 事件,
|
||||||
在这种情况下 `window-all-closed` 不会被触发。
|
在这种情况下 `window-all-closed` 不会被触发。
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,36 @@
|
||||||
# Mac App Store 应用提交向导
|
# Mac App Store 应用提交向导
|
||||||
|
|
||||||
自从 v0.34.0, Electron 就允许提交应用包到 Mac App Store
|
自从 v0.34.0,Electron 就允许提交应用包到 Mac App Store
|
||||||
(MAS) . 这个向导提供的信息有 : 如何提交应用和 MAS 构建的限制.
|
(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
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
...
|
||||||
|
<key>ElectronTeamID</key>
|
||||||
|
<string>TEAM_ID</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
```
|
||||||
|
|
||||||
|
之后,你需要准备2个授权文件。
|
||||||
|
|
||||||
`child.plist`:
|
`child.plist`:
|
||||||
|
|
||||||
|
@ -46,67 +56,76 @@ Program][developer-program] , 这需要花钱.
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<string>TEAM_ID.your.bundle.id</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
```
|
```
|
||||||
|
|
||||||
然后使用下面的脚本标识你的应用 :
|
请注意上述 `TEAM_ID` 对应开发者账户的 Team ID,`your.bundle.id` 对应软件打包时使用的 Bundle ID。
|
||||||
|
|
||||||
|
然后使用下面的脚本签名你的应用:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Name of your app.
|
# 应用名称
|
||||||
APP="YourApp"
|
APP="YourApp"
|
||||||
# The path of you app to sign.
|
# 应用路径
|
||||||
APP_PATH="/path/to/YouApp.app"
|
APP_PATH="/path/to/YourApp.app"
|
||||||
# The path to the location you want to put the signed package.
|
# 生成安装包路径
|
||||||
RESULT_PATH="~/Desktop/$APP.pkg"
|
RESULT_PATH="~/Desktop/$APP.pkg"
|
||||||
# The name of certificates you requested.
|
# 开发者应用签名证书
|
||||||
APP_KEY="3rd Party Mac Developer Application: Company Name (APPIDENTITY)"
|
APP_KEY="3rd Party Mac Developer Application: Company Name (APPIDENTITY)"
|
||||||
INSTALLER_KEY="3rd Party Mac Developer Installer: 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"
|
FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
|
||||||
|
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libnode.dylib"
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework"
|
||||||
if [ -d "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A" ]; then
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper"
|
||||||
# Signing a non-MAS build.
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/"
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Mantle.framework/Versions/A"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/Contents/MacOS/$APP Helper EH"
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/ReactiveCocoa.framework/Versions/A"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/"
|
||||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/Contents/MacOS/$APP Helper NP"
|
||||||
fi
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/"
|
||||||
codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH"
|
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"
|
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`
|
* `crashReporter`
|
||||||
* `autoUpdater`
|
* `autoUpdater`
|
||||||
|
|
||||||
并且下面的行为也改变了:
|
并且下面的行为也改变了:
|
||||||
|
|
||||||
* 一些机子的视频采集功能无效.
|
* 一些视频采集功能无效。
|
||||||
* 某些特征不可访问.
|
* 某些辅助功能无法访问。
|
||||||
* Apps 不可识别 DNS 改变.
|
* 应用无法检测 DNS 变化。
|
||||||
|
|
||||||
也由于应用沙箱的使用方法,应用可以访问的资源被严格限制了 ; 阅读更多信息 [App Sandboxing][app-sandboxing] .
|
也由于应用沙箱的使用方法,应用可以访问的资源被严格限制了;阅读更多信息 [App Sandboxing][app-sandboxing]。
|
||||||
|
|
||||||
## Electron 使用的加密算法
|
## 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)
|
* 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)
|
* 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
|
* 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)
|
* 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
|
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/
|
[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
|
[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
|
[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/
|
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
|
||||||
[issue-3871]: https://github.com/electron/electron/issues/3871
|
[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/
|
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# 快速入门
|
# 快速入门
|
||||||
|
|
||||||
## 简介
|
Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作一个专注于桌面应用的 Node.js 的变体,而不是 Web 服务器。
|
||||||
Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作是专注于桌面应用而不是 web 服务器的,io.js 的一个变体。
|
|
||||||
|
|
||||||
这不意味着 Electron 是绑定了 GUI 库的 JavaScript。相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。
|
这不意味着 Electron 是绑定了 GUI 库的 JavaScript。相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。
|
||||||
|
|
||||||
|
@ -14,13 +13,13 @@ Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造
|
||||||
在一般浏览器中,网页通常会在沙盒环境下运行,并且不允许访问原生资源。然而,Electron 用户拥有在网页中调用 io.js 的 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 应用
|
||||||
大体上,一个 Electron 应用的目录结构如下:
|
大体上,一个 Electron 应用的目录结构如下:
|
||||||
|
@ -42,53 +41,72 @@ your-app/
|
||||||
|
|
||||||
`main.js` 应该用于创建窗口和处理系统事件,一个典型的例子如下:
|
`main.js` 应该用于创建窗口和处理系统事件,一个典型的例子如下:
|
||||||
```javascript
|
```javascript
|
||||||
var app = require('app'); // 控制应用生命周期的模块。
|
const electron = require('electron');
|
||||||
var BrowserWindow = require('browser-window'); // 创建原生浏览器窗口的模块
|
// 控制应用生命周期的模块。
|
||||||
|
const {app} = electron;
|
||||||
|
// 创建原生浏览器窗口的模块。
|
||||||
|
const {BrowserWindow} = electron;
|
||||||
|
|
||||||
// 保持一个对于 window 对象的全局引用,不然,当 JavaScript 被 GC,
|
// 保持一个对于 window 对象的全局引用,如果你不这样做,
|
||||||
// window 会被自动地关闭
|
// 当 JavaScript 对象被垃圾回收, window 会被自动地关闭
|
||||||
var mainWindow = null;
|
let mainWindow;
|
||||||
|
|
||||||
// 当所有窗口被关闭了,退出。
|
function createWindow() {
|
||||||
app.on('window-all-closed', function() {
|
// 创建浏览器窗口。
|
||||||
// 在 OS X 上,通常用户在明确地按下 Cmd + Q 之前
|
mainWindow = new BrowserWindow({width: 800, height: 600});
|
||||||
// 应用会保持活动状态
|
|
||||||
if (process.platform != 'darwin') {
|
// 加载应用的 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();
|
app.quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当 Electron 完成了初始化并且准备创建浏览器窗口的时候
|
app.on('activate', () => {
|
||||||
// 这个方法就被调用
|
// On OS X it's common to re-create a window in the app when the
|
||||||
app.on('ready', function() {
|
// dock icon is clicked and there are no other windows open.
|
||||||
// 创建浏览器窗口。
|
if (mainWindow === null) {
|
||||||
mainWindow = new BrowserWindow({width: 800, height: 600});
|
createWindow();
|
||||||
|
}
|
||||||
// 加载应用的 index.html
|
|
||||||
mainWindow.loadURL('file://' + __dirname + '/index.html');
|
|
||||||
|
|
||||||
// 打开开发工具
|
|
||||||
mainWindow.openDevTools();
|
|
||||||
|
|
||||||
// 当 window 被关闭,这个事件会被发出
|
|
||||||
mainWindow.on('closed', function() {
|
|
||||||
// 取消引用 window 对象,如果你的应用支持多窗口的话,
|
|
||||||
// 通常会把多个 window 对象存放在一个数组里面,
|
|
||||||
// 但这次不是。
|
|
||||||
mainWindow = null;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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` :
|
最后,你想展示的 `index.html` :
|
||||||
```html
|
```html
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
<title>Hello World!</title>
|
<title>Hello World!</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello World!</h1>
|
<h1>Hello World!</h1>
|
||||||
We are using io.js <script>document.write(process.version)</script>
|
We are using node <script>document.write(process.versions.node)</script>,
|
||||||
|
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||||
and Electron <script>document.write(process.versions.electron)</script>.
|
and Electron <script>document.write(process.versions.electron)</script>.
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -97,8 +115,9 @@ app.on('ready', function() {
|
||||||
# 运行你的应用
|
# 运行你的应用
|
||||||
一旦你创建了最初的 `main.js`, `index.html` 和 `package.json` 这几个文件,你可能会想尝试在本地运行并测试,看看是不是和期望的那样正常运行。
|
一旦你创建了最初的 `main.js`, `index.html` 和 `package.json` 这几个文件,你可能会想尝试在本地运行并测试,看看是不是和期望的那样正常运行。
|
||||||
|
|
||||||
## electron-prebuild
|
## electron-prebuilt
|
||||||
如果你已经用 `npm` 全局安装了 `electron-prebuilt`,你只需要按照如下方式直接运行你的应用:
|
[electron-prebuilt][5] 是一个 `npm` 模块,包含所使用的 Electron 预编译版本。
|
||||||
|
如果你已经用 `npm` 全局安装了它,你只需要按照如下方式直接运行你的应用:
|
||||||
```bash
|
```bash
|
||||||
electron .
|
electron .
|
||||||
```
|
```
|
||||||
|
@ -121,13 +140,32 @@ $ ./electron/electron your-app/
|
||||||
```bash
|
```bash
|
||||||
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
$ ./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
|
*注意:*运行时需要你的系统已经安装了 [Git][9] 和 [Node.js][10](包含 [npm][11])。
|
||||||
[2]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/remote.md
|
|
||||||
[3]: https://github.com/electron/electron/releases
|
```bash
|
||||||
[4]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/tutorial/application-distribution.md
|
# 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/
|
||||||
|
|
|
@ -346,6 +346,7 @@ You can request the following paths by the name:
|
||||||
* `music` Directory for a user's music.
|
* `music` Directory for a user's music.
|
||||||
* `pictures` Directory for a user's pictures.
|
* `pictures` Directory for a user's pictures.
|
||||||
* `videos` Directory for a user's videos.
|
* `videos` Directory for a user's videos.
|
||||||
|
* `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin.
|
||||||
|
|
||||||
### `app.setPath(name, path)`
|
### `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`
|
`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).
|
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])`
|
### `app.commandLine.appendSwitch(switch[, value])`
|
||||||
|
|
||||||
Append a switch (with optional `value`) to Chromium's command line.
|
Append a switch (with optional `value`) to Chromium's command line.
|
||||||
|
|
|
@ -175,9 +175,13 @@ The `webPreferences` option is an object that can have following properties:
|
||||||
* `scrollBounce` Boolean - Enables scroll bounce (rubber banding) effect on
|
* `scrollBounce` Boolean - Enables scroll bounce (rubber banding) effect on
|
||||||
OS X. Default is `false`.
|
OS X. Default is `false`.
|
||||||
* `blinkFeatures` String - A list of feature strings separated by `,`, like
|
* `blinkFeatures` String - A list of feature strings separated by `,`, like
|
||||||
`CSSVariables,KeyboardEventKey`. The full list of supported feature strings
|
`CSSVariables,KeyboardEventKey` to enable. The full list of supported feature
|
||||||
can be found in the [setFeatureEnabledFromString][blink-feature-string]
|
strings can be found in the [RuntimeEnabledFeatures.in][blink-feature-string]
|
||||||
function.
|
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.
|
* `defaultFontFamily` Object - Sets the default font for the font-family.
|
||||||
* `standard` String - Defaults to `Times New Roman`.
|
* `standard` String - Defaults to `Times New Roman`.
|
||||||
* `serif` 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.
|
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
|
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)`
|
### `BrowserWindow.removeDevToolsExtension(name)`
|
||||||
|
|
||||||
|
@ -877,7 +885,7 @@ The `flags` is an array that can include following `String`s:
|
||||||
|
|
||||||
### `win.showDefinitionForSelection()` _OS X_
|
### `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_
|
### `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.
|
**Note:** This API always returns false on Windows.
|
||||||
|
|
||||||
### `win.setIgnoreMouseEvents(ignore)` _OS X_
|
### `win.setIgnoreMouseEvents(ignore)`
|
||||||
|
|
||||||
* `ignore` Boolean
|
* `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
|
||||||
|
|
|
@ -2,49 +2,70 @@
|
||||||
|
|
||||||
> Control file downloads from remote sources.
|
> Control file downloads from remote sources.
|
||||||
|
|
||||||
`DownloadItem` is an EventEmitter that represents a download item in Electron.
|
`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
|
It is used in `will-download` event of `Session` class, and allows users to
|
||||||
control the download item.
|
control the download item.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In the main process.
|
// In the main process.
|
||||||
win.webContents.session.on('will-download', (event, item, webContents) => {
|
win.webContents.session.on('will-download', (event, item, webContents) => {
|
||||||
// Set the save path, making Electron not to prompt a save dialog.
|
// Set the save path, making Electron not to prompt a save dialog.
|
||||||
item.setSavePath('/tmp/save.pdf');
|
item.setSavePath('/tmp/save.pdf')
|
||||||
console.log(item.getMimeType());
|
|
||||||
console.log(item.getFilename());
|
item.on('updated', (event, state) => {
|
||||||
console.log(item.getTotalBytes());
|
if (state === 'interrupted') {
|
||||||
item.on('updated', () => {
|
console.log('Download is interrupted but can be resumed')
|
||||||
console.log('Received bytes: ' + item.getReceivedBytes());
|
} else if (state === 'progressing') {
|
||||||
});
|
if (item.isPaused()) {
|
||||||
item.on('done', (e, state) => {
|
console.log('Download is paused')
|
||||||
if (state === 'completed') {
|
} else {
|
||||||
console.log('Download successfully');
|
console.log(`Received bytes: ${item.getReceivedBytes()}`)
|
||||||
} else {
|
}
|
||||||
console.log('Download is cancelled or interrupted that can\'t be resumed');
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
item.once('done', (event, state) => {
|
||||||
|
if (state === 'completed') {
|
||||||
|
console.log('Download successfully')
|
||||||
|
} else {
|
||||||
|
console.log(`Download failed: ${state}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
### Event: 'updated'
|
### Event: 'updated'
|
||||||
|
|
||||||
Emits when the `downloadItem` gets updated.
|
Returns:
|
||||||
|
|
||||||
### Event: 'done'
|
|
||||||
|
|
||||||
* `event` Event
|
* `event` Event
|
||||||
* `state` String
|
* `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, a cancelled download(via `downloadItem.cancel()`), and interrupted
|
||||||
download that can't be resumed.
|
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
|
## Methods
|
||||||
|
|
||||||
The `downloadItem` object has the following 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.
|
Pauses the download.
|
||||||
|
|
||||||
|
### `downloadItem.isPaused()`
|
||||||
|
|
||||||
|
Returns whether the download is paused.
|
||||||
|
|
||||||
### `downloadItem.resume()`
|
### `downloadItem.resume()`
|
||||||
|
|
||||||
Resumes the download that has been paused.
|
Resumes the download that has been paused.
|
||||||
|
|
||||||
|
### `downloadItem.canResume()`
|
||||||
|
|
||||||
|
Resumes whether the download can resume.
|
||||||
|
|
||||||
### `downloadItem.cancel()`
|
### `downloadItem.cancel()`
|
||||||
|
|
||||||
Cancels the download operation.
|
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
|
Returns a `String` represents the Content-Disposition field from the response
|
||||||
header.
|
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.
|
||||||
|
|
|
@ -14,8 +14,8 @@ To create a frameless window, you need to set `frame` to `false` in
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const {BrowserWindow} = require('electron');
|
const {BrowserWindow} = require('electron')
|
||||||
let win = new BrowserWindow({width: 800, height: 600, frame: false});
|
let win = new BrowserWindow({width: 800, height: 600, frame: false})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Alternatives on OS X
|
### 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:
|
You can do so by specifying the new `titleBarStyle` option:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
let win = new BrowserWindow({titleBarStyle: 'hidden'});
|
let win = new BrowserWindow({titleBarStyle: 'hidden'})
|
||||||
```
|
```
|
||||||
|
|
||||||
## Transparent window
|
## Transparent window
|
||||||
|
@ -37,7 +37,7 @@ By setting the `transparent` option to `true`, you can also make the frameless
|
||||||
window transparent:
|
window transparent:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
let win = new BrowserWindow({transparent: true, frame: false});
|
let win = new BrowserWindow({transparent: true, frame: false})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
@ -59,6 +59,16 @@ let win = new BrowserWindow({transparent: true, frame: false});
|
||||||
Linux.
|
Linux.
|
||||||
* On Mac the native window shadow will not be shown on a transparent window.
|
* 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
|
## Draggable region
|
||||||
|
|
||||||
By default, the frameless window is non-draggable. Apps need to specify
|
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
|
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
|
behave correctly on all platforms you should never use a custom context menu on
|
||||||
draggable areas.
|
draggable areas.
|
||||||
|
|
||||||
|
[ignore-mouse-events]: browser-window.md#winsetignoremouseeventsignore
|
||||||
|
|
|
@ -46,7 +46,9 @@ The `role` property can have following values:
|
||||||
* `cut`
|
* `cut`
|
||||||
* `copy`
|
* `copy`
|
||||||
* `paste`
|
* `paste`
|
||||||
|
* `pasteandmatchstyle`
|
||||||
* `selectall`
|
* `selectall`
|
||||||
|
* `delete`
|
||||||
* `minimize` - Minimize current window
|
* `minimize` - Minimize current window
|
||||||
* `close` - Close current window
|
* `close` - Close current window
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,12 @@ Creates an empty `nativeImage` instance.
|
||||||
|
|
||||||
Creates a new `nativeImage` instance from a file located at `path`.
|
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])`
|
### `nativeImage.createFromBuffer(buffer[, scaleFactor])`
|
||||||
|
|
||||||
* `buffer` [Buffer][buffer]
|
* `buffer` [Buffer][buffer]
|
||||||
|
@ -121,12 +127,6 @@ Creates a new `nativeImage` instance from `dataURL`.
|
||||||
|
|
||||||
The following methods are available on instances of `nativeImage`:
|
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()`
|
### `image.toPng()`
|
||||||
|
|
||||||
Returns a [Buffer][buffer] that contains the image's `PNG` encoded data.
|
Returns a [Buffer][buffer] that contains the image's `PNG` encoded data.
|
||||||
|
|
|
@ -80,7 +80,7 @@ exports.withLocalCallback = () => {
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// renderer process
|
// renderer process
|
||||||
const mapNumbers = require('remote').require('./mapNumbers');
|
const mapNumbers = require('electron').remote.require('./mapNumbers');
|
||||||
|
|
||||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
||||||
|
|
||||||
|
|
|
@ -9,44 +9,42 @@ emitted (by invoking or requiring it).
|
||||||
|
|
||||||
**Note:** In the renderer / DevTools, `window.screen` is a reserved DOM
|
**Note:** In the renderer / DevTools, `window.screen` is a reserved DOM
|
||||||
property, so writing `let {screen} = require('electron')` will not work.
|
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:
|
An example of creating a window that fills the whole screen:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const {app, BrowserWindow, screen: electronScreen} = require('electron');
|
const electron = require('electron')
|
||||||
|
const {app, BrowserWindow} = electron
|
||||||
|
|
||||||
let win;
|
let win
|
||||||
|
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
const {width, height} = electronScreen.getPrimaryDisplay().workAreaSize;
|
const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
|
||||||
win = new BrowserWindow({width, height});
|
win = new BrowserWindow({width, height})
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Another example of creating a window in the external display:
|
Another example of creating a window in the external display:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const {app, BrowserWindow, screen: electronScreen} = require('electron');
|
const electron = require('electron')
|
||||||
|
const {app, BrowserWindow} = require('electron')
|
||||||
|
|
||||||
let win;
|
let win
|
||||||
|
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
let displays = electronScreen.getAllDisplays();
|
let displays = electron.screen.getAllDisplays()
|
||||||
let externalDisplay = null;
|
let externalDisplay = displays.find((display) => {
|
||||||
for (let i in displays) {
|
return display.bounds.x !== 0 || display.bounds.y !== 0
|
||||||
if (displays[i].bounds.x !== 0 || displays[i].bounds.y !== 0) {
|
})
|
||||||
externalDisplay = displays[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (externalDisplay) {
|
if (externalDisplay) {
|
||||||
win = new BrowserWindow({
|
win = new BrowserWindow({
|
||||||
x: externalDisplay.bounds.x + 50,
|
x: externalDisplay.bounds.x + 50,
|
||||||
y: externalDisplay.bounds.y + 50
|
y: externalDisplay.bounds.y + 50
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
## The `Display` object
|
## The `Display` object
|
||||||
|
|
|
@ -206,7 +206,7 @@ Sets the proxy settings.
|
||||||
When `pacScript` and `proxyRules` are provided together, the `proxyRules`
|
When `pacScript` and `proxyRules` are provided together, the `proxyRules`
|
||||||
option is ignored and `pacScript` configuration is applied.
|
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[";"<schemeProxies>]
|
proxyRules = schemeProxies[";"<schemeProxies>]
|
||||||
|
@ -547,3 +547,22 @@ The `listener` will be called with `listener(details)` when an error occurs.
|
||||||
* `timestamp` Double
|
* `timestamp` Double
|
||||||
* `fromCache` Boolean
|
* `fromCache` Boolean
|
||||||
* `error` String - The error description.
|
* `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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -29,12 +29,12 @@ __Platform limitations:__
|
||||||
* When app indicator is used on Linux, the `click` event is ignored.
|
* 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,
|
* On Linux in order for changes made to individual `MenuItem`s to take effect,
|
||||||
you have to call `setContextMenu` again. For example:
|
you have to call `setContextMenu` again. For example:
|
||||||
* On Windows it is recommended to use `ICO` icons to get best visual effects.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
contextMenu.items[2].checked = false;
|
contextMenu.items[2].checked = false;
|
||||||
appIcon.setContextMenu(contextMenu);
|
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
|
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.
|
rely on the `click` event and always attach a context menu to the tray icon.
|
||||||
|
|
|
@ -288,6 +288,15 @@ a meta tag:
|
||||||
<meta name='theme-color' content='#ff0000'>
|
<meta name='theme-color' content='#ff0000'>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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'
|
### Event: 'cursor-changed'
|
||||||
|
|
||||||
Returns:
|
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
|
## Instance Properties
|
||||||
|
|
||||||
`WebContents` objects also have the following properties:
|
`WebContents` objects also have the following properties:
|
||||||
|
|
|
@ -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 `,`.
|
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
|
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
|
||||||
|
<webview src="https://www.github.com/" disableblinkfeatures="PreciseMemoryInfo, CSSVariables"></webview>
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
## Methods
|
||||||
|
|
||||||
|
@ -480,6 +490,10 @@ Sends an input `event` to the page.
|
||||||
See [webContents.sendInputEvent](web-contents.md##webcontentssendinputeventevent)
|
See [webContents.sendInputEvent](web-contents.md##webcontentssendinputeventevent)
|
||||||
for detailed description of `event` object.
|
for detailed description of `event` object.
|
||||||
|
|
||||||
|
### `<webview>.showDefinitionForSelection()` _OS X_
|
||||||
|
|
||||||
|
Shows pop-up dictionary that searches the selected word on the page.
|
||||||
|
|
||||||
### `<webview>.getWebContents()`
|
### `<webview>.getWebContents()`
|
||||||
|
|
||||||
Returns the [WebContents](web-contents.md) associated with this `webview`.
|
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
|
||||||
<meta name='theme-color' content='#ff0000'>
|
<meta name='theme-color' content='#ff0000'>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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'
|
### Event: 'devtools-opened'
|
||||||
|
|
||||||
Emitted when DevTools is opened.
|
Emitted when DevTools is opened.
|
||||||
|
@ -794,4 +816,4 @@ Emitted when DevTools is closed.
|
||||||
|
|
||||||
Emitted when DevTools is focused / opened.
|
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
|
||||||
|
|
|
@ -8,7 +8,7 @@ be found in the `.gyp` and `.gypi` files.
|
||||||
|
|
||||||
Following `gyp` files contain the main rules for building Electron:
|
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
|
* `common.gypi` adjusts the build configurations of Node to make it build
|
||||||
together with Chromium.
|
together with Chromium.
|
||||||
* `vendor/brightray/brightray.gyp` defines how `brightray` is built and
|
* `vendor/brightray/brightray.gyp` defines how `brightray` is built and
|
||||||
|
|
|
@ -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
|
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
|
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
|
```javascript
|
||||||
// In the main process.
|
// In the main process.
|
||||||
|
@ -41,12 +41,12 @@ global.sharedObject = {
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In page 1.
|
// In page 1.
|
||||||
require('remote').getGlobal('sharedObject').someProperty = 'new value';
|
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value';
|
||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In page 2.
|
// 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.
|
## My app's window/tray disappeared after a few minutes.
|
||||||
|
|
|
@ -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
|
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.
|
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
|
### Upload Your App
|
||||||
|
|
||||||
After signing your app, you can use Application Loader to upload it to iTunes
|
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
|
[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
|
[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
|
[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
|
[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/
|
[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/
|
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||||
|
|
|
@ -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 `<webview>` Tag
|
## Enable Flash Plugin in a `<webview>` Tag
|
||||||
|
|
||||||
Add `plugins` attribute to `<webview>` tag.
|
Add `plugins` attribute to `<webview>` tag.
|
||||||
|
|
|
@ -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,
|
powerful APIs like Cortana or Push Notifications, but through the Windows Store,
|
||||||
also simplifies installation and updating.
|
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
|
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
|
model. This guide explains how to use it - and what the capabilities and
|
||||||
limitations of an Electron AppX package are.
|
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
|
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
|
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
|
capabilities, an Electron app can pair up with an invisible UWP background task
|
||||||
together with the `exe` - sort of launched as a sidekick to run tasks in the
|
launched together with the `exe` - sort of launched as a sidekick to run tasks
|
||||||
background, receive push notifications, or to communicate with other UWP
|
in the background, receive push notifications, or to communicate with other UWP
|
||||||
applications.
|
applications.
|
||||||
|
|
||||||
To compile any existing Electron app, ensure that you have the following
|
To compile any existing Electron app, ensure that you have the following
|
||||||
requirements:
|
requirements:
|
||||||
|
|
||||||
* Windows 10 Anniversary Update - Enterprise Edition (This is build 14316 and up
|
* Windows 10 Anniversary Update (until the update is released to the general public,
|
||||||
- as of May 2016, it's part of the Windows Insiders Preview)
|
developers can use the Windows Insider Preview)
|
||||||
* A machine with 64 bit (x64) processor, Hardware-Assisted Virtualization, and
|
* The Windows 10 SDK, [downloadable here][windows-sdk]
|
||||||
Second Level Address Translation (SLAT)
|
|
||||||
* The Windows 10 SDK, [downloadable here](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
|
|
||||||
* At least Node 4 (to check, run `node -v`)
|
* At least Node 4 (to check, run `node -v`)
|
||||||
|
|
||||||
Then, go and install the `electron-windows-store` CLI:
|
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
|
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
|
Package the application using [electron-packager][electron-packager] (or a similar tool).
|
||||||
Desktop App Converter". This will take a few minutes, but don't worry - you only
|
Make sure to remove `node_modules` that you don't need in your final application, since
|
||||||
have to do this once. Download and Desktop App Converter from
|
any module you don't actually need will just increase your application's size.
|
||||||
[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.
|
|
||||||
|
|
||||||
The output should look roughly like this:
|
The output should look roughly like this:
|
||||||
|
|
||||||
|
@ -101,7 +78,7 @@ The output should look roughly like this:
|
||||||
└── xinput1_3.dll
|
└── xinput1_3.dll
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running the Command Line Tool
|
## Step 2: Running electron-windows-store
|
||||||
|
|
||||||
From an elevated PowerShell (run it "as Administrator"), run
|
From an elevated PowerShell (run it "as Administrator"), run
|
||||||
`electron-windows-store` with the required parameters, passing both the input
|
`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
|
to sign the new AppX pacakge. With the signed AppX package, the CLI can also
|
||||||
automatically install the package on your machine.
|
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
|
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
|
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`
|
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.
|
in developer or enterprise environments.
|
||||||
|
|
||||||
Another important limitation is that the compiled AppX package still contains a
|
Another important limitation is that the compiled AppX package still contains a
|
||||||
win32 executable - and will therefore not run on Xbox, HoloLens, or Phones.
|
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
|
|
@ -4,7 +4,7 @@
|
||||||
'product_name%': 'Electron',
|
'product_name%': 'Electron',
|
||||||
'company_name%': 'GitHub, Inc',
|
'company_name%': 'GitHub, Inc',
|
||||||
'company_abbr%': 'github',
|
'company_abbr%': 'github',
|
||||||
'version%': '1.2.1',
|
'version%': '1.2.2',
|
||||||
},
|
},
|
||||||
'includes': [
|
'includes': [
|
||||||
'filenames.gypi',
|
'filenames.gypi',
|
||||||
|
|
|
@ -63,6 +63,8 @@
|
||||||
'lib/renderer/api/remote.js',
|
'lib/renderer/api/remote.js',
|
||||||
'lib/renderer/api/screen.js',
|
'lib/renderer/api/screen.js',
|
||||||
'lib/renderer/api/web-frame.js',
|
'lib/renderer/api/web-frame.js',
|
||||||
|
'lib/renderer/extensions/i18n.js',
|
||||||
|
'lib/renderer/extensions/storage.js',
|
||||||
],
|
],
|
||||||
'js2c_sources': [
|
'js2c_sources': [
|
||||||
'lib/common/asar.js',
|
'lib/common/asar.js',
|
||||||
|
|
|
@ -151,6 +151,9 @@ Object.assign(BrowserWindow.prototype, {
|
||||||
},
|
},
|
||||||
inspectServiceWorker () {
|
inspectServiceWorker () {
|
||||||
return this.webContents.inspectServiceWorker()
|
return this.webContents.inspectServiceWorker()
|
||||||
|
},
|
||||||
|
showDefinitionForSelection () {
|
||||||
|
return this.webContents.showDefinitionForSelection()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ rolesMap = {
|
||||||
cut: 'cut',
|
cut: 'cut',
|
||||||
copy: 'copy',
|
copy: 'copy',
|
||||||
paste: 'paste',
|
paste: 'paste',
|
||||||
|
pasteandmatchstyle: 'pasteAndMatchStyle',
|
||||||
selectall: 'selectAll',
|
selectall: 'selectAll',
|
||||||
minimize: 'minimize',
|
minimize: 'minimize',
|
||||||
close: 'close',
|
close: 'close',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const {app} = require('electron')
|
const {app, session} = require('electron')
|
||||||
const {createProtocolObject, registerStandardSchemes} = process.atomBinding('protocol')
|
const {registerStandardSchemes} = process.atomBinding('protocol')
|
||||||
|
|
||||||
exports.registerStandardSchemes = function (schemes) {
|
exports.registerStandardSchemes = function (schemes) {
|
||||||
if (app.isReady()) {
|
if (app.isReady()) {
|
||||||
|
@ -10,7 +10,7 @@ exports.registerStandardSchemes = function (schemes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.once('ready', function () {
|
app.once('ready', function () {
|
||||||
let protocol = createProtocolObject()
|
let protocol = session.defaultSession.protocol
|
||||||
for (let method in protocol) {
|
for (let method in protocol) {
|
||||||
exports[method] = protocol[method].bind(protocol)
|
exports[method] = protocol[method].bind(protocol)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ const electron = require('electron')
|
||||||
const bindings = process.atomBinding('session')
|
const bindings = process.atomBinding('session')
|
||||||
|
|
||||||
const PERSIST_PREFIX = 'persist:'
|
const PERSIST_PREFIX = 'persist:'
|
||||||
|
const Session = new EventEmitter()
|
||||||
|
|
||||||
// Wrapper of binding.fromPartition that checks for ready event.
|
// Wrapper of binding.fromPartition that checks for ready event.
|
||||||
const fromPartition = function (partition, persist) {
|
const fromPartition = function (partition, persist) {
|
||||||
|
@ -14,7 +15,7 @@ const fromPartition = function (partition, persist) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the Session from |partition| string.
|
// Returns the Session from |partition| string.
|
||||||
exports.fromPartition = function (partition = '') {
|
Session.fromPartition = function (partition = '') {
|
||||||
if (partition === '') return exports.defaultSession
|
if (partition === '') return exports.defaultSession
|
||||||
|
|
||||||
if (partition.startsWith(PERSIST_PREFIX)) {
|
if (partition.startsWith(PERSIST_PREFIX)) {
|
||||||
|
@ -25,7 +26,7 @@ exports.fromPartition = function (partition = '') {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the default session.
|
// Returns the default session.
|
||||||
Object.defineProperty(exports, 'defaultSession', {
|
Object.defineProperty(Session, 'defaultSession', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: function () {
|
get: function () {
|
||||||
return fromPartition('', false)
|
return fromPartition('', false)
|
||||||
|
@ -35,6 +36,9 @@ Object.defineProperty(exports, 'defaultSession', {
|
||||||
const wrapSession = function (session) {
|
const wrapSession = function (session) {
|
||||||
// Session is an EventEmitter.
|
// Session is an EventEmitter.
|
||||||
Object.setPrototypeOf(session, EventEmitter.prototype)
|
Object.setPrototypeOf(session, EventEmitter.prototype)
|
||||||
|
Session.emit('session-created', session)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings._setWrapSession(wrapSession)
|
bindings._setWrapSession(wrapSession)
|
||||||
|
|
||||||
|
module.exports = Session
|
||||||
|
|
|
@ -10,6 +10,14 @@ session
|
||||||
const binding = process.atomBinding('web_contents')
|
const binding = process.atomBinding('web_contents')
|
||||||
const debuggerBinding = process.atomBinding('debugger')
|
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
|
let nextId = 0
|
||||||
const getNextId = function () {
|
const getNextId = function () {
|
||||||
return ++nextId
|
return ++nextId
|
||||||
|
@ -223,6 +231,8 @@ const wrapWebContents = function (webContents) {
|
||||||
|
|
||||||
this._printToPDF(printingSetting, callback)
|
this._printToPDF(printingSetting, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebContents.emit('web-contents-created', webContents)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding._setWrapWebContents(wrapWebContents)
|
binding._setWrapWebContents(wrapWebContents)
|
||||||
|
@ -235,12 +245,4 @@ const wrapDebugger = function (webContentsDebugger) {
|
||||||
|
|
||||||
debuggerBinding._setWrapDebugger(wrapDebugger)
|
debuggerBinding._setWrapDebugger(wrapDebugger)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = WebContents
|
||||||
create (options = {}) {
|
|
||||||
return binding.create(options)
|
|
||||||
},
|
|
||||||
|
|
||||||
fromId (id) {
|
|
||||||
return binding.fromId(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 renderProcessPreferences = process.atomBinding('render_process_preferences').forAllBrowserWindow()
|
||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
@ -20,10 +21,27 @@ const generateExtensionIdFromName = function (name) {
|
||||||
|
|
||||||
// Create or get manifest object from |srcDirectory|.
|
// Create or get manifest object from |srcDirectory|.
|
||||||
const getManifestFromPath = function (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]) {
|
if (!manifestNameMap[manifest.name]) {
|
||||||
const extensionId = generateExtensionIdFromName(manifest.name)
|
const extensionId = generateExtensionIdFromName(manifest.name)
|
||||||
console.log(extensionId)
|
|
||||||
manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
|
manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
|
||||||
Object.assign(manifest, {
|
Object.assign(manifest, {
|
||||||
srcDirectory: srcDirectory,
|
srcDirectory: srcDirectory,
|
||||||
|
@ -38,6 +56,8 @@ const getManifestFromPath = function (srcDirectory) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return manifest
|
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.
|
// Dispatch tabs events.
|
||||||
const hookWindowForTabEvents = function (win) {
|
const hookWebContentsForTabEvents = function (webContents) {
|
||||||
const tabId = win.webContents.id
|
const tabId = webContents.id
|
||||||
for (const page of objectValues(backgroundPages)) {
|
for (const page of objectValues(backgroundPages)) {
|
||||||
page.webContents.sendToAll('CHROME_TABS_ONCREATED', tabId)
|
page.webContents.sendToAll('CHROME_TABS_ONCREATED', tabId)
|
||||||
}
|
}
|
||||||
|
|
||||||
win.once('closed', () => {
|
webContents.once('destroyed', () => {
|
||||||
for (const page of objectValues(backgroundPages)) {
|
for (const page of objectValues(backgroundPages)) {
|
||||||
page.webContents.sendToAll('CHROME_TABS_ONREMOVED', tabId)
|
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)
|
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) {
|
ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message) {
|
||||||
const page = backgroundPages[extensionId]
|
const page = backgroundPages[extensionId]
|
||||||
if (!page) {
|
if (!page) {
|
||||||
|
@ -212,6 +236,15 @@ const loadDevToolsExtensions = function (win, manifests) {
|
||||||
win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
|
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.
|
// The persistent path of "DevTools Extensions" preference file.
|
||||||
let loadedExtensionsPath = null
|
let loadedExtensionsPath = null
|
||||||
|
|
||||||
|
@ -261,10 +294,12 @@ app.once('ready', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
session.on('session-created', function (ses) {
|
||||||
if (error) {
|
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
if (error) {
|
||||||
}
|
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Load persisted extensions.
|
// Load persisted extensions.
|
||||||
|
@ -286,12 +321,15 @@ app.once('ready', function () {
|
||||||
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
|
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
|
||||||
const manifest = getManifestFromPath(srcDirectory)
|
const manifest = getManifestFromPath(srcDirectory)
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
for (const win of BrowserWindow.getAllWindows()) {
|
for (const webContents of getAllWebContents()) {
|
||||||
loadDevToolsExtensions(win, [manifest])
|
if (webContents.getType() !== 'remote') {
|
||||||
|
loadDevToolsExtensions(webContents, [manifest])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return manifest.name
|
return manifest.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserWindow.removeDevToolsExtension = function (name) {
|
BrowserWindow.removeDevToolsExtension = function (name) {
|
||||||
const manifest = manifestNameMap[name]
|
const manifest = manifestNameMap[name]
|
||||||
if (!manifest) return
|
if (!manifest) return
|
||||||
|
@ -301,14 +339,4 @@ app.once('ready', function () {
|
||||||
delete manifestMap[manifest.extensionId]
|
delete manifestMap[manifest.extensionId]
|
||||||
delete manifestNameMap[name]
|
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))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,9 +4,9 @@ const ipcMain = require('electron').ipcMain
|
||||||
const webContents = require('electron').webContents
|
const webContents = require('electron').webContents
|
||||||
|
|
||||||
// Doesn't exist in early initialization.
|
// Doesn't exist in early initialization.
|
||||||
var webViewManager = null
|
let webViewManager = null
|
||||||
|
|
||||||
var supportedWebViewEvents = [
|
const supportedWebViewEvents = [
|
||||||
'load-commit',
|
'load-commit',
|
||||||
'did-finish-load',
|
'did-finish-load',
|
||||||
'did-fail-load',
|
'did-fail-load',
|
||||||
|
@ -36,32 +36,33 @@ var supportedWebViewEvents = [
|
||||||
'media-started-playing',
|
'media-started-playing',
|
||||||
'media-paused',
|
'media-paused',
|
||||||
'found-in-page',
|
'found-in-page',
|
||||||
'did-change-theme-color'
|
'did-change-theme-color',
|
||||||
|
'update-target-url'
|
||||||
]
|
]
|
||||||
|
|
||||||
var nextInstanceId = 0
|
let nextInstanceId = 0
|
||||||
var guestInstances = {}
|
const guestInstances = {}
|
||||||
var embedderElementsMap = {}
|
const embedderElementsMap = {}
|
||||||
var reverseEmbedderElementsMap = {}
|
const reverseEmbedderElementsMap = {}
|
||||||
|
|
||||||
// Moves the last element of array to the first one.
|
// Moves the last element of array to the first one.
|
||||||
var moveLastToFirst = function (list) {
|
const moveLastToFirst = function (list) {
|
||||||
return list.unshift(list.pop())
|
return list.unshift(list.pop())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate guestInstanceId.
|
// Generate guestInstanceId.
|
||||||
var getNextInstanceId = function () {
|
const getNextInstanceId = function () {
|
||||||
return ++nextInstanceId
|
return ++nextInstanceId
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new guest instance.
|
// Create a new guest instance.
|
||||||
var createGuest = function (embedder, params) {
|
const createGuest = function (embedder, params) {
|
||||||
var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners
|
|
||||||
if (webViewManager == null) {
|
if (webViewManager == null) {
|
||||||
webViewManager = process.atomBinding('web_view_manager')
|
webViewManager = process.atomBinding('web_view_manager')
|
||||||
}
|
}
|
||||||
id = getNextInstanceId(embedder)
|
|
||||||
guest = webContents.create({
|
const id = getNextInstanceId(embedder)
|
||||||
|
const guest = webContents.create({
|
||||||
isGuest: true,
|
isGuest: true,
|
||||||
partition: params.partition,
|
partition: params.partition,
|
||||||
embedder: embedder
|
embedder: embedder
|
||||||
|
@ -72,37 +73,32 @@ var createGuest = function (embedder, params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy guest when the embedder is gone or navigated.
|
// Destroy guest when the embedder is gone or navigated.
|
||||||
destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
|
const destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
|
||||||
destroy = function () {
|
const destroy = function () {
|
||||||
if (guestInstances[id] != null) {
|
if (guestInstances[id] != null) {
|
||||||
return destroyGuest(embedder, id)
|
destroyGuest(embedder, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0, len = destroyEvents.length; i < len; i++) {
|
for (const event of destroyEvents) {
|
||||||
event = destroyEvents[i]
|
|
||||||
embedder.once(event, destroy)
|
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
|
// is destroyed before users' listener gets called. It is done by moving our
|
||||||
// listener to the first one in queue.
|
// listener to the first one in queue.
|
||||||
listeners = embedder._events[event]
|
const listeners = embedder._events[event]
|
||||||
if (Array.isArray(listeners)) {
|
if (Array.isArray(listeners)) {
|
||||||
moveLastToFirst(listeners)
|
moveLastToFirst(listeners)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
guest.once('destroyed', function () {
|
guest.once('destroyed', function () {
|
||||||
var j, len1, results
|
for (const event of destroyEvents) {
|
||||||
results = []
|
embedder.removeListener(event, destroy)
|
||||||
for (j = 0, len1 = destroyEvents.length; j < len1; j++) {
|
|
||||||
event = destroyEvents[j]
|
|
||||||
results.push(embedder.removeListener(event, destroy))
|
|
||||||
}
|
}
|
||||||
return results
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Init guest web view after attached.
|
// Init guest web view after attached.
|
||||||
guest.once('did-attach', function () {
|
guest.once('did-attach', function () {
|
||||||
var opts
|
let opts
|
||||||
params = this.attachParams
|
params = this.attachParams
|
||||||
delete this.attachParams
|
delete this.attachParams
|
||||||
this.viewInstanceId = params.instanceId
|
this.viewInstanceId = params.instanceId
|
||||||
|
@ -135,13 +131,12 @@ var createGuest = function (embedder, params) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Dispatch events to embedder.
|
// Dispatch events to embedder.
|
||||||
fn = function (event) {
|
const fn = function (event) {
|
||||||
return guest.on(event, function (_, ...args) {
|
guest.on(event, function (_, ...args) {
|
||||||
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(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++) {
|
for (const event of supportedWebViewEvents) {
|
||||||
event = supportedWebViewEvents[j]
|
|
||||||
fn(event)
|
fn(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,12 +149,13 @@ var createGuest = function (embedder, params) {
|
||||||
guest.on('size-changed', function (_, ...args) {
|
guest.on('size-changed', function (_, ...args) {
|
||||||
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args))
|
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args))
|
||||||
})
|
})
|
||||||
|
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the guest to an element of embedder.
|
// Attach the guest to an element of embedder.
|
||||||
var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
|
const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
|
||||||
var guest, key, oldGuestInstanceId, ref1, webPreferences
|
let guest, key, oldGuestInstanceId, ref1, webPreferences
|
||||||
guest = guestInstances[guestInstanceId].guest
|
guest = guestInstances[guestInstanceId].guest
|
||||||
|
|
||||||
// Destroy the old guest when attaching.
|
// Destroy the old guest when attaching.
|
||||||
|
@ -181,7 +177,8 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params
|
||||||
plugins: params.plugins,
|
plugins: params.plugins,
|
||||||
zoomFactor: params.zoomFactor,
|
zoomFactor: params.zoomFactor,
|
||||||
webSecurity: !params.disablewebsecurity,
|
webSecurity: !params.disablewebsecurity,
|
||||||
blinkFeatures: params.blinkfeatures
|
blinkFeatures: params.blinkfeatures,
|
||||||
|
disableBlinkFeatures: params.disableblinkfeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.preload) {
|
if (params.preload) {
|
||||||
|
@ -194,12 +191,12 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy an existing guest instance.
|
// Destroy an existing guest instance.
|
||||||
var destroyGuest = function (embedder, id) {
|
const destroyGuest = function (embedder, id) {
|
||||||
var key
|
|
||||||
webViewManager.removeGuest(embedder, id)
|
webViewManager.removeGuest(embedder, id)
|
||||||
guestInstances[id].guest.destroy()
|
guestInstances[id].guest.destroy()
|
||||||
delete guestInstances[id]
|
delete guestInstances[id]
|
||||||
key = reverseEmbedderElementsMap[id]
|
|
||||||
|
const key = reverseEmbedderElementsMap[id]
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
delete reverseEmbedderElementsMap[id]
|
delete reverseEmbedderElementsMap[id]
|
||||||
return delete embedderElementsMap[key]
|
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) {
|
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, id, params) {
|
||||||
var ref1
|
const guestInstance = guestInstances[id]
|
||||||
return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0
|
return guestInstance != null ? guestInstance.guest.setSize(params) : void 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// Returns WebContents from its guest id.
|
// Returns WebContents from its guest id.
|
||||||
exports.getGuest = function (id) {
|
exports.getGuest = function (id) {
|
||||||
var ref1
|
const guestInstance = guestInstances[id]
|
||||||
return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0
|
return guestInstance != null ? guestInstance.guest : void 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the embedder of the guest.
|
// Returns the embedder of the guest.
|
||||||
exports.getEmbedder = function (id) {
|
exports.getEmbedder = function (id) {
|
||||||
var ref1
|
const guestInstance = guestInstances[id]
|
||||||
return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0
|
return guestInstance != null ? guestInstance.embedder : void 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const ipcMain = require('electron').ipcMain
|
const {BrowserWindow, ipcMain, webContents} = require('electron')
|
||||||
const BrowserWindow = require('electron').BrowserWindow
|
|
||||||
|
|
||||||
var hasProp = {}.hasOwnProperty
|
const hasProp = {}.hasOwnProperty
|
||||||
var frameToGuest = {}
|
const frameToGuest = {}
|
||||||
|
|
||||||
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||||
var mergeOptions = function (child, parent) {
|
const mergeOptions = function (child, parent) {
|
||||||
var key, value
|
let key, value
|
||||||
for (key in parent) {
|
for (key in parent) {
|
||||||
if (!hasProp.call(parent, key)) continue
|
if (!hasProp.call(parent, key)) continue
|
||||||
value = parent[key]
|
value = parent[key]
|
||||||
|
@ -24,7 +23,7 @@ var mergeOptions = function (child, parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge |options| with the |embedder|'s window's options.
|
// Merge |options| with the |embedder|'s window's options.
|
||||||
var mergeBrowserWindowOptions = function (embedder, options) {
|
const mergeBrowserWindowOptions = function (embedder, options) {
|
||||||
if (embedder.browserWindowOptions != null) {
|
if (embedder.browserWindowOptions != null) {
|
||||||
// Inherit the original options if it is a BrowserWindow.
|
// Inherit the original options if it is a BrowserWindow.
|
||||||
mergeOptions(options, embedder.browserWindowOptions)
|
mergeOptions(options, embedder.browserWindowOptions)
|
||||||
|
@ -45,9 +44,8 @@ var mergeBrowserWindowOptions = function (embedder, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new guest created by |embedder| with |options|.
|
// Create a new guest created by |embedder| with |options|.
|
||||||
var createGuest = function (embedder, url, frameName, options) {
|
const createGuest = function (embedder, url, frameName, options) {
|
||||||
var closedByEmbedder, closedByUser, guest, guestId, ref1
|
let guest = frameToGuest[frameName]
|
||||||
guest = frameToGuest[frameName]
|
|
||||||
if (frameName && (guest != null)) {
|
if (frameName && (guest != null)) {
|
||||||
guest.loadURL(url)
|
guest.loadURL(url)
|
||||||
return guest.id
|
return guest.id
|
||||||
|
@ -57,24 +55,26 @@ var createGuest = function (embedder, url, frameName, options) {
|
||||||
if (options.webPreferences == null) {
|
if (options.webPreferences == null) {
|
||||||
options.webPreferences = {}
|
options.webPreferences = {}
|
||||||
}
|
}
|
||||||
options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0
|
options.webPreferences.openerId = embedder.id
|
||||||
guest = new BrowserWindow(options)
|
guest = new BrowserWindow(options)
|
||||||
guest.loadURL(url)
|
guest.loadURL(url)
|
||||||
|
|
||||||
// When |embedder| is destroyed we should also destroy attached guest, and if
|
// When |embedder| is destroyed we should also destroy attached guest, and if
|
||||||
// guest is closed by user then we should prevent |embedder| from double
|
// guest is closed by user then we should prevent |embedder| from double
|
||||||
// closing guest.
|
// closing guest.
|
||||||
guestId = guest.id
|
const guestId = guest.webContents.id
|
||||||
closedByEmbedder = function () {
|
|
||||||
|
const closedByEmbedder = function () {
|
||||||
guest.removeListener('closed', closedByUser)
|
guest.removeListener('closed', closedByUser)
|
||||||
return guest.destroy()
|
guest.destroy()
|
||||||
}
|
}
|
||||||
closedByUser = function () {
|
const closedByUser = function () {
|
||||||
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
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)
|
embedder.once('render-view-deleted', closedByEmbedder)
|
||||||
guest.once('closed', closedByUser)
|
guest.once('closed', closedByUser)
|
||||||
|
|
||||||
if (frameName) {
|
if (frameName) {
|
||||||
frameToGuest[frameName] = guest
|
frameToGuest[frameName] = guest
|
||||||
guest.frameName = frameName
|
guest.frameName = frameName
|
||||||
|
@ -82,7 +82,22 @@ var createGuest = function (embedder, url, frameName, options) {
|
||||||
delete frameToGuest[frameName]
|
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.
|
// 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) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
|
||||||
var ref1
|
const guestWindow = getGuestWindow(guestId)
|
||||||
(ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0
|
if (guestWindow != null) guestWindow.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
|
||||||
var ref1
|
const guestWindow = getGuestWindow(guestId)
|
||||||
event.returnValue = (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0
|
event.returnValue = guestWindow != null ? guestWindow[method].apply(guestWindow, args) : null
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
|
||||||
var guestContents, ref1, ref2, sourceId
|
const guestContents = webContents.fromId(guestId)
|
||||||
sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0
|
if (guestContents == null) return
|
||||||
if (sourceId == null) {
|
|
||||||
return
|
if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
|
||||||
}
|
const sourceId = event.sender.id
|
||||||
guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0
|
guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
|
||||||
if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') {
|
|
||||||
guestContents != null ? guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
|
||||||
var ref1, ref2
|
const guestContents = webContents.fromId(guestId)
|
||||||
(ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0
|
if (guestContents != null) guestContents[method].apply(guestContents, args)
|
||||||
})
|
})
|
||||||
|
|
|
@ -99,6 +99,8 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
||||||
})
|
})
|
||||||
|
|
||||||
chrome.runtime = {
|
chrome.runtime = {
|
||||||
|
id: extensionId,
|
||||||
|
|
||||||
getURL: function (path) {
|
getURL: function (path) {
|
||||||
return url.format({
|
return url.format({
|
||||||
protocol: 'chrome-extension',
|
protocol: 'chrome-extension',
|
||||||
|
@ -181,12 +183,7 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
||||||
onMessage: chrome.runtime.onMessage
|
onMessage: chrome.runtime.onMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.storage = {
|
chrome.storage = require('./extensions/storage.js')
|
||||||
sync: {
|
|
||||||
get () {},
|
|
||||||
set () {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.pageAction = {
|
chrome.pageAction = {
|
||||||
show () {},
|
show () {},
|
||||||
|
@ -197,4 +194,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
||||||
setPopup () {},
|
setPopup () {},
|
||||||
getPopup () {}
|
getPopup () {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chrome.i18n = require('./extensions/i18n.js').setup(extensionId)
|
||||||
}
|
}
|
||||||
|
|
84
lib/renderer/extensions/i18n.js
Normal file
84
lib/renderer/extensions/i18n.js
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
lib/renderer/extensions/storage.js
Normal file
59
lib/renderer/extensions/storage.js
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,13 @@ var BrowserWindowProxy = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function BrowserWindowProxy (guestId1) {
|
function BrowserWindowProxy (guestId1) {
|
||||||
this.guestId = guestId1
|
Object.defineProperty(this, 'guestId', {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
writeable: false,
|
||||||
|
value: guestId1
|
||||||
|
})
|
||||||
|
|
||||||
this.closed = false
|
this.closed = false
|
||||||
ipcRenderer.once('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + this.guestId, () => {
|
ipcRenderer.once('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + this.guestId, () => {
|
||||||
BrowserWindowProxy.remove(this.guestId)
|
BrowserWindowProxy.remove(this.guestId)
|
||||||
|
|
|
@ -27,15 +27,16 @@ var WEB_VIEW_EVENTS = {
|
||||||
'crashed': [],
|
'crashed': [],
|
||||||
'gpu-crashed': [],
|
'gpu-crashed': [],
|
||||||
'plugin-crashed': ['name', 'version'],
|
'plugin-crashed': ['name', 'version'],
|
||||||
'media-started-playing': [],
|
|
||||||
'media-paused': [],
|
|
||||||
'did-change-theme-color': ['themeColor'],
|
|
||||||
'destroyed': [],
|
'destroyed': [],
|
||||||
'page-title-updated': ['title', 'explicitSet'],
|
'page-title-updated': ['title', 'explicitSet'],
|
||||||
'page-favicon-updated': ['favicons'],
|
'page-favicon-updated': ['favicons'],
|
||||||
'enter-html-full-screen': [],
|
'enter-html-full-screen': [],
|
||||||
'leave-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 = {
|
var DEPRECATED_EVENTS = {
|
||||||
|
|
|
@ -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.
|
// Sets up all of the webview attributes.
|
||||||
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
||||||
this.attributes = {}
|
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_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
|
||||||
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
|
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
|
||||||
this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(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]
|
const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]
|
||||||
autosizeAttributes.forEach((attribute) => {
|
autosizeAttributes.forEach((attribute) => {
|
||||||
|
|
|
@ -16,6 +16,7 @@ module.exports = {
|
||||||
ATTRIBUTE_PRELOAD: 'preload',
|
ATTRIBUTE_PRELOAD: 'preload',
|
||||||
ATTRIBUTE_USERAGENT: 'useragent',
|
ATTRIBUTE_USERAGENT: 'useragent',
|
||||||
ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
|
ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
|
||||||
|
ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',
|
||||||
|
|
||||||
// Internal attribute.
|
// Internal attribute.
|
||||||
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',
|
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',
|
||||||
|
|
|
@ -379,7 +379,8 @@ var registerWebViewElement = function () {
|
||||||
'downloadURL',
|
'downloadURL',
|
||||||
'inspectServiceWorker',
|
'inspectServiceWorker',
|
||||||
'print',
|
'print',
|
||||||
'printToPDF'
|
'printToPDF',
|
||||||
|
'showDefinitionForSelection'
|
||||||
]
|
]
|
||||||
nonblockMethods = [
|
nonblockMethods = [
|
||||||
'insertCSS',
|
'insertCSS',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "electron",
|
"name": "electron",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"asar": "^0.11.0",
|
"asar": "^0.11.0",
|
||||||
"request": "*",
|
"request": "*",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
||||||
|
|
||||||
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
||||||
'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent'
|
'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent'
|
||||||
LIBCHROMIUMCONTENT_COMMIT = '8f2a0aa0a9dd107e4d574cc9d552781c55c86bab'
|
LIBCHROMIUMCONTENT_COMMIT = '7ae12b2856b81f5bfa1290108f72c3e4b5c13c52'
|
||||||
|
|
||||||
PLATFORM = {
|
PLATFORM = {
|
||||||
'cygwin': 'win32',
|
'cygwin': 'win32',
|
||||||
|
|
|
@ -858,12 +858,27 @@ describe('browser-window module', function () {
|
||||||
w.loadURL('about:blank')
|
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 () {
|
describe('when the devtools is docked', function () {
|
||||||
it('creates the extension', function (done) {
|
it('creates the extension', function (done) {
|
||||||
w.webContents.openDevTools({mode: 'bottom'})
|
w.webContents.openDevTools({mode: 'bottom'})
|
||||||
|
|
||||||
ipcMain.once('answer', function (event, message) {
|
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()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -873,14 +888,54 @@ describe('browser-window module', function () {
|
||||||
it('creates the extension', function (done) {
|
it('creates the extension', function (done) {
|
||||||
w.webContents.openDevTools({mode: 'undocked'})
|
w.webContents.openDevTools({mode: 'undocked'})
|
||||||
|
|
||||||
ipcMain.once('answer', function (event, message) {
|
ipcMain.once('answer', function (event, message, extensionId) {
|
||||||
assert.equal(message, 'extension loaded')
|
assert.equal(message.runtimeId, 'foo')
|
||||||
|
assert.equal(message.tabId, w.webContents.id)
|
||||||
done()
|
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 () {
|
it('serializes the registered extensions on quit', function () {
|
||||||
var extensionName = 'foo'
|
var extensionName = 'foo'
|
||||||
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName)
|
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName)
|
||||||
|
|
|
@ -919,6 +919,23 @@ describe('protocol module', function () {
|
||||||
})
|
})
|
||||||
w.loadURL(origin)
|
w.loadURL(origin)
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
|
it('can have fetch working in it', function (done) {
|
||||||
|
const content = '<html><script>fetch("http://github.com")</script></html>'
|
||||||
|
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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,8 +16,15 @@ describe('session module', function () {
|
||||||
var fixtures = path.resolve(__dirname, 'fixtures')
|
var fixtures = path.resolve(__dirname, 'fixtures')
|
||||||
var w = null
|
var w = null
|
||||||
var url = 'http://127.0.0.1'
|
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 () {
|
beforeEach(function () {
|
||||||
|
if (w != null) {
|
||||||
|
w.destroy()
|
||||||
|
}
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
|
@ -26,7 +33,10 @@ describe('session module', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
w.destroy()
|
if (w != null) {
|
||||||
|
w.destroy()
|
||||||
|
}
|
||||||
|
w = null
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('session.cookies', function () {
|
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")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const ws = require('ws')
|
const ws = require('ws')
|
||||||
|
const url = require('url')
|
||||||
const remote = require('electron').remote
|
const remote = require('electron').remote
|
||||||
|
|
||||||
const BrowserWindow = remote.require('electron').BrowserWindow
|
const {BrowserWindow, session, webContents} = remote
|
||||||
const session = remote.require('electron').session
|
|
||||||
|
|
||||||
const isCI = remote.getGlobal('isCi')
|
const isCI = remote.getGlobal('isCi')
|
||||||
|
|
||||||
|
@ -165,6 +164,12 @@ describe('chromium feature', function () {
|
||||||
var b = window.open('about:blank', '', 'show=no')
|
var b = window.open('about:blank', '', 'show=no')
|
||||||
assert.equal(b.closed, false)
|
assert.equal(b.closed, false)
|
||||||
assert.equal(b.constructor.name, 'BrowserWindowProxy')
|
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()
|
b.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -235,7 +240,7 @@ describe('chromium feature', function () {
|
||||||
else
|
else
|
||||||
targetURL = 'file://' + fixtures + '/pages/base-page.html'
|
targetURL = 'file://' + fixtures + '/pages/base-page.html'
|
||||||
b = window.open(targetURL)
|
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)
|
assert.equal(b.location, targetURL)
|
||||||
b.close()
|
b.close()
|
||||||
done()
|
done()
|
||||||
|
@ -246,10 +251,10 @@ describe('chromium feature', function () {
|
||||||
// Load a page that definitely won't redirect
|
// Load a page that definitely won't redirect
|
||||||
var b
|
var b
|
||||||
b = window.open('about:blank')
|
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
|
// When it loads, redirect
|
||||||
b.location = 'file://' + fixtures + '/pages/base-page.html'
|
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
|
// After our second redirect, cleanup and callback
|
||||||
b.close()
|
b.close()
|
||||||
done()
|
done()
|
||||||
|
@ -308,7 +313,7 @@ describe('chromium feature', function () {
|
||||||
}
|
}
|
||||||
window.addEventListener('message', listener)
|
window.addEventListener('message', listener)
|
||||||
b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no')
|
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', '*')
|
b.postMessage('testing', '*')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -327,6 +332,25 @@ describe('chromium feature', function () {
|
||||||
window.addEventListener('message', listener)
|
window.addEventListener('message', listener)
|
||||||
b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no')
|
b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('supports windows opened from a <webview>', 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 () {
|
describe('creating a Uint8Array under browser side', function () {
|
||||||
|
|
1
spec/fixtures/devtools-extensions/bad-manifest/manifest.json
vendored
Normal file
1
spec/fixtures/devtools-extensions/bad-manifest/manifest.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue