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;
|
||||
else if (name == "videos")
|
||||
return chrome::DIR_USER_VIDEOS;
|
||||
else if (name == "pepperFlashSystemPlugin")
|
||||
return chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
@ -261,13 +263,14 @@ void App::OnContinueUserActivity(
|
|||
}
|
||||
#endif
|
||||
|
||||
void App::OnLogin(LoginHandler* login_handler) {
|
||||
void App::OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) {
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
bool prevent_default = Emit(
|
||||
"login",
|
||||
WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
|
||||
login_handler->request(),
|
||||
request_details,
|
||||
login_handler->auth_info(),
|
||||
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
|
||||
|
||||
|
@ -447,6 +450,15 @@ bool App::Relaunch(mate::Arguments* js_args) {
|
|||
return relauncher::RelaunchApp(argv);
|
||||
}
|
||||
|
||||
void App::DisableHardwareAcceleration(mate::Arguments* args) {
|
||||
if (Browser::Get()->is_ready()) {
|
||||
args->ThrowError("app.disableHardwareAcceleration() can only be called "
|
||||
"before app is ready");
|
||||
return;
|
||||
}
|
||||
content::GpuDataManager::GetInstance()->DisableHardwareAcceleration();
|
||||
}
|
||||
|
||||
#if defined(USE_NSS_CERTS)
|
||||
void App::ImportCertificate(
|
||||
const base::DictionaryValue& options,
|
||||
|
@ -528,7 +540,9 @@ void App::BuildPrototype(
|
|||
#endif
|
||||
.SetMethod("makeSingleInstance", &App::MakeSingleInstance)
|
||||
.SetMethod("releaseSingleInstance", &App::ReleaseSingleInstance)
|
||||
.SetMethod("relaunch", &App::Relaunch);
|
||||
.SetMethod("relaunch", &App::Relaunch)
|
||||
.SetMethod("disableHardwareAcceleration",
|
||||
&App::DisableHardwareAcceleration);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
|
|
@ -70,7 +70,8 @@ class App : public AtomBrowserClient::Delegate,
|
|||
void OnActivate(bool has_visible_windows) override;
|
||||
void OnWillFinishLaunching() override;
|
||||
void OnFinishLaunching() override;
|
||||
void OnLogin(LoginHandler* login_handler) override;
|
||||
void OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) override;
|
||||
#if defined(OS_MACOSX)
|
||||
void OnContinueUserActivity(
|
||||
bool* prevent_default,
|
||||
|
@ -111,7 +112,7 @@ class App : public AtomBrowserClient::Delegate,
|
|||
const ProcessSingleton::NotificationCallback& callback);
|
||||
void ReleaseSingleInstance();
|
||||
bool Relaunch(mate::Arguments* args);
|
||||
|
||||
void DisableHardwareAcceleration(mate::Arguments* args);
|
||||
#if defined(USE_NSS_CERTS)
|
||||
void ImportCertificate(const base::DictionaryValue& options,
|
||||
const net::CompletionCallback& callback);
|
||||
|
|
|
@ -25,6 +25,9 @@ struct Converter<content::DownloadItem::DownloadState> {
|
|||
content::DownloadItem::DownloadState state) {
|
||||
std::string download_state;
|
||||
switch (state) {
|
||||
case content::DownloadItem::IN_PROGRESS:
|
||||
download_state = "progressing";
|
||||
break;
|
||||
case content::DownloadItem::COMPLETE:
|
||||
download_state = "completed";
|
||||
break;
|
||||
|
@ -85,7 +88,7 @@ void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
|
|||
// Destroy the item once item is downloaded.
|
||||
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
|
||||
} else {
|
||||
Emit("updated");
|
||||
Emit("updated", item->GetState());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,10 +102,18 @@ void DownloadItem::Pause() {
|
|||
download_item_->Pause();
|
||||
}
|
||||
|
||||
bool DownloadItem::IsPaused() const {
|
||||
return download_item_->IsPaused();
|
||||
}
|
||||
|
||||
void DownloadItem::Resume() {
|
||||
download_item_->Resume();
|
||||
}
|
||||
|
||||
bool DownloadItem::CanResume() const {
|
||||
return download_item_->CanResume();
|
||||
}
|
||||
|
||||
void DownloadItem::Cancel() {
|
||||
download_item_->Cancel(true);
|
||||
download_item_->Remove();
|
||||
|
@ -141,6 +152,14 @@ const GURL& DownloadItem::GetURL() const {
|
|||
return download_item_->GetURL();
|
||||
}
|
||||
|
||||
content::DownloadItem::DownloadState DownloadItem::GetState() const {
|
||||
return download_item_->GetState();
|
||||
}
|
||||
|
||||
bool DownloadItem::IsDone() const {
|
||||
return download_item_->IsDone();
|
||||
}
|
||||
|
||||
void DownloadItem::SetSavePath(const base::FilePath& path) {
|
||||
save_path_ = path;
|
||||
}
|
||||
|
@ -155,7 +174,9 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
|
|||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.MakeDestroyable()
|
||||
.SetMethod("pause", &DownloadItem::Pause)
|
||||
.SetMethod("isPaused", &DownloadItem::IsPaused)
|
||||
.SetMethod("resume", &DownloadItem::Resume)
|
||||
.SetMethod("canResume", &DownloadItem::CanResume)
|
||||
.SetMethod("cancel", &DownloadItem::Cancel)
|
||||
.SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes)
|
||||
.SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes)
|
||||
|
@ -164,6 +185,8 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("getFilename", &DownloadItem::GetFilename)
|
||||
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
|
||||
.SetMethod("getURL", &DownloadItem::GetURL)
|
||||
.SetMethod("getState", &DownloadItem::GetState)
|
||||
.SetMethod("isDone", &DownloadItem::IsDone)
|
||||
.SetMethod("setSavePath", &DownloadItem::SetSavePath)
|
||||
.SetMethod("getSavePath", &DownloadItem::GetSavePath);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
|||
v8::Local<v8::ObjectTemplate> prototype);
|
||||
|
||||
void Pause();
|
||||
bool IsPaused() const;
|
||||
void Resume();
|
||||
bool CanResume() const;
|
||||
void Cancel();
|
||||
int64_t GetReceivedBytes() const;
|
||||
int64_t GetTotalBytes() const;
|
||||
|
@ -36,6 +38,8 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
|||
std::string GetFilename() const;
|
||||
std::string GetContentDisposition() const;
|
||||
const GURL& GetURL() const;
|
||||
content::DownloadItem::DownloadState GetState() const;
|
||||
bool IsDone() const;
|
||||
void SetSavePath(const base::FilePath& path);
|
||||
base::FilePath GetSavePath() const;
|
||||
|
||||
|
|
|
@ -12,8 +12,12 @@
|
|||
#include "atom/browser/net/url_request_fetch_job.h"
|
||||
#include "atom/browser/net/url_request_string_job.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "url/url_util.h"
|
||||
|
||||
|
@ -158,21 +162,21 @@ namespace {
|
|||
|
||||
void RegisterStandardSchemes(
|
||||
const std::vector<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);
|
||||
}
|
||||
policy->RegisterWebSafeScheme(scheme);
|
||||
}
|
||||
|
||||
mate::Handle<atom::api::Protocol> CreateProtocol(v8::Isolate* isolate) {
|
||||
auto browser_context = static_cast<atom::AtomBrowserContext*>(
|
||||
atom::AtomBrowserMainParts::Get()->browser_context());
|
||||
return atom::api::Protocol::Create(isolate, browser_context);
|
||||
auto command_line = base::CommandLine::ForCurrentProcess();
|
||||
command_line->AppendSwitchASCII(atom::switches::kStandardSchemes,
|
||||
base::JoinString(schemes, ","));
|
||||
}
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context, void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
mate::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("createProtocolObject", base::Bind(&CreateProtocol, isolate));
|
||||
dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/containers/scoped_ptr_hash_map.h"
|
||||
|
@ -16,7 +17,10 @@
|
|||
#include "native_mate/arguments.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "native_mate/wrappable.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class URLRequest;
|
||||
|
@ -30,10 +34,10 @@ class AtomURLRequestJobFactory;
|
|||
|
||||
namespace api {
|
||||
|
||||
class Protocol : public mate::Wrappable<Protocol> {
|
||||
class Protocol : public mate::TrackableObject<Protocol> {
|
||||
public:
|
||||
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 BooleanCallback = base::Callback<void(bool)>;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "atom/browser/api/atom_api_cookies.h"
|
||||
#include "atom/browser/api/atom_api_download_item.h"
|
||||
#include "atom/browser/api/atom_api_protocol.h"
|
||||
#include "atom/browser/api/atom_api_web_request.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
|
@ -462,6 +463,14 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
|||
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) {
|
||||
if (web_request_.IsEmpty()) {
|
||||
auto handle = atom::api::WebRequest::Create(isolate, browser_context());
|
||||
|
@ -512,6 +521,7 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("allowNTLMCredentialsForDomains",
|
||||
&Session::AllowNTLMCredentialsForDomains)
|
||||
.SetProperty("cookies", &Session::Cookies)
|
||||
.SetProperty("protocol", &Session::Protocol)
|
||||
.SetProperty("webRequest", &Session::WebRequest);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,10 +81,12 @@ class Session: public mate::TrackableObject<Session>,
|
|||
void ClearHostResolverCache(mate::Arguments* args);
|
||||
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
|
||||
|
||||
// Cached object.
|
||||
v8::Global<v8::Value> cookies_;
|
||||
v8::Global<v8::Value> protocol_;
|
||||
v8::Global<v8::Value> web_request_;
|
||||
|
||||
// The X-DevTools-Emulate-Network-Conditions-Client-Id.
|
||||
|
|
|
@ -389,6 +389,11 @@ void WebContents::ActivateContents(content::WebContents* source) {
|
|||
Emit("activate");
|
||||
}
|
||||
|
||||
void WebContents::UpdateTargetURL(content::WebContents* source,
|
||||
const GURL& url) {
|
||||
Emit("update-target-url", url);
|
||||
}
|
||||
|
||||
bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
|
||||
return type_ == BROWSER_WINDOW;
|
||||
}
|
||||
|
@ -739,6 +744,15 @@ int WebContents::GetID() const {
|
|||
return web_contents()->GetRenderProcessHost()->GetID();
|
||||
}
|
||||
|
||||
std::string WebContents::GetType() const {
|
||||
switch (type_) {
|
||||
case BROWSER_WINDOW: return "window";
|
||||
case WEB_VIEW: return "webview";
|
||||
case REMOTE: return "remote";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool WebContents::Equal(const WebContents* web_contents) const {
|
||||
return GetID() == web_contents->GetID();
|
||||
}
|
||||
|
@ -1089,6 +1103,14 @@ void WebContents::StopFindInPage(content::StopFindAction action) {
|
|||
web_contents()->StopFinding(action);
|
||||
}
|
||||
|
||||
void WebContents::ShowDefinitionForSelection() {
|
||||
#if defined(OS_MACOSX)
|
||||
const auto view = web_contents()->GetRenderWidgetHostView();
|
||||
if (view)
|
||||
view->ShowDefinitionForSelection();
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebContents::Focus() {
|
||||
web_contents()->Focus();
|
||||
}
|
||||
|
@ -1274,6 +1296,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
|
||||
.SetMethod("setSize", &WebContents::SetSize)
|
||||
.SetMethod("isGuest", &WebContents::IsGuest)
|
||||
.SetMethod("getType", &WebContents::GetType)
|
||||
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
|
||||
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
|
||||
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
|
||||
|
@ -1284,6 +1307,8 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("_printToPDF", &WebContents::PrintToPDF)
|
||||
.SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
|
||||
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
|
||||
.SetMethod("showDefinitionForSelection",
|
||||
&WebContents::ShowDefinitionForSelection)
|
||||
.SetProperty("id", &WebContents::ID)
|
||||
.SetProperty("session", &WebContents::Session)
|
||||
.SetProperty("hostWebContents", &WebContents::HostWebContents)
|
||||
|
@ -1350,6 +1375,8 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
|||
dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents);
|
||||
dict.SetMethod("fromId",
|
||||
&mate::TrackableObject<atom::api::WebContents>::FromWeakMapID);
|
||||
dict.SetMethod("getAllWebContents",
|
||||
&mate::TrackableObject<atom::api::WebContents>::GetAll);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -59,6 +59,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
v8::Local<v8::ObjectTemplate> prototype);
|
||||
|
||||
int GetID() const;
|
||||
std::string GetType() const;
|
||||
bool Equal(const WebContents* web_contents) const;
|
||||
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
||||
void DownloadURL(const GURL& url);
|
||||
|
@ -116,6 +117,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
void ReplaceMisspelling(const base::string16& word);
|
||||
uint32_t FindInPage(mate::Arguments* args);
|
||||
void StopFindInPage(content::StopFindAction action);
|
||||
void ShowDefinitionForSelection();
|
||||
|
||||
// Focus.
|
||||
void Focus();
|
||||
|
@ -182,6 +184,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
const gfx::Rect& pos) override;
|
||||
void CloseContents(content::WebContents* source) override;
|
||||
void ActivateContents(content::WebContents* contents) override;
|
||||
void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
|
||||
bool IsPopupOrPanel(const content::WebContents* source) const override;
|
||||
void HandleKeyboardEvent(
|
||||
content::WebContents* source,
|
||||
|
|
|
@ -623,12 +623,6 @@ void Window::UnhookAllWindowMessages() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void Window::ShowDefinitionForSelection() {
|
||||
window_->ShowDefinitionForSelection();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
void Window::SetIcon(mate::Handle<NativeImage> icon) {
|
||||
#if defined(OS_WIN)
|
||||
|
@ -760,10 +754,6 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage)
|
||||
.SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages)
|
||||
#endif
|
||||
#if defined(OS_MACOSX)
|
||||
.SetMethod("showDefinitionForSelection",
|
||||
&Window::ShowDefinitionForSelection)
|
||||
#endif
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
.SetMethod("setIcon", &Window::SetIcon)
|
||||
#endif
|
||||
|
|
|
@ -169,10 +169,6 @@ class Window : public mate::TrackableObject<Window>,
|
|||
void UnhookAllWindowMessages();
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void ShowDefinitionForSelection();
|
||||
#endif
|
||||
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
void SetIcon(mate::Handle<NativeImage> icon);
|
||||
#endif
|
||||
|
|
|
@ -169,6 +169,14 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
|
|||
if (process_type != "renderer")
|
||||
return;
|
||||
|
||||
// Copy following switches to child process.
|
||||
static const char* const kCommonSwitchNames[] = {
|
||||
switches::kStandardSchemes,
|
||||
};
|
||||
command_line->CopySwitchesFrom(
|
||||
*base::CommandLine::ForCurrentProcess(),
|
||||
kCommonSwitchNames, arraysize(kCommonSwitchNames));
|
||||
|
||||
// The registered service worker schemes.
|
||||
if (!g_custom_service_worker_schemes.empty())
|
||||
command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes,
|
||||
|
|
|
@ -31,10 +31,13 @@ void HandleExternalProtocolInUI(
|
|||
if (!web_contents)
|
||||
return;
|
||||
|
||||
GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
|
||||
auto callback = base::Bind(&OnOpenExternal, escaped_url);
|
||||
auto permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
if (!permission_helper)
|
||||
return;
|
||||
|
||||
GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
|
||||
auto callback = base::Bind(&OnOpenExternal, escaped_url);
|
||||
permission_helper->RequestOpenExternalPermission(callback, has_user_gesture);
|
||||
}
|
||||
|
||||
|
|
|
@ -151,8 +151,12 @@ void Browser::DidFinishLaunching() {
|
|||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
|
||||
}
|
||||
|
||||
void Browser::RequestLogin(LoginHandler* login_handler) {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
|
||||
void Browser::RequestLogin(
|
||||
LoginHandler* login_handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details) {
|
||||
FOR_EACH_OBSERVER(BrowserObserver,
|
||||
observers_,
|
||||
OnLogin(login_handler, *(request_details.get())));
|
||||
}
|
||||
|
||||
void Browser::NotifyAndShutdown() {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#endif
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
|
@ -165,7 +166,8 @@ class Browser : public WindowListObserver {
|
|||
void DidFinishLaunching();
|
||||
|
||||
// Request basic auth login.
|
||||
void RequestLogin(LoginHandler* login_handler);
|
||||
void RequestLogin(LoginHandler* login_handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details);
|
||||
|
||||
void AddObserver(BrowserObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
|
|
|
@ -49,7 +49,8 @@ class BrowserObserver {
|
|||
virtual void OnFinishLaunching() {}
|
||||
|
||||
// The browser requests HTTP login.
|
||||
virtual void OnLogin(LoginHandler* login_handler) {}
|
||||
virtual void OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) {}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// The browser wants to resume a user activity via handoff. (OS X only)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "atom/browser/login_handler.h"
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/resource_dispatcher_host.h"
|
||||
|
@ -37,11 +39,18 @@ LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
|
|||
render_frame_id_(0) {
|
||||
content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
|
||||
&render_process_host_id_, &render_frame_id_);
|
||||
|
||||
// Fill request details on IO thread.
|
||||
std::unique_ptr<base::DictionaryValue> request_details(
|
||||
new base::DictionaryValue);
|
||||
FillRequestDetails(request_details.get(), request_);
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&Browser::RequestLogin,
|
||||
base::Unretained(Browser::Get()),
|
||||
base::RetainedRef(make_scoped_refptr(this))));
|
||||
base::RetainedRef(make_scoped_refptr(this)),
|
||||
base::Passed(&request_details)));
|
||||
}
|
||||
|
||||
LoginHandler::~LoginHandler() {
|
||||
|
|
|
@ -36,7 +36,6 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate {
|
|||
void Login(const base::string16& username, const base::string16& password);
|
||||
|
||||
const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
|
||||
const net::URLRequest* request() const { return request_; }
|
||||
|
||||
protected:
|
||||
~LoginHandler() override;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/window_list.h"
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
|
@ -166,7 +167,7 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
|||
// For normal window, use white as default background.
|
||||
SetBackgroundColor("#FFFF");
|
||||
}
|
||||
std::string title("Electron");
|
||||
std::string title(Browser::Get()->GetName());
|
||||
options.Get(options::kTitle, &title);
|
||||
SetTitle(title);
|
||||
|
||||
|
@ -281,9 +282,6 @@ bool NativeWindow::IsDocumentEdited() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void NativeWindow::SetIgnoreMouseEvents(bool ignore) {
|
||||
}
|
||||
|
||||
void NativeWindow::SetMenu(ui::MenuModel* menu) {
|
||||
}
|
||||
|
||||
|
@ -337,10 +335,6 @@ void NativeWindow::CapturePage(const gfx::Rect& rect,
|
|||
kBGRA_8888_SkColorType);
|
||||
}
|
||||
|
||||
void NativeWindow::ShowDefinitionForSelection() {
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
void NativeWindow::SetAutoHideMenuBar(bool auto_hide) {
|
||||
}
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ class NativeWindow : public base::SupportsUserData,
|
|||
virtual std::string GetRepresentedFilename();
|
||||
virtual void SetDocumentEdited(bool edited);
|
||||
virtual bool IsDocumentEdited();
|
||||
virtual void SetIgnoreMouseEvents(bool ignore);
|
||||
virtual void SetIgnoreMouseEvents(bool ignore) = 0;
|
||||
virtual void SetMenu(ui::MenuModel* menu);
|
||||
virtual bool HasModalDialog();
|
||||
virtual gfx::NativeWindow GetNativeWindow() = 0;
|
||||
|
@ -179,9 +179,6 @@ class NativeWindow : public base::SupportsUserData,
|
|||
virtual void CapturePage(const gfx::Rect& rect,
|
||||
const CapturePageCallback& callback);
|
||||
|
||||
// Show popup dictionary.
|
||||
virtual void ShowDefinitionForSelection();
|
||||
|
||||
// Toggle the menu bar.
|
||||
virtual void SetAutoHideMenuBar(bool auto_hide);
|
||||
virtual bool IsMenuBarAutoHide();
|
||||
|
|
|
@ -83,7 +83,6 @@ class NativeWindowMac : public NativeWindow {
|
|||
void SetProgressBar(double progress) override;
|
||||
void SetOverlayIcon(const gfx::Image& overlay,
|
||||
const std::string& description) override;
|
||||
void ShowDefinitionForSelection() override;
|
||||
|
||||
void SetVisibleOnAllWorkspaces(bool visible) override;
|
||||
bool IsVisibleOnAllWorkspaces() override;
|
||||
|
@ -139,6 +138,9 @@ class NativeWindowMac : public NativeWindow {
|
|||
// The presentation options before entering kiosk mode.
|
||||
NSApplicationPresentationOptions kiosk_options_;
|
||||
|
||||
// The window title, for frameless windows we only set title when fullscreen.
|
||||
std::string title_;
|
||||
|
||||
// Force showing the buttons for frameless window.
|
||||
bool force_show_buttons_;
|
||||
|
||||
|
|
|
@ -193,10 +193,16 @@ bool ScopedDisableResize::disable_resize_ = false;
|
|||
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
||||
shell_->NotifyWindowEnterFullScreen();
|
||||
|
||||
// For frameless window we don't set title for normal mode since the title
|
||||
// bar is expected to be empty, but after entering fullscreen mode we have
|
||||
// to set one, because title bar is visible here.
|
||||
NSWindow* window = shell_->GetNativeWindow();
|
||||
if (shell_->transparent() || !shell_->has_frame())
|
||||
[window setTitle:base::SysUTF8ToNSString(shell_->GetTitle())];
|
||||
|
||||
// Restore the native toolbar immediately after entering fullscreen, if we do
|
||||
// this before leaving fullscreen, traffic light buttons will be jumping.
|
||||
if (shell_->should_hide_native_toolbar_in_fullscreen()) {
|
||||
NSWindow* window = shell_->GetNativeWindow();
|
||||
base::scoped_nsobject<NSToolbar> toolbar(
|
||||
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
||||
[toolbar setShowsBaselineSeparator:NO];
|
||||
|
@ -209,6 +215,11 @@ bool ScopedDisableResize::disable_resize_ = false;
|
|||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
||||
// Restore the title bar to empty.
|
||||
NSWindow* window = shell_->GetNativeWindow();
|
||||
if (shell_->transparent() || !shell_->has_frame())
|
||||
[window setTitle:@""];
|
||||
|
||||
// Turn off the style for toolbar.
|
||||
if (shell_->should_hide_native_toolbar_in_fullscreen())
|
||||
shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
|
||||
|
@ -513,10 +524,6 @@ NativeWindowMac::NativeWindowMac(
|
|||
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
||||
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
||||
|
||||
// Disable zoom button if window is not resizable.
|
||||
if (!maximizable)
|
||||
SetMaximizable(false);
|
||||
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
|
@ -544,6 +551,12 @@ NativeWindowMac::NativeWindowMac(
|
|||
}];
|
||||
|
||||
InstallView();
|
||||
|
||||
// Disable zoom button if window is not resizable.
|
||||
// Set maximizable state last to ensure zoom button does not get reset
|
||||
// by calls to other APIs.
|
||||
if (!maximizable)
|
||||
SetMaximizable(false);
|
||||
}
|
||||
|
||||
NativeWindowMac::~NativeWindowMac() {
|
||||
|
@ -781,6 +794,8 @@ void NativeWindowMac::Center() {
|
|||
}
|
||||
|
||||
void NativeWindowMac::SetTitle(const std::string& title) {
|
||||
title_ = title;
|
||||
|
||||
// We don't want the title to show in transparent or frameless window.
|
||||
if (transparent() || !has_frame())
|
||||
return;
|
||||
|
@ -789,7 +804,7 @@ void NativeWindowMac::SetTitle(const std::string& title) {
|
|||
}
|
||||
|
||||
std::string NativeWindowMac::GetTitle() {
|
||||
return base::SysNSStringToUTF8([window_ title]);
|
||||
return title_;
|
||||
}
|
||||
|
||||
void NativeWindowMac::FlashFrame(bool flash) {
|
||||
|
@ -920,15 +935,6 @@ void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay,
|
|||
const std::string& description) {
|
||||
}
|
||||
|
||||
void NativeWindowMac::ShowDefinitionForSelection() {
|
||||
if (!web_contents())
|
||||
return;
|
||||
auto rwhv = web_contents()->GetRenderWidgetHostView();
|
||||
if (!rwhv)
|
||||
return;
|
||||
rwhv->ShowDefinitionForSelection();
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible) {
|
||||
SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
|
||||
}
|
||||
|
|
|
@ -89,6 +89,21 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
|
|||
(modifiers == (Modifiers::AltKey | Modifiers::IsRight));
|
||||
}
|
||||
|
||||
#if defined(USE_X11)
|
||||
int SendClientEvent(XDisplay* display, ::Window window, const char* msg) {
|
||||
XEvent event = {};
|
||||
event.xclient.type = ClientMessage;
|
||||
event.xclient.send_event = True;
|
||||
event.xclient.message_type = XInternAtom(display, msg, False);
|
||||
event.xclient.window = window;
|
||||
event.xclient.format = 32;
|
||||
XSendEvent(display, DefaultRootWindow(display), False,
|
||||
SubstructureRedirectMask | SubstructureNotifyMask, &event);
|
||||
XFlush(display);
|
||||
return True;
|
||||
}
|
||||
#endif
|
||||
|
||||
class NativeWindowClientView : public views::ClientView {
|
||||
public:
|
||||
NativeWindowClientView(views::Widget* widget,
|
||||
|
@ -298,10 +313,19 @@ void NativeWindowViews::CloseImmediately() {
|
|||
}
|
||||
|
||||
void NativeWindowViews::Focus(bool focus) {
|
||||
if (focus)
|
||||
if (focus) {
|
||||
#if defined(OS_WIN)
|
||||
window_->Activate();
|
||||
else
|
||||
#elif defined(USE_X11)
|
||||
// The "Activate" implementation of Chromium is not reliable on Linux.
|
||||
::Window window = GetAcceleratedWidget();
|
||||
XDisplay* xdisplay = gfx::GetXDisplay();
|
||||
SendClientEvent(xdisplay, window, "_NET_ACTIVE_WINDOW");
|
||||
XMapRaised(xdisplay, window);
|
||||
#endif
|
||||
} else {
|
||||
window_->Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsFocused() {
|
||||
|
@ -648,6 +672,26 @@ bool NativeWindowViews::HasShadow() {
|
|||
return wm::GetShadowType(GetNativeWindow()) != wm::SHADOW_TYPE_NONE;
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetIgnoreMouseEvents(bool ignore) {
|
||||
#if defined(OS_WIN)
|
||||
LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE);
|
||||
if (ignore)
|
||||
ex_style |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
|
||||
else
|
||||
ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
|
||||
::SetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE, ex_style);
|
||||
#elif defined(USE_X11)
|
||||
if (ignore) {
|
||||
XRectangle r = {0, 0, 1, 1};
|
||||
XShapeCombineRectangles(gfx::GetXDisplay(), GetAcceleratedWidget(),
|
||||
ShapeInput, 0, 0, &r, 1, ShapeSet, YXBanded);
|
||||
} else {
|
||||
XShapeCombineMask(gfx::GetXDisplay(), GetAcceleratedWidget(),
|
||||
ShapeInput, 0, 0, None, ShapeSet);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
|
||||
if (menu_model == nullptr) {
|
||||
// Remove accelerators
|
||||
|
|
|
@ -91,6 +91,7 @@ class NativeWindowViews : public NativeWindow,
|
|||
void SetBackgroundColor(const std::string& color_name) override;
|
||||
void SetHasShadow(bool has_shadow) override;
|
||||
bool HasShadow() override;
|
||||
void SetIgnoreMouseEvents(bool ignore) override;
|
||||
void SetMenu(ui::MenuModel* menu_model) override;
|
||||
gfx::NativeWindow GetNativeWindow() override;
|
||||
void SetOverlayIcon(const gfx::Image& overlay,
|
||||
|
|
|
@ -71,18 +71,13 @@ bool MatchesFilterCondition(net::URLRequest* request,
|
|||
|
||||
// Overloaded by multiple types to fill the |details| object.
|
||||
void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
|
||||
FillRequestDetails(details, request);
|
||||
details->SetInteger("id", request->identifier());
|
||||
details->SetString("url", request->url().spec());
|
||||
details->SetString("method", request->method());
|
||||
details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000);
|
||||
auto info = content::ResourceRequestInfo::ForRequest(request);
|
||||
details->SetString("resourceType",
|
||||
info ? ResourceTypeToString(info->GetResourceType())
|
||||
: "other");
|
||||
std::unique_ptr<base::ListValue> list(new base::ListValue);
|
||||
GetUploadData(list.get(), request);
|
||||
if (!list->empty())
|
||||
details->Set("uploadData", std::move(list));
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
|
|
|
@ -44,7 +44,7 @@ void HandlerCallback(const BeforeStartCallback& before_start,
|
|||
|
||||
void AskForOptions(v8::Isolate* isolate,
|
||||
const JavaScriptHandler& handler,
|
||||
net::URLRequest* request,
|
||||
std::unique_ptr<base::DictionaryValue> request_details,
|
||||
const BeforeStartCallback& before_start,
|
||||
const ResponseCallback& callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
@ -53,7 +53,7 @@ void AskForOptions(v8::Isolate* isolate,
|
|||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
handler.Run(
|
||||
request,
|
||||
*(request_details.get()),
|
||||
mate::ConvertToV8(isolate,
|
||||
base::Bind(&HandlerCallback, before_start, callback)));
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef ATOM_BROWSER_NET_JS_ASKER_H_
|
||||
#define ATOM_BROWSER_NET_JS_ASKER_H_
|
||||
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
@ -19,7 +20,7 @@
|
|||
namespace atom {
|
||||
|
||||
using JavaScriptHandler =
|
||||
base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
|
||||
base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
@ -31,7 +32,7 @@ using ResponseCallback =
|
|||
// Ask handler for options in UI thread.
|
||||
void AskForOptions(v8::Isolate* isolate,
|
||||
const JavaScriptHandler& handler,
|
||||
net::URLRequest* request,
|
||||
std::unique_ptr<base::DictionaryValue> request_details,
|
||||
const BeforeStartCallback& before_start,
|
||||
const ResponseCallback& callback);
|
||||
|
||||
|
@ -67,12 +68,15 @@ class JsAsker : public RequestJob {
|
|||
private:
|
||||
// RequestJob:
|
||||
void Start() override {
|
||||
std::unique_ptr<base::DictionaryValue> request_details(
|
||||
new base::DictionaryValue);
|
||||
FillRequestDetails(request_details.get(), RequestJob::request());
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&internal::AskForOptions,
|
||||
isolate_,
|
||||
handler_,
|
||||
RequestJob::request(),
|
||||
base::Passed(&request_details),
|
||||
base::Bind(&JsAsker::BeforeStartInUI,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
base::Bind(&JsAsker::OnResponse,
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundleIconFile</key>
|
||||
<string>electron.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.2.1</string>
|
||||
<string>1.2.2</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.1</string>
|
||||
<string>1.2.2</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
@ -56,8 +56,8 @@ END
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,1,0
|
||||
PRODUCTVERSION 1,2,1,0
|
||||
FILEVERSION 1,2,2,0
|
||||
PRODUCTVERSION 1,2,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -74,12 +74,12 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "1.2.1"
|
||||
VALUE "FileVersion", "1.2.2"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "1.2.1"
|
||||
VALUE "ProductVersion", "1.2.2"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
|
|
@ -33,7 +33,7 @@ Role kRolesMap[] = {
|
|||
{ @selector(copy:), "copy" },
|
||||
{ @selector(paste:), "paste" },
|
||||
{ @selector(delete:), "delete" },
|
||||
{ @selector(pasteAndMatchStyle:), "paste-and-match-style" },
|
||||
{ @selector(pasteAndMatchStyle:), "pasteandmatchstyle" },
|
||||
{ @selector(selectAll:), "selectall" },
|
||||
{ @selector(performMiniaturize:), "minimize" },
|
||||
{ @selector(performClose:), "close" },
|
||||
|
|
|
@ -54,7 +54,7 @@ TaskbarHost::~TaskbarHost() {
|
|||
|
||||
bool TaskbarHost::SetThumbarButtons(
|
||||
HWND window, const std::vector<ThumbarButton>& buttons) {
|
||||
if (buttons.size() > kMaxButtonsCount || !InitailizeTaskbar())
|
||||
if (buttons.size() > kMaxButtonsCount || !InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
callback_map_.clear();
|
||||
|
@ -118,7 +118,7 @@ bool TaskbarHost::SetThumbarButtons(
|
|||
}
|
||||
|
||||
bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
||||
if (!InitailizeTaskbar())
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
HRESULT r;
|
||||
|
@ -133,7 +133,7 @@ bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
|||
|
||||
bool TaskbarHost::SetOverlayIcon(
|
||||
HWND window, const gfx::Image& overlay, const std::string& text) {
|
||||
if (!InitailizeTaskbar())
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
base::win::ScopedHICON icon(
|
||||
|
@ -152,7 +152,7 @@ bool TaskbarHost::HandleThumbarButtonEvent(int button_id) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool TaskbarHost::InitailizeTaskbar() {
|
||||
bool TaskbarHost::InitializeTaskbar() {
|
||||
if (FAILED(taskbar_.CreateInstance(CLSID_TaskbarList,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER)) ||
|
||||
|
|
|
@ -44,8 +44,8 @@ class TaskbarHost {
|
|||
bool HandleThumbarButtonEvent(int button_id);
|
||||
|
||||
private:
|
||||
// Initailize the taskbar object.
|
||||
bool InitailizeTaskbar();
|
||||
// Initialize the taskbar object.
|
||||
bool InitializeTaskbar();
|
||||
|
||||
using CallbackMap = std::map<int, base::Closure>;
|
||||
CallbackMap callback_map_;
|
||||
|
|
|
@ -166,6 +166,13 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
|
|||
command_line->AppendSwitchASCII(::switches::kEnableBlinkFeatures,
|
||||
blink_features);
|
||||
|
||||
// Disable blink features.
|
||||
std::string disable_blink_features;
|
||||
if (web_preferences.GetString(options::kDisableBlinkFeatures,
|
||||
&disable_blink_features))
|
||||
command_line->AppendSwitchASCII(::switches::kDisableBlinkFeatures,
|
||||
disable_blink_features);
|
||||
|
||||
// The initial visibility state.
|
||||
NativeWindow* window = NativeWindow::FromWebContents(web_contents);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#define ATOM_MAJOR_VERSION 1
|
||||
#define ATOM_MINOR_VERSION 2
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
#define ATOM_PATCH_VERSION 2
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#ifndef ATOM_COMMON_CHROME_VERSION_H_
|
||||
#define ATOM_COMMON_CHROME_VERSION_H_
|
||||
|
||||
#define CHROME_VERSION_STRING "51.0.2704.63"
|
||||
#define CHROME_VERSION_STRING "51.0.2704.84"
|
||||
#define CHROME_VERSION "v" CHROME_VERSION_STRING
|
||||
|
||||
#endif // ATOM_COMMON_CHROME_VERSION_H_
|
||||
|
|
|
@ -22,22 +22,6 @@
|
|||
|
||||
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
|
||||
v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
|
||||
v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
|
||||
|
@ -69,6 +53,19 @@ v8::Local<v8::Value> Converter<scoped_refptr<net::X509Certificate>>::ToV8(
|
|||
|
||||
namespace atom {
|
||||
|
||||
void FillRequestDetails(base::DictionaryValue* details,
|
||||
const net::URLRequest* request) {
|
||||
details->SetString("method", request->method());
|
||||
std::string url;
|
||||
if (!request->url_chain().empty()) url = request->url().spec();
|
||||
details->SetStringWithoutPathExpansion("url", url);
|
||||
details->SetString("referrer", request->referrer());
|
||||
std::unique_ptr<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,
|
||||
const net::URLRequest* request) {
|
||||
const net::UploadDataStream* upload_data = request->get_upload();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "native_mate/converter.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class ListValue;
|
||||
}
|
||||
|
||||
|
@ -20,12 +21,6 @@ class X509Certificate;
|
|||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<const net::URLRequest*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const net::URLRequest* val);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<const net::AuthChallengeInfo*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
|
@ -42,6 +37,9 @@ struct Converter<scoped_refptr<net::X509Certificate>> {
|
|||
|
||||
namespace atom {
|
||||
|
||||
void FillRequestDetails(base::DictionaryValue* details,
|
||||
const net::URLRequest* request);
|
||||
|
||||
void GetUploadData(base::ListValue* upload_data_list,
|
||||
const net::URLRequest* request);
|
||||
|
||||
|
|
|
@ -107,8 +107,12 @@ const char kOpenerID[] = "openerId";
|
|||
const char kScrollBounce[] = "scrollBounce";
|
||||
|
||||
// Enable blink features.
|
||||
// TODO(kevinsawicki) Rename to enableBlinkFeatures in 2.0
|
||||
const char kBlinkFeatures[] = "blinkFeatures";
|
||||
|
||||
// Disable blink features.
|
||||
const char kDisableBlinkFeatures[] = "disableBlinkFeatures";
|
||||
|
||||
} // namespace options
|
||||
|
||||
namespace switches {
|
||||
|
@ -125,6 +129,9 @@ const char kPpapiFlashVersion[] = "ppapi-flash-version";
|
|||
// Disable HTTP cache.
|
||||
const char kDisableHttpCache[] = "disable-http-cache";
|
||||
|
||||
// The list of standard schemes.
|
||||
const char kStandardSchemes[] = "standard-schemes";
|
||||
|
||||
// Register schemes to handle service worker.
|
||||
const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes";
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ extern const char kExperimentalCanvasFeatures[];
|
|||
extern const char kOpenerID[];
|
||||
extern const char kScrollBounce[];
|
||||
extern const char kBlinkFeatures[];
|
||||
extern const char kDisableBlinkFeatures[];
|
||||
|
||||
} // namespace options
|
||||
|
||||
|
@ -70,6 +71,7 @@ extern const char kEnablePlugins[];
|
|||
extern const char kPpapiFlashPath[];
|
||||
extern const char kPpapiFlashVersion[];
|
||||
extern const char kDisableHttpCache[];
|
||||
extern const char kStandardSchemes[];
|
||||
extern const char kRegisterServiceWorkerSchemes[];
|
||||
extern const char kSSLVersionFallbackMin[];
|
||||
extern const char kCipherSuiteBlacklist[];
|
||||
|
|
|
@ -122,6 +122,16 @@ bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
|||
AtomRendererClient::AtomRendererClient()
|
||||
: node_bindings_(NodeBindings::Create(false)),
|
||||
atom_bindings_(new AtomBindings) {
|
||||
// Parse --standard-schemes=scheme1,scheme2
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
std::string custom_schemes = command_line->GetSwitchValueASCII(
|
||||
switches::kStandardSchemes);
|
||||
if (!custom_schemes.empty()) {
|
||||
std::vector<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() {
|
||||
|
|
|
@ -8,11 +8,14 @@ const url = require('url')
|
|||
|
||||
// Parse command line options.
|
||||
const argv = process.argv.slice(1)
|
||||
const option = { file: null, help: null, version: null, webdriver: null, modules: [] }
|
||||
const option = { file: null, help: null, version: null, abi: null, webdriver: null, modules: [] }
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
if (argv[i] === '--version' || argv[i] === '-v') {
|
||||
option.version = true
|
||||
break
|
||||
} else if (argv[i] === '--abi') {
|
||||
option.abi = true
|
||||
break
|
||||
} else if (argv[i].match(/^--app=/)) {
|
||||
option.file = argv[i].split('=')[1]
|
||||
break
|
||||
|
@ -201,7 +204,7 @@ app.once('ready', () => {
|
|||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
label: 'Quit ' + app.getName(),
|
||||
accelerator: 'Command+Q',
|
||||
click () { app.quit() }
|
||||
}
|
||||
|
@ -304,6 +307,9 @@ if (option.file && !option.webdriver) {
|
|||
} else if (option.version) {
|
||||
console.log('v' + process.versions.electron)
|
||||
process.exit(0)
|
||||
} else if (option.abi) {
|
||||
console.log(process.versions.modules)
|
||||
process.exit(0)
|
||||
} else if (option.help) {
|
||||
const helpMessage = `Electron ${process.versions.electron} - Build cross platform desktop apps with JavaScript, HTML, and CSS
|
||||
|
||||
|
@ -321,7 +327,8 @@ if (option.file && !option.webdriver) {
|
|||
-h, --help Print this usage message.
|
||||
-i, --interactive Open a REPL to the main process.
|
||||
-r, --require Module to preload (option can be repeated)
|
||||
-v, --version Print the version.`
|
||||
-v, --version Print the version.
|
||||
--abi Print the application binary interface.`
|
||||
console.log(helpMessage)
|
||||
process.exit(0)
|
||||
} else if (option.interactive) {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
`ipcRenderer`는 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다:
|
||||
|
||||
### `ipcMain.on(channel, listener)`
|
||||
### `ipcRenderer.on(channel, listener)`
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function
|
||||
|
@ -20,7 +20,7 @@
|
|||
`channel`에 대해 이벤트를 리스닝합니다. 새로운 메시지가 도착하면 `listener`가
|
||||
`listener(event, args...)` 형식으로 호출됩니다.
|
||||
|
||||
### `ipcMain.once(channel, listener)`
|
||||
### `ipcRenderer.once(channel, listener)`
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function
|
||||
|
@ -28,7 +28,7 @@
|
|||
이벤트에 대해 한 번만 작동하는 `listener` 함수를 등록합니다. 이 `listener`는 등록된
|
||||
후 `channel`에 보내지는 메시지에 한해 호출됩니다. 호출된 이후엔 리스너가 삭제됩니다.
|
||||
|
||||
### `ipcMain.removeListener(channel, listener)`
|
||||
### `ipcRenderer.removeListener(channel, listener)`
|
||||
|
||||
* `channel` String
|
||||
* `listener` Function
|
||||
|
@ -39,7 +39,7 @@
|
|||
|
||||
지정한 `channel`에 대한 리스너를 저장하는 배열에서 지정한 `listener`를 삭제합니다.
|
||||
|
||||
### `ipcMain.removeAllListeners(channel)`
|
||||
### `ipcRenderer.removeAllListeners(channel)`
|
||||
|
||||
* `channel` String (optional)
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ exports.withLocalCallback = () => {
|
|||
|
||||
```javascript
|
||||
// 렌더러 프로세스
|
||||
const mapNumbers = require('remote').require('./mapNumbers');
|
||||
const mapNumbers = require('electron').remote.require('./mapNumbers');
|
||||
|
||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
||||
|
||||
|
|
|
@ -284,6 +284,15 @@ Returns:
|
|||
<meta name='theme-color' content='#ff0000'>
|
||||
```
|
||||
|
||||
### Event: 'update-target-url'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `url` URL
|
||||
|
||||
마우스나 키보드를 사용해 링크에 포커스를 할 때 발생하는 이벤트입니다.
|
||||
|
||||
### Event: 'cursor-changed'
|
||||
|
||||
Returns:
|
||||
|
@ -591,6 +600,12 @@ CSS 코드를 현재 웹 페이지에 삽입합니다.
|
|||
|
||||
웹 페이지에서 `replaceMisspelling` 편집 커맨드를 실행합니다.
|
||||
|
||||
### `webContents.insertText(text)`
|
||||
|
||||
* `text` String
|
||||
|
||||
포커스된 요소에 `text`를 삽입합니다.
|
||||
|
||||
### `webContents.findInPage(text[, options])`
|
||||
|
||||
* `text` String - 찾을 콘텐츠, 반드시 공백이 아니여야 합니다.
|
||||
|
|
|
@ -117,25 +117,7 @@ MyApp.app/Contents
|
|||
|
||||
또한 Electron 소스 코드를 다시 빌드할 때 어플리케이션 이름을 변경할 수 있습니다.
|
||||
|
||||
`GYP_DEFINES` 환경변수를 사용하여 다음과 같이 다시 빌드할 수 있습니다:
|
||||
|
||||
__Windows__
|
||||
|
||||
```bash
|
||||
> set "GYP_DEFINES=project_name=myapp product_name=MyApp"
|
||||
> python script\clean.py
|
||||
> python script\bootstrap.py
|
||||
> python script\build.py -c R -t myapp
|
||||
```
|
||||
|
||||
__Bash__
|
||||
|
||||
```bash
|
||||
$ export GYP_DEFINES="project_name=myapp product_name=MyApp"
|
||||
$ script/clean.py
|
||||
$ script/bootstrap.py
|
||||
$ script/build.py -c Release -t myapp
|
||||
```
|
||||
`atom.gyp` 파일을 수정하여 다음과 같이 다시 빌드할 수 있습니다:
|
||||
|
||||
### grunt-build-atom-shell
|
||||
|
||||
|
@ -175,16 +157,16 @@ Electron의 개발자로써, Electron을 매우 많은 시나리오에서도 작
|
|||
|
||||
3. 다음의 환경 변수들을 설정합니다:
|
||||
|
||||
* `ELECTRON_GITHUB_TOKEN` - GitHub에 릴리즈를 만들 수 있는 토큰.
|
||||
* `ELECTRON_S3_ACCESS_KEY`, `ELECTRON_S3_BUCKET`, `ELECTRON_S3_SECRET_KEY` -
|
||||
* `ELECTRON_GITHUB_TOKEN` - GitHub에 릴리즈를 만들 수 있는 토큰.
|
||||
* `ELECTRON_S3_ACCESS_KEY`, `ELECTRON_S3_BUCKET`, `ELECTRON_S3_SECRET_KEY` -
|
||||
node.js 헤더 뿐만 아니라 심볼을 업로드할 장소.
|
||||
* `ELECTRON_RELEASE` - `true`로 지정하고 업로드 부분이 실행되면, 설정되지 않은
|
||||
* `ELECTRON_RELEASE` - `true`로 지정하고 업로드 부분이 실행되면, 설정되지 않은
|
||||
부분을 남기고 `surf-build`가 CI-type 확인을 실행합니다. 모든 pull request를
|
||||
실행할 때 적합합니다.
|
||||
* `CI` - `true` 또는 다른 것을 지정하면 실패합니다.
|
||||
* `GITHUB_TOKEN` - `ELECTRON_GITHUB_TOKEN`와 같게 설정
|
||||
* `SURF_TEMP` - Windows에선 `C:\Temp`로 설정하면 긴 경로 문제를 해결할 수 있습니다.
|
||||
* `TARGET_ARCH` - `ia32` 또는 `x64`를 지정.
|
||||
* `CI` - `true` 또는 다른 것을 지정하면 실패합니다.
|
||||
* `GITHUB_TOKEN` - `ELECTRON_GITHUB_TOKEN`와 같게 설정
|
||||
* `SURF_TEMP` - Windows에선 `C:\Temp`로 설정하면 긴 경로 문제를 해결할 수 있습니다.
|
||||
* `TARGET_ARCH` - `ia32` 또는 `x64`를 지정.
|
||||
|
||||
4. Electron에 기여를 하는 기여자라면, _반드시_ `script/upload.py`에서 포크를 위해
|
||||
`ELECTRON_REPO`를 설정해야 합니다. (`MYORG/electron`)
|
||||
|
|
|
@ -120,6 +120,21 @@ productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RES
|
|||
문서를 참고하여 기본적인 개념을 이해해야 합니다. 그리고 자격(plist) 파일에
|
||||
어플리케이션에서 요구하는 권한의 키를 추가합니다.
|
||||
|
||||
그 외에 [electron-osx-sign][electron-osx-sign] 모듈을 이용해서 직접 서명할 수도 있습니다.
|
||||
|
||||
#### 네이티브 모듈 서명하기
|
||||
|
||||
앱 내부에서 사용한 네이티브 모듈들도 서명이 필요합니다.
|
||||
electron-osx-sign 을 사용한다면, 앱 실행 인수 목록에 경로를 반드시 지정해야 합니다.
|
||||
|
||||
```bash
|
||||
electron-osx-sign YourApp.app YourApp.app/Contents/Resources/app/node_modules/nativemodule/build/release/nativemodule
|
||||
```
|
||||
|
||||
참고할 점은 네이티브 모듈이 의도하지 않았지만 오브젝트 파일(.o)을 포함하는 경우도 있습니다.
|
||||
이 경우 오브젝트 파일들의 서명을 해야할 수도 있습니다.
|
||||
[electron-packager][electron-packager]를 사용한다면, 빌드 과정에 `--ignore=.+\.o$` 코드를 추가해 해당 파일을 무시해줍시다.
|
||||
|
||||
### 어플리케이션 업로드
|
||||
|
||||
어플리케이션 서명을 완료한 후 iTunes Connect에 업로드하기 위해 Application Loader를
|
||||
|
@ -190,6 +205,8 @@ ERN의 승인을 얻는 방법은, 다음 글을 참고하는 것이 좋습니
|
|||
[nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps
|
||||
[enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
|
||||
[create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html
|
||||
[electron-osx-sign]: https://github.com/electron-userland/electron-osx-sign
|
||||
[electron-packager]: https://github.com/electron-userland/electron-packager
|
||||
[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
|
||||
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
|
||||
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||
|
|
|
@ -41,7 +41,7 @@ API를 사용하여 low-level 수준으로 운영체제와 상호작용할 수
|
|||
프로세스에서 그 작업을 처리할 수 있도록 메인 프로세스와 통신을 해야 합니다.
|
||||
|
||||
Electron에는 메인 프로세스와 렌더러 프로세스 사이에 통신을 할 수 있도록
|
||||
[ipc](../api/ipc-renderer.md) 모듈을 제공하고 있습니다.
|
||||
[ipcRenderer](../api/ipc-renderer.md)와 [ipcMain](../api/ipc-main.md) 모듈을 제공하고 있습니다.
|
||||
또는 [remote](../api/remote.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다.
|
||||
또한 FAQ에서 [다양한 객체를 공유하는 방법](share-data)도 소개하고 있습니다.
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
Пожалуйста, убедитесь, что вы используете документацию, которые соответствует вашей версии Electron.
|
||||
Номер версии должен быть частью адреса страницы. Если это не так, вы
|
||||
возможно, используете документацию ветки разработки, которая может содержать изменения api,
|
||||
Пожалуйста, убедитесь, что Вы используете документацию, которая соответствует вашей версии Electron.
|
||||
Номер версии должен быть частью адреса страницы. Если это не так, Вы
|
||||
возможно, используете документацию ветки разработки, которая может содержать изменения api,
|
||||
которые не совместимы с вашей версией Electron. Если это так,
|
||||
Вы можете переключиться на другую версию документации в списке
|
||||
[доступные версии](http://electron.atom.io/docs/) на atom.io, или
|
||||
если вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и
|
||||
[доступные версии](http://electron.atom.io/docs/) на [atom.io](atom.io), или
|
||||
если Вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и
|
||||
выберите тег, который соответствует вашей версии.
|
||||
|
||||
## Руководства
|
||||
|
@ -13,7 +13,7 @@
|
|||
* [Application Distribution](tutorial/application-distribution.md)
|
||||
* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md)
|
||||
* [Application Packaging](tutorial/application-packaging.md)
|
||||
* [Using Native Node Modules](tutorial/using-native-node-modules.md)
|
||||
* [Использование нативных модулей NodeJS](tutorial/using-native-node-modules.md)
|
||||
* [Отладка главного процесса](tutorial/debugging-main-process.md)
|
||||
* [Использование Selenium и WebDriver](tutorial/using-selenium-and-webdriver.md)
|
||||
* [DevTools Extension](tutorial/devtools-extension.md)
|
||||
|
@ -22,8 +22,8 @@
|
|||
## Учебники
|
||||
|
||||
* [Быстрый старт](tutorial/quick-start.md)
|
||||
* [Desktop Environment Integration](tutorial/desktop-environment-integration.md)
|
||||
* [Online/Offline Event Detection](tutorial/online-offline-events.md)
|
||||
* [Интеграция рабочего окружения](tutorial/desktop-environment-integration.md)
|
||||
* [Определение Онлайн/Оффлайн состояния](tutorial/online-offline-events.md)
|
||||
|
||||
## API References
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
|||
* [`<webview>` Tag](api/web-view-tag.md)
|
||||
* [`window.open` Function](api/window-open.md)
|
||||
|
||||
### Modules for the Main Process:
|
||||
### Модули для Main Process:
|
||||
|
||||
* [app](api/app.md)
|
||||
* [autoUpdater](api/auto-updater.md)
|
||||
|
@ -61,7 +61,7 @@
|
|||
* [remote](api/remote.md)
|
||||
* [webFrame](api/web-frame.md)
|
||||
|
||||
### Modules for Both Processes:
|
||||
### Модули для обоих процессов:
|
||||
|
||||
* [clipboard](api/clipboard.md)
|
||||
* [crashReporter](api/crash-reporter.md)
|
||||
|
@ -72,11 +72,11 @@
|
|||
## Разработка
|
||||
|
||||
* [Стиль кодирования](development/coding-style.md)
|
||||
* [Source Code Directory Structure](development/source-code-directory-structure.md)
|
||||
* [Структура папок с исходным кодом](development/source-code-directory-structure.md)
|
||||
* [Technical Differences to NW.js (formerly node-webkit)](development/atom-shell-vs-node-webkit.md)
|
||||
* [Обзор системы сборки](development/build-system-overview.md)
|
||||
* [Инструкции по сборке (OS X)](development/build-instructions-osx.md)
|
||||
* [Инструкции по сборке (Windows)](development/build-instructions-windows.md)
|
||||
* [Инструкции по сборке (Linux)](development/build-instructions-linux.md)
|
||||
* [Настройка сервера символов для отладчика](development/setting-up-symbol-server.md)
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Распространение приложения
|
||||
|
||||
Чтобы разпространять ваше приложение на Electron, папка с вашим приложением
|
||||
должна называться `app` и находиться в папке ресурсов Electron (на OS X это
|
||||
`Electron.app/Contents/Resources/`, на Linux и Windows - `resources/`),
|
||||
должна называться `app` и находиться в папке ресурсов Electron (в OS X это
|
||||
`Electron.app/Contents/Resources/`, в Linux и Windows - `resources/`),
|
||||
вот так:
|
||||
|
||||
На OS X:
|
||||
Для OS X:
|
||||
|
||||
```text
|
||||
electron/Electron.app/Contents/Resources/app/
|
||||
|
@ -14,7 +14,7 @@ electron/Electron.app/Contents/Resources/app/
|
|||
└── index.html
|
||||
```
|
||||
|
||||
На Windows и Linux:
|
||||
Для Windows и Linux:
|
||||
|
||||
```text
|
||||
electron/resources/app
|
||||
|
@ -23,13 +23,13 @@ electron/resources/app
|
|||
└── index.html
|
||||
```
|
||||
|
||||
Затем запустите `Electron.app` (или `electron` на Linux, `electron.exe` на Windows),
|
||||
Затем запустите `Electron.app` (или `electron` в Linux, `electron.exe` в Windows),
|
||||
и Electron запустится как ваше приложение. Теперь папка `electron` и есть дистрибутив,
|
||||
который вы должны распространять пользователям.
|
||||
который Вы должны распространять пользователям.
|
||||
|
||||
## Упаковка вашего приложения в файл
|
||||
|
||||
Если вы не хотите распространять исходные коды вашего проект, вы можете
|
||||
Если Вы `не хотите` распространять исходные коды вашего проекта, Вы можете
|
||||
упаковать его в архив [asar](https://github.com/atom/asar), чтобы не
|
||||
показывать пользователям исходные коды.
|
||||
|
||||
|
@ -38,25 +38,26 @@ electron/resources/app
|
|||
после чего Electron попробует считать ресурсы и запустить архив.
|
||||
|
||||
|
||||
На OS X:
|
||||
Для OS X:
|
||||
|
||||
```text
|
||||
electron/Electron.app/Contents/Resources/
|
||||
└── app.asar
|
||||
```
|
||||
|
||||
На Windows и Linux:
|
||||
Для Windows и Linux:
|
||||
|
||||
```text
|
||||
electron/resources/
|
||||
└── app.asar
|
||||
```
|
||||
|
||||
Больше деталей можна найти в [инстуркции по упаковке приложения](application-packaging.md).
|
||||
Больше деталей можно найти в [инстуркции по упаковке приложения](application-packaging.md).
|
||||
|
||||
## Ребрендирование скачанных исполняемых файлов
|
||||
После того, как вы подключили ваше приложение к Electron,
|
||||
вам наверняка захочеться ребрендировать его перед распространением.
|
||||
|
||||
После того, как Вы подключили ваше приложение к Electron,
|
||||
Вам наверняка захочется ребрендировать его перед распространением.
|
||||
|
||||
### Windows
|
||||
|
||||
|
@ -73,7 +74,7 @@ electron/resources/
|
|||
* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
|
||||
|
||||
Вы таже можете переименовать приложение-помощник, чтобы оно не показывало `Electron Helper`,
|
||||
убедитесь, что вы переименовали его исполняемый файл.
|
||||
убедитесь, что Вы переименовали его исполняемый файл.
|
||||
|
||||
Структура переименованного приложения выглядит примерно так:
|
||||
|
||||
|
@ -104,22 +105,22 @@ MyApp.app/Contents
|
|||
## Rebranding by Rebuilding Electron from Source
|
||||
|
||||
Вы также можете ребрендировать Electron изменив имя продукта и собрав его
|
||||
из исходных кодов. Чтобы сделать это вам нужно изменить `atom.gyp` и полностью
|
||||
из исходных кодов. Чтобы сделать это Вам нужно изменить `atom.gyp` и полностью
|
||||
пересобрать Electron.
|
||||
|
||||
### grunt-build-atom-shell
|
||||
|
||||
Проверка и пересборка кода Electron довольно сложная задача, так что мы
|
||||
мы сделали файл-инструкцию для Grunt, который будет делать это автоматически:
|
||||
сделали файл-инструкцию для Grunt, который будет делать это автоматически:
|
||||
[grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell).
|
||||
|
||||
Этот файл автоматически просмотрит изменения в `.gyp` фалле, соберёт
|
||||
Этот файл автоматически просмотрит изменения в `.gyp` файле, соберёт
|
||||
Electron из исходных кодов и пересоберёт модули Node, чтобы всё подходило
|
||||
под новое имя.
|
||||
|
||||
## Инструменты
|
||||
|
||||
Вы также можете использовать инструменты оттретьих лиц,
|
||||
Вы также можете использовать инструменты от третьих лиц,
|
||||
которые сделают работу за вас:
|
||||
|
||||
* [electron-packager](https://github.com/maxogden/electron-packager)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Упаковка приложения
|
||||
|
||||
Чтобы смягчить [проблемы](https://github.com/joyent/node/issues/6960) с длинными
|
||||
именами под Windows, немного ускорить `require` и скрыть ваши исходные коды, вы
|
||||
именами под Windows, немного ускорить `require` и скрыть ваши исходные коды, Вы
|
||||
можете упаковать его в архив [asar][asar], немного поменяв исходный код.
|
||||
|
||||
## Генерация архива `asar`
|
||||
|
@ -25,16 +25,16 @@ $ asar pack your-app app.asar
|
|||
|
||||
## Использование архивов `asar`
|
||||
|
||||
В Electron есть два вида API: API Node, которые устанавливаются с помощью Node.Js и
|
||||
В Electron есть два вида API: API Node, которые устанавливаются с помощью Node.JS и
|
||||
веб API, которые предоставляюся Chromium. Оба предоставляют возможность считывать из
|
||||
архивов `asar`.
|
||||
|
||||
### Node API
|
||||
|
||||
С специальными патчами в Electron, части Node API вроде `fs.readFile` и `require`
|
||||
Со специальными патчами в Electron, части Node API вроде `fs.readFile` и `require`
|
||||
считают архивы `asar` виртуальными папками и файлы в них доступны как в обычных.
|
||||
|
||||
Например, у нас есть арихив `example.asar` в `/path/to`:
|
||||
Например, у нас есть архив `example.asar` в `/path/to`:
|
||||
|
||||
```bash
|
||||
$ asar list /path/to/example.asar
|
||||
|
@ -60,13 +60,13 @@ const fs = require('fs');
|
|||
fs.readdirSync('/path/to/example.asar');
|
||||
```
|
||||
|
||||
Ичпользуем модуль из архива:
|
||||
Используем модуль из архива:
|
||||
|
||||
```javascript
|
||||
require('/path/to/example.asar/dir/module.js');
|
||||
```
|
||||
|
||||
Вы также можете показывать веб страницы из архива `asar` через `BrowserWindow`:
|
||||
Вы также можете показывать веб-страницы из архива `asar` через `BrowserWindow`:
|
||||
|
||||
```javascript
|
||||
const BrowserWindow = require('electron').BrowserWindow;
|
||||
|
@ -76,7 +76,7 @@ win.loadURL('file:///path/to/example.asar/static/index.html');
|
|||
|
||||
### Веб API
|
||||
|
||||
На веб страницах файлы запрашиваются с помощью протокола `file:`. Как и в Node API
|
||||
На веб-страницах файлы запрашиваются с помощью протокола `file:`. Как и в Node API
|
||||
архивы `asar` считаются за директории.
|
||||
|
||||
Пример получения файла с помощью `$.get`:
|
||||
|
@ -93,7 +93,7 @@ $.get('file:///path/to/example.asar/file.txt', function(data) {
|
|||
|
||||
### Использование архива `asar` в качестве обычного файла
|
||||
|
||||
Для случаев, когда вам, например, нужно проверить хэш-сумму архива `asar`,
|
||||
Для случаев, когда Вам, например, нужно проверить хэш-сумму архива `asar`,
|
||||
нужно использовать архив как файл. Для этой цели существует встроенный модуль
|
||||
`original-fs`, который предоставляет доступ к `fs` без поддежки `asar`:
|
||||
|
||||
|
@ -101,6 +101,7 @@ $.get('file:///path/to/example.asar/file.txt', function(data) {
|
|||
var originalFs = require('original-fs');
|
||||
originalFs.readFileSync('/path/to/example.asar');
|
||||
```
|
||||
|
||||
Вы также можете выставить `process.noAsar` в `true`, чтобы выключить поддержку `asar`
|
||||
в модуле `fs`:
|
||||
|
||||
|
@ -112,7 +113,7 @@ fs.readFileSync('/path/to/example.asar');
|
|||
## Ограничения Node API
|
||||
|
||||
Хотя мы и старались как могли, чтобы сделать `asar` максимально похожим на папки,
|
||||
всё ещё существуют некоторые ограничения из-за низкоуровневой натуры Node API.
|
||||
всё ещё существуют некоторые ограничения из-за низкоуровневой архитектуры Node API.
|
||||
|
||||
### Архивы только для чтения
|
||||
|
||||
|
@ -121,8 +122,8 @@ fs.readFileSync('/path/to/example.asar');
|
|||
|
||||
### Нельзя установить рабочую директорию в архиве
|
||||
|
||||
Хотя архивы `asar` и считаются папками, они ими на самом деле не являются,
|
||||
так что вы не можете установить в них рабочую директорию. Передача
|
||||
Хотя архивы `asar` и считаются папками, они ими на самом деле не являются ими,
|
||||
так что Вы не можете установить в них рабочую директорию. Передача
|
||||
архивов `asar` в качестве аргумента `cwd` некоторым API также может вызывать ошибки.
|
||||
|
||||
|
||||
|
@ -154,18 +155,18 @@ API которым нужна распаковка:
|
|||
`child_process.spawn` и `child_process.execFile`, но только `execFile` может
|
||||
исполнять файлы из архивов `asar`.
|
||||
|
||||
Так вышло потому, что `exec` и `spawn` принимают `команду` а не `файл` как параметр,
|
||||
Так вышло потому, что `exec` и `spawn` принимают `команду`, а не `файл` как параметр,
|
||||
а `команды` исполняются в оболочке. Нет никакой реальной возможности проверить,
|
||||
реален ли файл или находится в архиве `asar`, и даже если мы смогли бы проверить,
|
||||
то неясно, как земенить путь к файлу без побочных эффектов.
|
||||
|
||||
## Добавление распакованых файлов в архив `asar`
|
||||
|
||||
Как говорилось выше, некоторые API Node будут распаковывать файлв,
|
||||
Как говорилось выше, некоторые API Node будут распаковывать файлы,
|
||||
чтобы их использовать. Кроме увеличенного потребления ресурсов это также
|
||||
может вызвать предупрждения от антивирусов.
|
||||
может вызвать предупреждения от антивирусов.
|
||||
|
||||
Чтобы обойти это, вы можете распаковать некоторые файлы, создавая архивы,
|
||||
Чтобы обойти это, Вы можете распаковать некоторые файлы, создавая архивы,
|
||||
с помощью опции `--unpack`. Пример показывает распаковку нативных модулей:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
#Руководство по утверждению вашего приложения в App Store
|
||||
|
||||
Начиная с версии v0.34.0 Electron позволяет вам сформировать данные для App Store к вашему приложению.
|
||||
Начиная с версии v0.34.0 Electron позволяет Вам сформировать данные для App Store к вашему приложению.
|
||||
Данное руководство представляет собой пошаговую инструкцию по созданию данных файлов.
|
||||
|
||||
Помните, что когда Вы подаете свое приложение на рассмотрение в App Store Вы должны обладать аккаунтом разработчика,
|
||||
|
@ -9,9 +9,7 @@
|
|||
|
||||
## Как отправить свое приложение на рассмотрение в App Store
|
||||
|
||||
Последующие шаги подробно описывают, что и в какой последовательности Вам требуется сделать, но не гарантируют что Ваше приложение
|
||||
будет рассмотрено Apple. Мы так же рекомендуем вам прочитать официальную документацию по оформлению своего приложения и информации к нему
|
||||
чтобы пройти проверку в App Store.
|
||||
Последующие шаги подробно описывают, что и в какой последовательности Вам требуется сделать, но не гарантируют, что Ваше приложение будет рассмотрено Apple. Мы также рекомендуем Вам прочитать официальную документацию по оформлению своего приложения и информации к нему, чтобы пройти проверку в App Store.
|
||||
|
||||
## Получение сертификата
|
||||
|
||||
|
@ -24,8 +22,9 @@
|
|||
|
||||
Во-первых, нам нужно подготовить два файла:
|
||||
|
||||
child.plist:
|
||||
|
||||
`child.plist`:
|
||||
|
||||
```xml
|
||||
<?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">
|
||||
<plist version="1.0">
|
||||
|
@ -36,9 +35,11 @@
|
|||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
parent.plist:
|
||||
|
||||
```
|
||||
|
||||
`parent.plist`:
|
||||
|
||||
```xml
|
||||
<?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">
|
||||
<plist version="1.0">
|
||||
|
@ -49,8 +50,11 @@
|
|||
<string>(allow mach-lookup (global-name-regex #"^org.chromium.Chromium.rohitfork.[0-9]+$"))</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
Затем подписываем свое приложение, с помощью специального сценария:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Имя вашего приложения.
|
||||
|
@ -79,42 +83,45 @@
|
|||
codesign -s "$APP_KEY" -f --entitlements parent.plist "$APP_PATH"
|
||||
|
||||
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH"
|
||||
|
||||
Если вы только начали разрабатывать под Mac OS X, то мы желаем Вам прочитать [App SandBox](https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html "Ссылка для новичков в разработке приложений для Mac OS X")
|
||||
```
|
||||
|
||||
## Обновление приложения
|
||||
Если вы только начали разрабатывать под Mac OS X, то мы советуем Вам прочитать [App SandBox](https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html "Ссылка для новичков в разработке приложений для Mac OS X")
|
||||
|
||||
После того, как Вы подписали свое приложение вы сможете загрузить его в Itunes Connect для обработки, убедитесь, что вы создали [запись](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html "ссылка на показ как создавать запись в Itunes Connect") перед отправкой.
|
||||
## Обновление приложения
|
||||
|
||||
После того, как Вы подписали свое приложение Вы сможете загрузить его в Itunes Connect для обработки, убедитесь, что Вы создали [запись](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html "ссылка на показ как создавать запись в Itunes Connect") перед отправкой.
|
||||
|
||||
## Объяснение использования 'temporary-exception'
|
||||
|
||||
Когда песочница Apple временно исключила ваше приложение, согласно [документации](https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html "Документация по исключениям") вам нужно объяснить насколько это важное исключение:
|
||||
Когда песочница Apple временно исключила ваше приложение, согласно [документации](https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html "Документация по исключениям") Вам нужно объяснить насколько это важное исключение:
|
||||
|
||||
>Примечание: если вы временно исключаете свое приложение, обязательно прочитайте и выполните рекомендации по правам на исключение.
|
||||
>Примечание: если Вы временно исключаете свое приложение, обязательно прочитайте и выполните рекомендации по правам на исключение.
|
||||
>которые предоставляются в Itunes Connect. Самое важное указать почему ваше приложение должно быть исключенно.
|
||||
|
||||
Вы можете объяснить, что ваше приложение построено на основе браузера Chromium, который использует Mach из-за его мульти-процесс архитектуры. Но есть еще вероятность, что ваше приложение не удалось проверить именно из-за этого.
|
||||
Вы можете объяснить, что ваше приложение построено на основе браузера Chromium, который использует Mach из-за его мультипроцессной архитектуры. Но есть еще вероятность, что ваше приложение не удалось проверить именно из-за этого.
|
||||
|
||||
## Отправка приложения на проверку
|
||||
|
||||
Следующие шаги описаны в официальной [документации](https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html "Официальная статья по отправке приложения на проверку")
|
||||
|
||||
#Ограничения в Mac App Store
|
||||
# Ограничения в Mac App Store
|
||||
|
||||
Для того чтобы удовлетворить всем просьбам SandBox App Store , некотоыре из модулей были отключены:
|
||||
Для того чтобы удовлетворить всем просьбам SandBox App Store, некоторые из модулей были отключены:
|
||||
- crashReporter
|
||||
- autoUpdater
|
||||
и следующие проблемы были несколько изменены:
|
||||
|
||||
А также следующие проблемы были несколько изменены:
|
||||
- Захват видео на некоторых машинах может не работать
|
||||
- Некоторые специалньые возможности могут не работать
|
||||
- Некоторые специальные возможности могут не работать
|
||||
- Приложения не будут в курсе изменения DNS
|
||||
Так же из за использования SandBox App Store некоторые возможности могут быть не доступны или ограничены, подробнее о ограничениях
|
||||
|
||||
Также из-за использования SandBox App Store некоторые возможности могут быть не доступны или ограничены, подробнее о ограничениях
|
||||
Вы можете прочитать в [документации](https://developer.apple.com/app-sandboxing/ "Ссылка на ограничения в SandBox AppStore")
|
||||
|
||||
# Криптографические алгоритмы котоыре использует Electron
|
||||
# Криптографические алгоритмы которые использует Electron
|
||||
|
||||
Смотря в какой стране и городе Вы находитесь Apple может потребовать от Вас задокументировать алгоритмы криптографии котоыре вы используете
|
||||
и если потребуется то попросит предоставить Вас копию регистрации вашего алгоритма шифрования.
|
||||
Смотря в какой стране и городе Вы находитесь, Apple может потребовать от Вас задокументировать алгоритмы криптографии которые Вы используете
|
||||
и если потребуется, то попросит Вас предоставить копию регистрации вашего алгоритма шифрования.
|
||||
|
||||
Electron использует следующие алгоритмы шифрования:
|
||||
- AES - NIST SP 800-38A, NIST SP 800-38D, RFC 3394
|
||||
|
@ -141,5 +148,5 @@ Electron использует следующие алгоритмы шифров
|
|||
- RC5 - http://people.csail.mit.edu/rivest/Rivest-rc5rev.pdf
|
||||
- RIPEMD - ISO/IEC 10118-3
|
||||
|
||||
Если Вы используете необычный алгоритм, то вот статья о том как получить разрешение на использование собственного алгоритма шифрования в
|
||||
Если Вы используете необычный алгоритм, то вот статья о том, как получить разрешение на использование собственного алгоритма шифрования в
|
||||
рамках закона США - [статья](https://pupeno.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/ "Статья о том как получить разрешение на свой алгоритм шифрования")
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Быстрый старт
|
||||
|
||||
Electron позволяет вам делать приложения для рабочего стола на чистом JavaScript,
|
||||
предоставляя среду с богатым API. Можете представлять его как Node.js, который
|
||||
ориентирован на рабочий стол, а не веб сервера.
|
||||
Electron позволяет Вам делать приложения для рабочего стола на чистом JavaScript,
|
||||
предоставляя среду с богатым API. Можете представлять его как Node.js приложение, которое
|
||||
ориентировано для рабочего стола, а не для веб сервера.
|
||||
|
||||
Это, однако, не значит, что Electron — лишь привязки к GUI билиотекам. На деле
|
||||
Electron использует веб-страницы как интерфейс, так что вы можете считать его
|
||||
Однако это не значит, что Electron — лишь привязка к GUI билиотекам. На деле
|
||||
Electron использует веб-страницы как интерфейс, так что Вы можете считать его
|
||||
небольшим Chroumium браузером, который контролируется с помощью JavaScript.
|
||||
|
||||
### Главный процесс
|
||||
|
@ -18,11 +18,11 @@ __главным процессом__. Скрипт, который работа
|
|||
|
||||
Так как Electron использует Chromium для показа веб-страниц,
|
||||
мульти-процессовая архитектура показа страниц Chromium тоже используется.
|
||||
Каждая веб-страницы в Electron работает в своём собственном процессе,
|
||||
Каждая веб-страница в Electron работает в своём собственном процессе,
|
||||
который называется __процесс-рендерер__.
|
||||
|
||||
В обычных браузерах веб-страницы обычно запускаются в "песочнице" и им недоступны
|
||||
реальные ресурсы компьютера. Пользователи Electron же могут использовать API
|
||||
реальные ресурсы компьютера. Пользователи Electron напротив могут использовать API
|
||||
Node.js на страницах, что допускает более низкоуровневую работу с операционной системой.
|
||||
|
||||
### Разница мужду главным процессом и процессом-рендерером
|
||||
|
@ -43,11 +43,11 @@ Node.js на страницах, что допускает более низко
|
|||
В Electron есть несолько способов общения между процессам. Например, модули
|
||||
[`ipcRenderer`](../api/ipc-renderer.md) и [`ipcMain`](../api/ipc-main.md) используются
|
||||
для отправки сообщений, а [remote](../api/remote.md) - для коммуникации в RPC стиле.
|
||||
В ЧАВО также есть пункт о том, [как разделять информацию между страницами][share-data]
|
||||
В FAQ также есть пункт о том, [как разделять информацию между страницами][share-data]
|
||||
|
||||
## Первое приложение на Electron
|
||||
|
||||
Как правило, приложение Electron структурировано следующим образом::
|
||||
Как правило, приложение Electron структурировано следующим образом:
|
||||
|
||||
```text
|
||||
your-app/
|
||||
|
@ -56,9 +56,9 @@ your-app/
|
|||
└── index.html
|
||||
```
|
||||
|
||||
Формат `package.json` точно такой же, как у модулей Node и сприпт, объявленый
|
||||
Формат `package.json` точно такой же, как у модулей Node и скрипт, объявленый
|
||||
как `main`, будет выполняться при запуске вашего приложения, работая в
|
||||
главном процессе. Например, ваш `package.json` может выглядеть вот так:
|
||||
главном процессе. Например, Ваш `package.json` может выглядеть вот так:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -82,12 +82,12 @@ const app = electron.app
|
|||
// Модуль, создающий окно приложения.
|
||||
const BrowserWindow = electron.BrowserWindow
|
||||
|
||||
// Удерживайте глобальное обращение к объекту окна, если вы так не сделаете, то
|
||||
// Удерживайте глобальное обращение к объекту окна, если Вы так не сделаете, то
|
||||
// окно само закроется после того, как объект будет собран сборщиком мусора.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Создаём окно браузера.
|
||||
// Создаём окно браузера
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600})
|
||||
|
||||
// и загружаем index.html приложения.
|
||||
|
@ -98,9 +98,9 @@ function createWindow () {
|
|||
|
||||
// Будет выполнено, когда пользователь закроет окно
|
||||
mainWindow.on('closed', function () {
|
||||
//Убрать обращение на объект окна, обычно стоит хранить окна в массиве,
|
||||
//если ваше приложение поддерживает несколько, сейчас стоит удалить
|
||||
//соответствующий элемент.
|
||||
// Убрать обращение на объект окна, обычно стоит хранить окна в массиве,
|
||||
// если ваше приложение поддерживает несколько, сейчас стоит удалить
|
||||
// соответствующий элемент.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ app.on('ready', createWindow)
|
|||
// Выйти, если все окна закрыты
|
||||
app.on('window-all-closed', function () {
|
||||
//На OS X приложение и его строка меню обычно остаются активными,
|
||||
//пока пользователь не завершит их с помощью Cmd + Q.
|
||||
//пока пользователь не завершит их с помощью `Cmd + Q`.
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
|
@ -129,12 +129,12 @@ app.on('activate', function () {
|
|||
}
|
||||
})
|
||||
|
||||
//В этот файл вы можете включить остальной код вашего главного процесса.
|
||||
//В этот файл Вы можете включить остальной код вашего главного процесса.
|
||||
//Вы также можете разложить его по отдельным файлам и подключить с помощью require.
|
||||
|
||||
```
|
||||
|
||||
Наконец, `index.html`, страница, которую вы хотите показать:
|
||||
Наконец, `index.html`, страница, которую Вы хотите показать:
|
||||
|
||||
```html
|
||||
<!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`](https://github.com/electron-userland/electron-prebuilt) — `npm` модуль,
|
||||
который содержит прекомпилированную версию Electron.
|
||||
|
||||
Если вы установили Electron глобально через `npm`, то вам нужно будет всего лишь
|
||||
Если вы установили Electron глобально через `npm`, то Вам нужно будет всего лишь
|
||||
запустить сдедующее в папке вашего проекта:
|
||||
|
||||
```bash
|
||||
electron .
|
||||
```
|
||||
|
||||
Если вы установили Electron локально, то выполните это:
|
||||
Если Вы установили Electron локально, то выполните это:
|
||||
|
||||
```bash
|
||||
./node_modules/.bin/electron .
|
||||
|
@ -177,7 +177,7 @@ electron .
|
|||
|
||||
### Исполняемые файлы Electron, скачанные вручную
|
||||
|
||||
Если вы скачали Electron вручную, то вы можете использовать
|
||||
Если Вы скачали Electron вручную, то Вы можете использовать
|
||||
исполняемые файлы прямо в папке вашего проекта.
|
||||
|
||||
#### Windows
|
||||
|
@ -198,27 +198,27 @@ $ ./electron/electron your-app/
|
|||
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
||||
```
|
||||
|
||||
`Electron.app` — часть реализного пакета Electron, вы можете скачать его
|
||||
`Electron.app` — часть реализного пакета Electron, Вы можете скачать его
|
||||
[тут](https://github.com/electron/electron/releases).
|
||||
|
||||
### Запустить как дистрибутив
|
||||
|
||||
Когда вы закончили написание вашего приложения, вы можете создать
|
||||
Когда Вы закончили написание вашего приложения, Вы можете создать
|
||||
дистрибутив, следуя инструкциям [отсюда](./application-distribution.md) и
|
||||
затем запустить полученное приложение.
|
||||
|
||||
### Попробуйте этот пример
|
||||
|
||||
Скопируйте и запустите этот обучающий код, ичпользуя репозиторий [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start)
|
||||
Скопируйте и запустите этот обучающий код, используя репозиторий [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start)
|
||||
|
||||
**Заметка**: Для запуска требуется [Git](https://git-scm.com) и [Node.js](https://nodejs.org/en/download/) (который включает в себя [npm](https://npmjs.org)).
|
||||
|
||||
```bash
|
||||
# Склонируйте репозиторий
|
||||
# Клонируем репозиторий
|
||||
$ git clone https://github.com/electron/electron-quick-start
|
||||
# Перейдите в папку репозитория
|
||||
# Переходим в папку скачанного репозитория
|
||||
$ cd electron-quick-start
|
||||
# Установите зависимости и запустите
|
||||
# Устанавливаем зависимости и запускаем
|
||||
$ npm install && npm start
|
||||
```
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
Платформы поддерживаемые Electron:
|
||||
# Платформы поддерживаемые Electron:
|
||||
|
||||
#OS X
|
||||
Поддерживает только 64-ых битные OS X. Минимально поддерживаемой версией является OS X 10.9
|
||||
Следующие платформы поддерживаются Electron:
|
||||
|
||||
#Windows
|
||||
Поддерживаются операционные систем Windows 7 и выше, старые операционные системы не поддерживаются (и не работают).
|
||||
Поддерживаются платформы на x86 и amd64 (64-разрядная) для Windows. Будьте внимательны, что ARM Windows не поддерживается на данный момент.
|
||||
### OS X
|
||||
|
||||
Поддерживает только 64-x битные OS X. Минимально поддерживаемой версией является OS X 10.9
|
||||
|
||||
### Windows
|
||||
|
||||
Поддерживаются операционные системы Windows 7 и выше, старые операционные системы не поддерживаются (и не работают).
|
||||
|
||||
Поддерживаются бинарники `x86` и `amd64` (x64) для Windows. Будьте внимательны, `ARM` версия Windows не поддерживается на данный момент.
|
||||
|
||||
### Linux
|
||||
|
||||
Подготовленные бинарники Electron для `ia32`(`i686`) и `x64`(`amd64`) архитектур сделаны на Ubuntu 12.04,
|
||||
`arm` бинарник сделан на ARM v7 с hard-float ABI и NEON для Debian Wheezy.
|
||||
|
||||
#Linux
|
||||
Поддерживает архитектуры ia32(i686 в) и x64(amd64).
|
||||
Гарантированно будет работать в дистрибутивах:
|
||||
- Ubuntu 12.04 и выше
|
||||
- Fedora 21
|
||||
- Debian 8
|
||||
|
||||
* Ubuntu 12.04 и выше
|
||||
* Fedora 21
|
||||
* Debian 8
|
||||
|
|
|
@ -32,7 +32,7 @@ app.on('window-all-closed', function() {
|
|||
|
||||
当所有的窗口都被关闭时触发。
|
||||
|
||||
这个时间仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`,
|
||||
这个事件仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`,
|
||||
或者开发者调用了 `app.quit()` ,Electron 将会先尝试关闭所有的窗口再触发 `will-quit` 事件,
|
||||
在这种情况下 `window-all-closed` 不会被触发。
|
||||
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
# Mac App Store 应用提交向导
|
||||
|
||||
自从 v0.34.0, Electron 就允许提交应用包到 Mac App Store
|
||||
(MAS) . 这个向导提供的信息有 : 如何提交应用和 MAS 构建的限制.
|
||||
自从 v0.34.0,Electron 就允许提交应用包到 Mac App Store
|
||||
(MAS)。这个向导提供的信息有: 如何提交应用和 MAS 构建的限制。
|
||||
|
||||
__注意:__ 从 v0.36.0,当应用成为沙箱之后,会有一个 bug 阻止 GPU 进程开启 , 所以在这个 bug 修复之前,建议使用 v0.35.x .更多查看 [issue #3871][issue-3871] .
|
||||
|
||||
__注意:__ 提交应用到 Mac App Store 需要参加 [Apple Developer
|
||||
Program][developer-program] , 这需要花钱.
|
||||
__注意:__ 提交应用到 Mac App Store 需要参加 [Apple Developer
|
||||
Program][developer-program],这需要额外花费。
|
||||
|
||||
## 如何提交
|
||||
|
||||
下面步骤介绍了一个简单的提交应用到商店方法.然而,这些步骤不能保证你的应用被 Apple 接受;你仍然需要阅读 Apple 的 [Submitting Your App][submitting-your-app] 关于如何满足 Mac App Store 要求的向导.
|
||||
下面步骤介绍了一个简单的提交应用到商店方法。然而,这些步骤不能保证你的应用被 Apple 接受;你仍然需要阅读 Apple 的 [Submitting Your App][submitting-your-app] 关于如何满足 Mac App Store 要求的向导。
|
||||
|
||||
### 获得证书
|
||||
|
||||
为了提交应用到商店,首先需要从 Apple 获得一个证书.可以遵循 [existing guides][nwjs-guide].
|
||||
为了提交应用到商店,首先需要从 Apple 获得一个证书。可以遵循 [现有向导][nwjs-guide]。
|
||||
|
||||
### App 签名
|
||||
### 软件签名
|
||||
|
||||
获得证书之后,你可以使用 [Application Distribution](application-distribution.md) 打包你的应用, 然后前往提交你的应用.这个步骤基本上和其他程序一样,但是这 key 一个个的标识 Electron 的每个依赖.
|
||||
获得证书之后,你可以使用 [应用部署](application-distribution.md) 打包你的应用,之后进行提交。
|
||||
|
||||
首先,你需要准备2个授权文件 .
|
||||
首先,你需要在软件包内的 `Info.plist` 中增添一项 `ElectronTeamID`:
|
||||
|
||||
```xml
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
...
|
||||
<key>ElectronTeamID</key>
|
||||
<string>TEAM_ID</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
之后,你需要准备2个授权文件。
|
||||
|
||||
`child.plist`:
|
||||
|
||||
|
@ -46,67 +56,76 @@ Program][developer-program] , 这需要花钱.
|
|||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<string>TEAM_ID.your.bundle.id</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
然后使用下面的脚本标识你的应用 :
|
||||
请注意上述 `TEAM_ID` 对应开发者账户的 Team ID,`your.bundle.id` 对应软件打包时使用的 Bundle ID。
|
||||
|
||||
然后使用下面的脚本签名你的应用:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Name of your app.
|
||||
# 应用名称
|
||||
APP="YourApp"
|
||||
# The path of you app to sign.
|
||||
APP_PATH="/path/to/YouApp.app"
|
||||
# The path to the location you want to put the signed package.
|
||||
# 应用路径
|
||||
APP_PATH="/path/to/YourApp.app"
|
||||
# 生成安装包路径
|
||||
RESULT_PATH="~/Desktop/$APP.pkg"
|
||||
# The name of certificates you requested.
|
||||
# 开发者应用签名证书
|
||||
APP_KEY="3rd Party Mac Developer Application: Company Name (APPIDENTITY)"
|
||||
INSTALLER_KEY="3rd Party Mac Developer Installer: Company Name (APPIDENTITY)"
|
||||
# 授权文件路径
|
||||
CHILD_PLIST="/path/to/child.plist"
|
||||
PARENT_PLIST="/path/to/parent.plist"
|
||||
|
||||
FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
|
||||
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A"
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper.app/"
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper EH.app/"
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP Helper NP.app/"
|
||||
if [ -d "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A" ]; then
|
||||
# Signing a non-MAS build.
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Mantle.framework/Versions/A"
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/ReactiveCocoa.framework/Versions/A"
|
||||
codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/Squirrel.framework/Versions/A"
|
||||
fi
|
||||
codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libnode.dylib"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/Contents/MacOS/$APP Helper EH"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/Contents/MacOS/$APP Helper NP"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacOS/$APP"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH"
|
||||
|
||||
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH"
|
||||
```
|
||||
如果你是 OS X 下的应用沙箱使用新手,应当仔细阅读 Apple 的 [Enabling App Sandbox][enable-app-sandbox] 来有一点基础,然后向授权文件添加你的应用需要的许可 keys .
|
||||
|
||||
### 上传你的应用并检查提交
|
||||
如果你是 OS X 下的应用沙箱使用新手,应当仔细阅读 Apple 的 [Enabling App Sandbox][enable-app-sandbox] 了解一些基础,然后在授权文件 (entitlements files) 内添加你的应用需要的许可。
|
||||
|
||||
在签名应用之后,可以使用应用 Loader 来上传到 iTunes 链接处理 , 确保在上传之前你已经 [created a record][create-record]. 然后你能 [submit your app for review][submit-for-review].
|
||||
### 上传你的应用并检查提交
|
||||
|
||||
## MAS构建限制
|
||||
在签名应用之后,你可以使用 Application Loader 上传软件到 iTunes Connect 进行处理。请确保在上传之前你已经 [创建应用记录][create-record],再 [提交进行审核][submit-for-review]。
|
||||
|
||||
为了让你的应用沙箱满足所有条件,在 MAS 构建的时候,下面的模块被禁用了 :
|
||||
## MAS 构建限制
|
||||
|
||||
为了让你的应用满足沙箱的所有条件,在 MAS 构建的时候,下面的模块已被禁用:
|
||||
|
||||
* `crashReporter`
|
||||
* `autoUpdater`
|
||||
|
||||
并且下面的行为也改变了:
|
||||
|
||||
* 一些机子的视频采集功能无效.
|
||||
* 某些特征不可访问.
|
||||
* Apps 不可识别 DNS 改变.
|
||||
* 一些视频采集功能无效。
|
||||
* 某些辅助功能无法访问。
|
||||
* 应用无法检测 DNS 变化。
|
||||
|
||||
也由于应用沙箱的使用方法,应用可以访问的资源被严格限制了 ; 阅读更多信息 [App Sandboxing][app-sandboxing] .
|
||||
也由于应用沙箱的使用方法,应用可以访问的资源被严格限制了;阅读更多信息 [App Sandboxing][app-sandboxing]。
|
||||
|
||||
## Electron 使用的加密算法
|
||||
|
||||
取决于你所在地方的国家和地区 , Mac App Store 或许需要记录你应用的加密算法 , 甚至要求你提交一个 U.S 加密注册(ERN) 许可的复印件.
|
||||
取决于你所在地方的国家和地区,Mac App Store 或许需要记录你应用的加密算法,甚至要求你提交一个 U.S. 加密注册 (ERN) 许可的复印件。
|
||||
|
||||
Electron 使用下列加密算法:
|
||||
Electron 使用下列加密算法:
|
||||
|
||||
* AES - [NIST SP 800-38A](http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf), [NIST SP 800-38D](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf), [RFC 3394](http://www.ietf.org/rfc/rfc3394.txt)
|
||||
* HMAC - [FIPS 198-1](http://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf)
|
||||
|
@ -132,9 +151,9 @@ Electron 使用下列加密算法:
|
|||
* RC5 - http://people.csail.mit.edu/rivest/Rivest-rc5rev.pdf
|
||||
* RIPEMD - [ISO/IEC 10118-3](http://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2FIEC%2010118-3:2004)
|
||||
|
||||
如何获取 ERN 许可, 可看这篇文章: [How to legally
|
||||
如何获取 ERN 许可, 可看这篇文章:[How to legally
|
||||
submit an app to Apple’s App Store when it uses encryption (or how to obtain an
|
||||
ERN)][ern-tutorial].
|
||||
ERN)][ern-tutorial]。
|
||||
|
||||
[developer-program]: https://developer.apple.com/support/compare-memberships/
|
||||
[submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html
|
||||
|
@ -144,4 +163,4 @@ ERN)][ern-tutorial].
|
|||
[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
|
||||
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
|
||||
[issue-3871]: https://github.com/electron/electron/issues/3871
|
||||
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||
[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 来创造桌面应用。你可以把它看作是专注于桌面应用而不是 web 服务器的,io.js 的一个变体。
|
||||
Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作一个专注于桌面应用的 Node.js 的变体,而不是 Web 服务器。
|
||||
|
||||
这不意味着 Electron 是绑定了 GUI 库的 JavaScript。相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。
|
||||
|
||||
|
@ -14,13 +13,13 @@ Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造
|
|||
在一般浏览器中,网页通常会在沙盒环境下运行,并且不允许访问原生资源。然而,Electron 用户拥有在网页中调用 io.js 的 APIs 的能力,可以与底层操作系统直接交互。
|
||||
|
||||
## 主进程与渲染进程的区别
|
||||
主进程使用 BrowserWindow 实例创建网页。每个 BrowserWindow 实例都在自己的渲染进程里运行着一个网页。当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。
|
||||
主进程使用 `BrowserWindow` 实例创建页面。每个 `BrowserWindow` 实例都在自己的渲染进程里运行页面。当一个 `BrowserWindow` 实例被销毁后,相应的渲染进程也会被终止。
|
||||
|
||||
主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的网页。
|
||||
主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的页面。
|
||||
|
||||
由于在网页里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在网页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。
|
||||
由于在页面里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。
|
||||
|
||||
在 Electron,我们提供用于在主进程与渲染进程之间通讯的 [ipc][1] 模块。并且也有一个远程进程调用风格的通讯模块 [remote][2]。
|
||||
在 Electron,我们提供几种方法用于主进程和渲染进程之间的通讯。像 [ipcRenderer][1] 和 [ipcMain][2] 模块用于发送消息, [remote][3] 模块用于 RPC 方式通讯。这些内容都可以在一个 FAQ 中查看 [how to share data between web pages][4]。
|
||||
|
||||
# 打造你第一个 Electron 应用
|
||||
大体上,一个 Electron 应用的目录结构如下:
|
||||
|
@ -42,53 +41,72 @@ your-app/
|
|||
|
||||
`main.js` 应该用于创建窗口和处理系统事件,一个典型的例子如下:
|
||||
```javascript
|
||||
var app = require('app'); // 控制应用生命周期的模块。
|
||||
var BrowserWindow = require('browser-window'); // 创建原生浏览器窗口的模块
|
||||
const electron = require('electron');
|
||||
// 控制应用生命周期的模块。
|
||||
const {app} = electron;
|
||||
// 创建原生浏览器窗口的模块。
|
||||
const {BrowserWindow} = electron;
|
||||
|
||||
// 保持一个对于 window 对象的全局引用,不然,当 JavaScript 被 GC,
|
||||
// window 会被自动地关闭
|
||||
var mainWindow = null;
|
||||
// 保持一个对于 window 对象的全局引用,如果你不这样做,
|
||||
// 当 JavaScript 对象被垃圾回收, window 会被自动地关闭
|
||||
let mainWindow;
|
||||
|
||||
// 当所有窗口被关闭了,退出。
|
||||
app.on('window-all-closed', function() {
|
||||
// 在 OS X 上,通常用户在明确地按下 Cmd + Q 之前
|
||||
// 应用会保持活动状态
|
||||
if (process.platform != 'darwin') {
|
||||
function createWindow() {
|
||||
// 创建浏览器窗口。
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600});
|
||||
|
||||
// 加载应用的 index.html。
|
||||
mainWindow.loadURL(`file://${__dirname}/index.html`);
|
||||
|
||||
// 启用开发工具。
|
||||
mainWindow.webContents.openDevTools();
|
||||
|
||||
// 当 window 被关闭,这个事件会被触发。
|
||||
mainWindow.on('closed', () => {
|
||||
// 取消引用 window 对象,如果你的应用支持多窗口的话,
|
||||
// 通常会把多个 window 对象存放在一个数组里面,
|
||||
// 与此同时,你应该删除相应的元素。
|
||||
mainWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow);
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
// 当 Electron 完成了初始化并且准备创建浏览器窗口的时候
|
||||
// 这个方法就被调用
|
||||
app.on('ready', function() {
|
||||
// 创建浏览器窗口。
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600});
|
||||
|
||||
// 加载应用的 index.html
|
||||
mainWindow.loadURL('file://' + __dirname + '/index.html');
|
||||
|
||||
// 打开开发工具
|
||||
mainWindow.openDevTools();
|
||||
|
||||
// 当 window 被关闭,这个事件会被发出
|
||||
mainWindow.on('closed', function() {
|
||||
// 取消引用 window 对象,如果你的应用支持多窗口的话,
|
||||
// 通常会把多个 window 对象存放在一个数组里面,
|
||||
// 但这次不是。
|
||||
mainWindow = null;
|
||||
});
|
||||
app.on('activate', () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
```
|
||||
最后,你想展示的 `index.html` :
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<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>.
|
||||
</body>
|
||||
</html>
|
||||
|
@ -97,8 +115,9 @@ app.on('ready', function() {
|
|||
# 运行你的应用
|
||||
一旦你创建了最初的 `main.js`, `index.html` 和 `package.json` 这几个文件,你可能会想尝试在本地运行并测试,看看是不是和期望的那样正常运行。
|
||||
|
||||
## electron-prebuild
|
||||
如果你已经用 `npm` 全局安装了 `electron-prebuilt`,你只需要按照如下方式直接运行你的应用:
|
||||
## electron-prebuilt
|
||||
[electron-prebuilt][5] 是一个 `npm` 模块,包含所使用的 Electron 预编译版本。
|
||||
如果你已经用 `npm` 全局安装了它,你只需要按照如下方式直接运行你的应用:
|
||||
```bash
|
||||
electron .
|
||||
```
|
||||
|
@ -121,13 +140,32 @@ $ ./electron/electron your-app/
|
|||
```bash
|
||||
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
||||
```
|
||||
`Electron.app` 里面是 Electron 发布包,你可以在[这里][3]下载到。
|
||||
`Electron.app` 里面是 Electron 发布包,你可以在 [这里][6] 下载到。
|
||||
|
||||
# 以发行版本运行
|
||||
在你完成了你的应用后,你可以按照[应用部署][4]指导发布一个版本,并且以已经打包好的形式运行应用。
|
||||
在你完成了你的应用后,你可以按照 [应用部署][7] 指导发布一个版本,并且以已经打包好的形式运行应用。
|
||||
|
||||
# 参照下面例子
|
||||
复制并且运行这个库 [electron/electron-quick-start][8]。
|
||||
|
||||
[1]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/ipc-main-process.md
|
||||
[2]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/api/remote.md
|
||||
[3]: https://github.com/electron/electron/releases
|
||||
[4]: https://github.com/electron/electron/blob/master/docs-translations/zh-CN/tutorial/application-distribution.md
|
||||
*注意:*运行时需要你的系统已经安装了 [Git][9] 和 [Node.js][10](包含 [npm][11])。
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
$ git clone https://github.com/electron/electron-quick-start
|
||||
# Go into the repository
|
||||
$ cd electron-quick-start
|
||||
# Install dependencies and run the app
|
||||
$ npm install && npm start
|
||||
```
|
||||
[1]: https://github.com/electron/electron/blob/v1.1.3/docs/api/ipc-renderer.md
|
||||
[2]: https://github.com/electron/electron/blob/v1.1.3/docs/api/ipc-main.md
|
||||
[3]: https://github.com/electron/electron/blob/v1.1.3/docs/api/remote.md
|
||||
[4]: https://github.com/electron/electron/blob/v1.1.3/docs/faq/electron-faq.md#how-to-share-data-between-web-pages
|
||||
[5]: https://github.com/electron-userland/electron-prebuilt
|
||||
[6]: https://github.com/electron/electron/releases
|
||||
[7]: https://github.com/electron/electron/blob/v1.1.3/docs/tutorial/application-distribution.md
|
||||
[8]: https://github.com/electron/electron-quick-start
|
||||
[9]: https://git-scm.com/
|
||||
[10]: https://nodejs.org/en/download/
|
||||
[11]: https://www.npmjs.com/
|
||||
|
|
|
@ -346,6 +346,7 @@ You can request the following paths by the name:
|
|||
* `music` Directory for a user's music.
|
||||
* `pictures` Directory for a user's pictures.
|
||||
* `videos` Directory for a user's videos.
|
||||
* `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin.
|
||||
|
||||
### `app.setPath(name, path)`
|
||||
|
||||
|
@ -561,6 +562,12 @@ Imports the certificate in pkcs12 format into the platform certificate store.
|
|||
`callback` is called with the `result` of import operation, a value of `0`
|
||||
indicates success while any other value indicates failure according to chromium [net_error_list](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
|
||||
|
||||
### `app.disableHardwareAcceleration()`
|
||||
|
||||
Disables hardware acceleration for current app.
|
||||
|
||||
This method can only be called before app is ready.
|
||||
|
||||
### `app.commandLine.appendSwitch(switch[, value])`
|
||||
|
||||
Append a switch (with optional `value`) to Chromium's command line.
|
||||
|
|
|
@ -175,9 +175,13 @@ The `webPreferences` option is an object that can have following properties:
|
|||
* `scrollBounce` Boolean - Enables scroll bounce (rubber banding) effect on
|
||||
OS X. Default is `false`.
|
||||
* `blinkFeatures` String - A list of feature strings separated by `,`, like
|
||||
`CSSVariables,KeyboardEventKey`. The full list of supported feature strings
|
||||
can be found in the [setFeatureEnabledFromString][blink-feature-string]
|
||||
function.
|
||||
`CSSVariables,KeyboardEventKey` to enable. The full list of supported feature
|
||||
strings can be found in the [RuntimeEnabledFeatures.in][blink-feature-string]
|
||||
file.
|
||||
* `disableBlinkFeatures` String - A list of feature strings separated by `,`,
|
||||
like `CSSVariables,KeyboardEventKey` to disable. The full list of supported
|
||||
feature strings can be found in the
|
||||
[RuntimeEnabledFeatures.in][blink-feature-string] file.
|
||||
* `defaultFontFamily` Object - Sets the default font for the font-family.
|
||||
* `standard` String - Defaults to `Times New Roman`.
|
||||
* `serif` String - Defaults to `Times New Roman`.
|
||||
|
@ -380,7 +384,11 @@ Find a window according to its ID.
|
|||
Adds DevTools extension located at `path`, and returns extension's name.
|
||||
|
||||
The extension will be remembered so you only need to call this API once, this
|
||||
API is not for programming use.
|
||||
API is not for programming use. If you try to add an extension that has already
|
||||
been loaded, this method will not return and instead log a warning to the
|
||||
console.
|
||||
|
||||
Method will also not return if the extension's manifest is missing or incomplete.
|
||||
|
||||
### `BrowserWindow.removeDevToolsExtension(name)`
|
||||
|
||||
|
@ -877,7 +885,7 @@ The `flags` is an array that can include following `String`s:
|
|||
|
||||
### `win.showDefinitionForSelection()` _OS X_
|
||||
|
||||
Shows pop-up dictionary that searches the selected word on the page.
|
||||
Same as `webContents.showDefinitionForSelection()`.
|
||||
|
||||
### `win.setIcon(icon)` _Windows_ _Linux_
|
||||
|
||||
|
@ -924,10 +932,14 @@ Returns whether the window is visible on all workspaces.
|
|||
|
||||
**Note:** This API always returns false on Windows.
|
||||
|
||||
### `win.setIgnoreMouseEvents(ignore)` _OS X_
|
||||
### `win.setIgnoreMouseEvents(ignore)`
|
||||
|
||||
* `ignore` Boolean
|
||||
|
||||
Ignore all moused events that happened in the window.
|
||||
Makes the window ignore all mouse events.
|
||||
|
||||
[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=576
|
||||
All mouse events happened in this window will be passed to the window below
|
||||
this window, but if this window has focus, it will still receive keyboard
|
||||
events.
|
||||
|
||||
[blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
|
||||
|
|
|
@ -2,49 +2,70 @@
|
|||
|
||||
> Control file downloads from remote sources.
|
||||
|
||||
`DownloadItem` is an EventEmitter that represents a download item in Electron.
|
||||
It is used in `will-download` event of `Session` module, and allows users to
|
||||
`DownloadItem` is an `EventEmitter` that represents a download item in Electron.
|
||||
It is used in `will-download` event of `Session` class, and allows users to
|
||||
control the download item.
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
win.webContents.session.on('will-download', (event, item, webContents) => {
|
||||
// Set the save path, making Electron not to prompt a save dialog.
|
||||
item.setSavePath('/tmp/save.pdf');
|
||||
console.log(item.getMimeType());
|
||||
console.log(item.getFilename());
|
||||
console.log(item.getTotalBytes());
|
||||
item.on('updated', () => {
|
||||
console.log('Received bytes: ' + item.getReceivedBytes());
|
||||
});
|
||||
item.on('done', (e, state) => {
|
||||
if (state === 'completed') {
|
||||
console.log('Download successfully');
|
||||
} else {
|
||||
console.log('Download is cancelled or interrupted that can\'t be resumed');
|
||||
item.setSavePath('/tmp/save.pdf')
|
||||
|
||||
item.on('updated', (event, state) => {
|
||||
if (state === 'interrupted') {
|
||||
console.log('Download is interrupted but can be resumed')
|
||||
} else if (state === 'progressing') {
|
||||
if (item.isPaused()) {
|
||||
console.log('Download is paused')
|
||||
} else {
|
||||
console.log(`Received bytes: ${item.getReceivedBytes()}`)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
item.once('done', (event, state) => {
|
||||
if (state === 'completed') {
|
||||
console.log('Download successfully')
|
||||
} else {
|
||||
console.log(`Download failed: ${state}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
### Event: 'updated'
|
||||
|
||||
Emits when the `downloadItem` gets updated.
|
||||
|
||||
### Event: 'done'
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `state` String
|
||||
* `completed` - The download completed successfully.
|
||||
* `cancelled` - The download has been cancelled.
|
||||
* `interrupted` - An error broke the connection with the file server.
|
||||
|
||||
Emits when the download is in a terminal state. This includes a completed
|
||||
Emitted when the download has been updated and is not done.
|
||||
|
||||
The `state` can be one of following:
|
||||
|
||||
* `progressing` - The download is in-progress.
|
||||
* `interrupted` - The download has interrupted and can be resumed.
|
||||
|
||||
### Event: 'done'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `state` String
|
||||
|
||||
Emitted when the download is in a terminal state. This includes a completed
|
||||
download, a cancelled download(via `downloadItem.cancel()`), and interrupted
|
||||
download that can't be resumed.
|
||||
|
||||
The `state` can be one of following:
|
||||
|
||||
* `completed` - The download completed successfully.
|
||||
* `cancelled` - The download has been cancelled.
|
||||
* `interrupted` - The download has interrupted and can not resume.
|
||||
|
||||
## Methods
|
||||
|
||||
The `downloadItem` object has the following methods:
|
||||
|
@ -61,10 +82,18 @@ routine to determine the save path(Usually prompts a save dialog).
|
|||
|
||||
Pauses the download.
|
||||
|
||||
### `downloadItem.isPaused()`
|
||||
|
||||
Returns whether the download is paused.
|
||||
|
||||
### `downloadItem.resume()`
|
||||
|
||||
Resumes the download that has been paused.
|
||||
|
||||
### `downloadItem.canResume()`
|
||||
|
||||
Resumes whether the download can resume.
|
||||
|
||||
### `downloadItem.cancel()`
|
||||
|
||||
Cancels the download operation.
|
||||
|
@ -102,3 +131,14 @@ Returns a `Integer` represents the received bytes of the download item.
|
|||
|
||||
Returns a `String` represents the Content-Disposition field from the response
|
||||
header.
|
||||
|
||||
### `downloadItem.getState()`
|
||||
|
||||
Returns current state as `String`.
|
||||
|
||||
Possible values are:
|
||||
|
||||
* `progressing` - The download is in-progress.
|
||||
* `completed` - The download completed successfully.
|
||||
* `cancelled` - The download has been cancelled.
|
||||
* `interrupted` - The download has interrupted.
|
||||
|
|
|
@ -14,8 +14,8 @@ To create a frameless window, you need to set `frame` to `false` in
|
|||
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron');
|
||||
let win = new BrowserWindow({width: 800, height: 600, frame: false});
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({width: 800, height: 600, frame: false})
|
||||
```
|
||||
|
||||
### Alternatives on OS X
|
||||
|
@ -28,7 +28,7 @@ the window controls ("traffic lights") for standard window actions.
|
|||
You can do so by specifying the new `titleBarStyle` option:
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({titleBarStyle: 'hidden'});
|
||||
let win = new BrowserWindow({titleBarStyle: 'hidden'})
|
||||
```
|
||||
|
||||
## Transparent window
|
||||
|
@ -37,7 +37,7 @@ By setting the `transparent` option to `true`, you can also make the frameless
|
|||
window transparent:
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({transparent: true, frame: false});
|
||||
let win = new BrowserWindow({transparent: true, frame: false})
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
@ -59,6 +59,16 @@ let win = new BrowserWindow({transparent: true, frame: false});
|
|||
Linux.
|
||||
* On Mac the native window shadow will not be shown on a transparent window.
|
||||
|
||||
## Click-through window
|
||||
|
||||
To create a click-through window, i.e. making the window ignore all mouse
|
||||
events, you can call the [win.setIgnoreMouseEvents(ignore)][ignore-mouse-events]
|
||||
API:
|
||||
|
||||
```javascript
|
||||
win.setIgnoreMouseEvents(true)
|
||||
```
|
||||
|
||||
## Draggable region
|
||||
|
||||
By default, the frameless window is non-draggable. Apps need to specify
|
||||
|
@ -108,3 +118,5 @@ On some platforms, the draggable area will be treated as a non-client frame, so
|
|||
when you right click on it a system menu will pop up. To make the context menu
|
||||
behave correctly on all platforms you should never use a custom context menu on
|
||||
draggable areas.
|
||||
|
||||
[ignore-mouse-events]: browser-window.md#winsetignoremouseeventsignore
|
||||
|
|
|
@ -46,7 +46,9 @@ The `role` property can have following values:
|
|||
* `cut`
|
||||
* `copy`
|
||||
* `paste`
|
||||
* `pasteandmatchstyle`
|
||||
* `selectall`
|
||||
* `delete`
|
||||
* `minimize` - Minimize current window
|
||||
* `close` - Close current window
|
||||
|
||||
|
|
|
@ -103,6 +103,12 @@ Creates an empty `nativeImage` instance.
|
|||
|
||||
Creates a new `nativeImage` instance from a file located at `path`.
|
||||
|
||||
```javascript
|
||||
const nativeImage = require('electron').nativeImage;
|
||||
|
||||
let image = nativeImage.createFromPath('/Users/somebody/images/icon.png');
|
||||
```
|
||||
|
||||
### `nativeImage.createFromBuffer(buffer[, scaleFactor])`
|
||||
|
||||
* `buffer` [Buffer][buffer]
|
||||
|
@ -121,12 +127,6 @@ Creates a new `nativeImage` instance from `dataURL`.
|
|||
|
||||
The following methods are available on instances of `nativeImage`:
|
||||
|
||||
```javascript
|
||||
const nativeImage = require('electron').nativeImage;
|
||||
|
||||
let image = nativeImage.createFromPath('/Users/somebody/images/icon.png');
|
||||
```
|
||||
|
||||
### `image.toPng()`
|
||||
|
||||
Returns a [Buffer][buffer] that contains the image's `PNG` encoded data.
|
||||
|
|
|
@ -80,7 +80,7 @@ exports.withLocalCallback = () => {
|
|||
|
||||
```javascript
|
||||
// renderer process
|
||||
const mapNumbers = require('remote').require('./mapNumbers');
|
||||
const mapNumbers = require('electron').remote.require('./mapNumbers');
|
||||
|
||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
||||
|
||||
|
|
|
@ -9,44 +9,42 @@ emitted (by invoking or requiring it).
|
|||
|
||||
**Note:** In the renderer / DevTools, `window.screen` is a reserved DOM
|
||||
property, so writing `let {screen} = require('electron')` will not work.
|
||||
In our examples below, we use `electronScreen` as the variable name instead.
|
||||
|
||||
An example of creating a window that fills the whole screen:
|
||||
|
||||
```javascript
|
||||
const {app, BrowserWindow, screen: electronScreen} = require('electron');
|
||||
const electron = require('electron')
|
||||
const {app, BrowserWindow} = electron
|
||||
|
||||
let win;
|
||||
let win
|
||||
|
||||
app.on('ready', () => {
|
||||
const {width, height} = electronScreen.getPrimaryDisplay().workAreaSize;
|
||||
win = new BrowserWindow({width, height});
|
||||
const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
|
||||
win = new BrowserWindow({width, height})
|
||||
});
|
||||
```
|
||||
|
||||
Another example of creating a window in the external display:
|
||||
|
||||
```javascript
|
||||
const {app, BrowserWindow, screen: electronScreen} = require('electron');
|
||||
const electron = require('electron')
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
let win;
|
||||
let win
|
||||
|
||||
app.on('ready', () => {
|
||||
let displays = electronScreen.getAllDisplays();
|
||||
let externalDisplay = null;
|
||||
for (let i in displays) {
|
||||
if (displays[i].bounds.x !== 0 || displays[i].bounds.y !== 0) {
|
||||
externalDisplay = displays[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
let displays = electron.screen.getAllDisplays()
|
||||
let externalDisplay = displays.find((display) => {
|
||||
return display.bounds.x !== 0 || display.bounds.y !== 0
|
||||
})
|
||||
|
||||
if (externalDisplay) {
|
||||
win = new BrowserWindow({
|
||||
x: externalDisplay.bounds.x + 50,
|
||||
y: externalDisplay.bounds.y + 50
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
## The `Display` object
|
||||
|
|
|
@ -206,7 +206,7 @@ Sets the proxy settings.
|
|||
When `pacScript` and `proxyRules` are provided together, the `proxyRules`
|
||||
option is ignored and `pacScript` configuration is applied.
|
||||
|
||||
The `proxyRules` has to follow the rules bellow:
|
||||
The `proxyRules` has to follow the rules below:
|
||||
|
||||
```
|
||||
proxyRules = schemeProxies[";"<schemeProxies>]
|
||||
|
@ -547,3 +547,22 @@ The `listener` will be called with `listener(details)` when an error occurs.
|
|||
* `timestamp` Double
|
||||
* `fromCache` Boolean
|
||||
* `error` String - The error description.
|
||||
|
||||
#### `ses.protocol`
|
||||
|
||||
Returns an instance of [protocol](protocol.md) module for this session.
|
||||
|
||||
```javascript
|
||||
const {app, session} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
app.on('ready', function () {
|
||||
const protocol = session.fromPartition(partitionName).protocol
|
||||
protocol.registerFileProtocol('atom', function (request, callback) {
|
||||
var url = request.url.substr(7)
|
||||
callback({path: path.normalize(__dirname + '/' + url)})
|
||||
}, function (error) {
|
||||
if (error)
|
||||
console.error('Failed to register protocol')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -29,12 +29,12 @@ __Platform limitations:__
|
|||
* When app indicator is used on Linux, the `click` event is ignored.
|
||||
* On Linux in order for changes made to individual `MenuItem`s to take effect,
|
||||
you have to call `setContextMenu` again. For example:
|
||||
* On Windows it is recommended to use `ICO` icons to get best visual effects.
|
||||
|
||||
```javascript
|
||||
contextMenu.items[2].checked = false;
|
||||
appIcon.setContextMenu(contextMenu);
|
||||
```
|
||||
* On Windows it is recommended to use `ICO` icons to get best visual effects.
|
||||
|
||||
If you want to keep exact same behaviors on all platforms, you should not
|
||||
rely on the `click` event and always attach a context menu to the tray icon.
|
||||
|
|
|
@ -288,6 +288,15 @@ a meta tag:
|
|||
<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'
|
||||
|
||||
Returns:
|
||||
|
@ -948,6 +957,10 @@ win.webContents.on('did-finish-load', () => {
|
|||
});
|
||||
```
|
||||
|
||||
### `webContents.showDefinitionForSelection()` _OS X_
|
||||
|
||||
Shows pop-up dictionary that searches the selected word on the page.
|
||||
|
||||
## Instance Properties
|
||||
|
||||
`WebContents` objects also have the following properties:
|
||||
|
|
|
@ -205,7 +205,17 @@ If "on", the guest page will be allowed to open new windows.
|
|||
|
||||
A list of strings which specifies the blink features to be enabled separated by `,`.
|
||||
The full list of supported feature strings can be found in the
|
||||
[setFeatureEnabledFromString][blink-feature-string] function.
|
||||
[RuntimeEnabledFeatures.in][blink-feature-string] file.
|
||||
|
||||
### `disableblinkfeatures`
|
||||
|
||||
```html
|
||||
<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
|
||||
|
||||
|
@ -480,6 +490,10 @@ Sends an input `event` to the page.
|
|||
See [webContents.sendInputEvent](web-contents.md##webcontentssendinputeventevent)
|
||||
for detailed description of `event` object.
|
||||
|
||||
### `<webview>.showDefinitionForSelection()` _OS X_
|
||||
|
||||
Shows pop-up dictionary that searches the selected word on the page.
|
||||
|
||||
### `<webview>.getWebContents()`
|
||||
|
||||
Returns the [WebContents](web-contents.md) associated with this `webview`.
|
||||
|
@ -782,6 +796,14 @@ Emitted when a page's theme color changes. This is usually due to encountering a
|
|||
<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'
|
||||
|
||||
Emitted when DevTools is opened.
|
||||
|
@ -794,4 +816,4 @@ Emitted when DevTools is closed.
|
|||
|
||||
Emitted when DevTools is focused / opened.
|
||||
|
||||
[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=527
|
||||
[blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
|
||||
|
|
|
@ -8,7 +8,7 @@ be found in the `.gyp` and `.gypi` files.
|
|||
|
||||
Following `gyp` files contain the main rules for building Electron:
|
||||
|
||||
* `atom.gyp` defines how Electron itself is built.
|
||||
* `electron.gyp` defines how Electron itself is built.
|
||||
* `common.gypi` adjusts the build configurations of Node to make it build
|
||||
together with Chromium.
|
||||
* `vendor/brightray/brightray.gyp` defines how `brightray` is built and
|
||||
|
|
|
@ -30,7 +30,7 @@ use HTML5 APIs which are already available in browsers. Good candidates are
|
|||
|
||||
Or you can use the IPC system, which is specific to Electron, to store objects
|
||||
in the main process as a global variable, and then to access them from the
|
||||
renderers through the `remote` module:
|
||||
renderers through the `remote` property of `electron` module:
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
|
@ -41,12 +41,12 @@ global.sharedObject = {
|
|||
|
||||
```javascript
|
||||
// In page 1.
|
||||
require('remote').getGlobal('sharedObject').someProperty = 'new value';
|
||||
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value';
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In page 2.
|
||||
console.log(require('remote').getGlobal('sharedObject').someProperty);
|
||||
console.log(require('electron').remote.getGlobal('sharedObject').someProperty);
|
||||
```
|
||||
|
||||
## My app's window/tray disappeared after a few minutes.
|
||||
|
|
|
@ -120,6 +120,24 @@ If you are new to app sandboxing under OS X, you should also read through
|
|||
Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then
|
||||
add keys for the permissions needed by your app to the entitlements files.
|
||||
|
||||
Apart from manually signing your app, you can also choose to use the
|
||||
[electron-osx-sign][electron-osx-sign] module to do the job.
|
||||
|
||||
#### Sign Native Modules
|
||||
|
||||
Native modules used in your app also need to be signed. If using
|
||||
electron-osx-sign, be sure to include the path to the built binaries in the
|
||||
argument list:
|
||||
|
||||
```bash
|
||||
electron-osx-sign YourApp.app YourApp.app/Contents/Resources/app/node_modules/nativemodule/build/release/nativemodule
|
||||
```
|
||||
|
||||
Also note that native modules may have intermediate files produced which should
|
||||
not be included (as they would also need to be signed). If you use
|
||||
[electron-packager][electron-packager], add `--ignore=.+\.o$` to build step to
|
||||
ignore these files.
|
||||
|
||||
### Upload Your App
|
||||
|
||||
After signing your app, you can use Application Loader to upload it to iTunes
|
||||
|
@ -189,6 +207,8 @@ ERN)][ern-tutorial].
|
|||
[nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps
|
||||
[enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
|
||||
[create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html
|
||||
[electron-osx-sign]: https://github.com/electron-userland/electron-osx-sign
|
||||
[electron-packager]: https://github.com/electron-userland/electron-packager
|
||||
[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
|
||||
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
|
||||
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||
|
|
|
@ -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
|
||||
|
||||
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,
|
||||
also simplifies installation and updating.
|
||||
|
||||
Microsoft [developed a tool that compiles Electron apps as `.appx` packages](http://github.com/catalystcode/electron-windows-store),
|
||||
Microsoft [developed a tool that compiles Electron apps as `.appx` packages][electron-windows-store],
|
||||
enabling developers to use some of the goodies found in the new application
|
||||
model. This guide explains how to use it - and what the capabilities and
|
||||
limitations of an Electron AppX package are.
|
||||
|
@ -22,19 +22,17 @@ installation and uninstallation.
|
|||
|
||||
In addition, the exe is launched inside the appx model - meaning that it can use
|
||||
many of the APIs available to the Universal Windows Platform. To gain even more
|
||||
capabilities, an Electron app can pair up with an invisible UWP app launched
|
||||
together with the `exe` - sort of launched as a sidekick to run tasks in the
|
||||
background, receive push notifications, or to communicate with other UWP
|
||||
capabilities, an Electron app can pair up with an invisible UWP background task
|
||||
launched together with the `exe` - sort of launched as a sidekick to run tasks
|
||||
in the background, receive push notifications, or to communicate with other UWP
|
||||
applications.
|
||||
|
||||
To compile any existing Electron app, ensure that you have the following
|
||||
requirements:
|
||||
|
||||
* Windows 10 Anniversary Update - Enterprise Edition (This is build 14316 and up
|
||||
- as of May 2016, it's part of the Windows Insiders Preview)
|
||||
* A machine with 64 bit (x64) processor, Hardware-Assisted Virtualization, and
|
||||
Second Level Address Translation (SLAT)
|
||||
* The Windows 10 SDK, [downloadable here](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
|
||||
* Windows 10 Anniversary Update (until the update is released to the general public,
|
||||
developers can use the Windows Insider Preview)
|
||||
* The Windows 10 SDK, [downloadable here][windows-sdk]
|
||||
* At least Node 4 (to check, run `node -v`)
|
||||
|
||||
Then, go and install the `electron-windows-store` CLI:
|
||||
|
@ -43,32 +41,11 @@ Then, go and install the `electron-windows-store` CLI:
|
|||
npm install -g electron-windows-store
|
||||
```
|
||||
|
||||
## Setup and Preparation
|
||||
## Step 1: Package Your Electron Application
|
||||
|
||||
Before running the CLI for the first time, you will have to setup the "Windows
|
||||
Desktop App Converter". This will take a few minutes, but don't worry - you only
|
||||
have to do this once. Download and Desktop App Converter from
|
||||
[here](https://www.microsoft.com/en-us/download/details.aspx?id=51691). You will
|
||||
receive two files: `DesktopAppConverter.zip` and `BaseImage-14316.wim`.
|
||||
|
||||
1. Unzip `DesktopAppConverter.zip`. From an elevated PowerShell (opened with
|
||||
"run as Administrator", ensure that your systems execution policy allows us to
|
||||
run everything we intend to run by calling `Set-ExecutionPolicy bypass`.
|
||||
2. Then, run the installation of the Desktop App Converter, passing in the
|
||||
location of the Windows base Image (downloaded as `BaseImage-14316.wim`), by
|
||||
calling `.\DesktopAppConverter.ps1 -Setup -BaseImage .\BaseImage-14316.wim`.
|
||||
3. If running the above command prompts you for a reboot, please restart your
|
||||
machine and run the above command again after a successful restart.
|
||||
|
||||
Once installation succeeded, you can move on to compiling your Electron app.
|
||||
|
||||
## Package Your Electron Application
|
||||
|
||||
Package the application using
|
||||
[electron-packager](https://github.com/electron-userland/electron-packager)
|
||||
(or a similar tool). Make sure to remove `node_modules` that you don't need in
|
||||
your final application, since any module you don't actually need will just
|
||||
increase your application's size.
|
||||
Package the application using [electron-packager][electron-packager] (or a similar tool).
|
||||
Make sure to remove `node_modules` that you don't need in your final application, since
|
||||
any module you don't actually need will just increase your application's size.
|
||||
|
||||
The output should look roughly like this:
|
||||
|
||||
|
@ -101,7 +78,7 @@ The output should look roughly like this:
|
|||
└── xinput1_3.dll
|
||||
```
|
||||
|
||||
## Running the Command Line Tool
|
||||
## Step 2: Running electron-windows-store
|
||||
|
||||
From an elevated PowerShell (run it "as Administrator"), run
|
||||
`electron-windows-store` with the required parameters, passing both the input
|
||||
|
@ -130,13 +107,54 @@ Finally, the tool can be used to create a trusted certificate on your computer
|
|||
to sign the new AppX pacakge. With the signed AppX package, the CLI can also
|
||||
automatically install the package on your machine.
|
||||
|
||||
## Using the AppX Package
|
||||
## Step 3: Using the AppX Package
|
||||
|
||||
Since the Windows Anniversary Update (codenamed Windows Redstone) has not been
|
||||
released to consumers yet, you won't be able to release your app to the Windows
|
||||
Store until later this year - but you can already use the `Add-AppxPackage`
|
||||
[PowerShell Cmdlet to install it on machines](https://technet.microsoft.com/en-us/library/hh856048.aspx)
|
||||
[PowerShell Cmdlet to install it on machines][add-appxpackage]
|
||||
in developer or enterprise environments.
|
||||
|
||||
Another important limitation is that the compiled AppX package still contains a
|
||||
win32 executable - and will therefore not run on Xbox, HoloLens, or Phones.
|
||||
|
||||
## Optional: Add UWP Features using a BackgroundTask
|
||||
You can pair your Electron app up with an invisible UWP background task that
|
||||
gets to make full use of Windows 10 features - like push notifications,
|
||||
Cortana integration, or live tiles.
|
||||
|
||||
To check out how an Electron app that uses a background task to send toast
|
||||
notifications and live tiles, [check out the Microsoft-provided sample][background-task].
|
||||
|
||||
## Optional: Convert using Container Virtualiziation
|
||||
|
||||
To generate the AppX package, the `electron-windows-store` CLI uses a template
|
||||
that should work for most Electron apps. However, if you are using a custom
|
||||
installer, or should you experience any trouble with the generated package, you
|
||||
can attempt to create a package using compilation with a Windows Container - in
|
||||
that mode, the CLI will install and run your application in blank Windows Container
|
||||
to determine what modifications your application is exactly doing to the operating
|
||||
system.
|
||||
|
||||
Before running the CLI for the, you will have to setup the "Windows Desktop App
|
||||
Converter". This will take a few minutes, but don't worry - you only have to do
|
||||
this once. Download and Desktop App Converter from [here][app-converter].
|
||||
You will receive two files: `DesktopAppConverter.zip` and `BaseImage-14316.wim`.
|
||||
|
||||
1. Unzip `DesktopAppConverter.zip`. From an elevated PowerShell (opened with
|
||||
"run as Administrator", ensure that your systems execution policy allows us to
|
||||
run everything we intend to run by calling `Set-ExecutionPolicy bypass`.
|
||||
2. Then, run the installation of the Desktop App Converter, passing in the
|
||||
location of the Windows base Image (downloaded as `BaseImage-14316.wim`), by
|
||||
calling `.\DesktopAppConverter.ps1 -Setup -BaseImage .\BaseImage-14316.wim`.
|
||||
3. If running the above command prompts you for a reboot, please restart your
|
||||
machine and run the above command again after a successful restart.
|
||||
|
||||
Once installation succeeded, you can move on to compiling your Electron app.
|
||||
|
||||
[windows-sdk]: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk
|
||||
[app-converter]: https://www.microsoft.com/en-us/download/details.aspx?id=51691
|
||||
[add-appxpackage]: https://technet.microsoft.com/en-us/library/hh856048.aspx
|
||||
[electron-packager]: https://github.com/electron-userland/electron-packager
|
||||
[electron-windows-store]: https://github.com/catalystcode/electron-windows-store
|
||||
[background-task]: https://github.com/felixrieseberg/electron-uwp-background
|
|
@ -4,7 +4,7 @@
|
|||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '1.2.1',
|
||||
'version%': '1.2.2',
|
||||
},
|
||||
'includes': [
|
||||
'filenames.gypi',
|
||||
|
|
|
@ -63,6 +63,8 @@
|
|||
'lib/renderer/api/remote.js',
|
||||
'lib/renderer/api/screen.js',
|
||||
'lib/renderer/api/web-frame.js',
|
||||
'lib/renderer/extensions/i18n.js',
|
||||
'lib/renderer/extensions/storage.js',
|
||||
],
|
||||
'js2c_sources': [
|
||||
'lib/common/asar.js',
|
||||
|
|
|
@ -151,6 +151,9 @@ Object.assign(BrowserWindow.prototype, {
|
|||
},
|
||||
inspectServiceWorker () {
|
||||
return this.webContents.inspectServiceWorker()
|
||||
},
|
||||
showDefinitionForSelection () {
|
||||
return this.webContents.showDefinitionForSelection()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ rolesMap = {
|
|||
cut: 'cut',
|
||||
copy: 'copy',
|
||||
paste: 'paste',
|
||||
pasteandmatchstyle: 'pasteAndMatchStyle',
|
||||
selectall: 'selectAll',
|
||||
minimize: 'minimize',
|
||||
close: 'close',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const {app} = require('electron')
|
||||
const {createProtocolObject, registerStandardSchemes} = process.atomBinding('protocol')
|
||||
const {app, session} = require('electron')
|
||||
const {registerStandardSchemes} = process.atomBinding('protocol')
|
||||
|
||||
exports.registerStandardSchemes = function (schemes) {
|
||||
if (app.isReady()) {
|
||||
|
@ -10,7 +10,7 @@ exports.registerStandardSchemes = function (schemes) {
|
|||
}
|
||||
|
||||
app.once('ready', function () {
|
||||
let protocol = createProtocolObject()
|
||||
let protocol = session.defaultSession.protocol
|
||||
for (let method in protocol) {
|
||||
exports[method] = protocol[method].bind(protocol)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const electron = require('electron')
|
|||
const bindings = process.atomBinding('session')
|
||||
|
||||
const PERSIST_PREFIX = 'persist:'
|
||||
const Session = new EventEmitter()
|
||||
|
||||
// Wrapper of binding.fromPartition that checks for ready event.
|
||||
const fromPartition = function (partition, persist) {
|
||||
|
@ -14,7 +15,7 @@ const fromPartition = function (partition, persist) {
|
|||
}
|
||||
|
||||
// Returns the Session from |partition| string.
|
||||
exports.fromPartition = function (partition = '') {
|
||||
Session.fromPartition = function (partition = '') {
|
||||
if (partition === '') return exports.defaultSession
|
||||
|
||||
if (partition.startsWith(PERSIST_PREFIX)) {
|
||||
|
@ -25,7 +26,7 @@ exports.fromPartition = function (partition = '') {
|
|||
}
|
||||
|
||||
// Returns the default session.
|
||||
Object.defineProperty(exports, 'defaultSession', {
|
||||
Object.defineProperty(Session, 'defaultSession', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return fromPartition('', false)
|
||||
|
@ -35,6 +36,9 @@ Object.defineProperty(exports, 'defaultSession', {
|
|||
const wrapSession = function (session) {
|
||||
// Session is an EventEmitter.
|
||||
Object.setPrototypeOf(session, EventEmitter.prototype)
|
||||
Session.emit('session-created', session)
|
||||
}
|
||||
|
||||
bindings._setWrapSession(wrapSession)
|
||||
|
||||
module.exports = Session
|
||||
|
|
|
@ -10,6 +10,14 @@ session
|
|||
const binding = process.atomBinding('web_contents')
|
||||
const debuggerBinding = process.atomBinding('debugger')
|
||||
|
||||
const WebContents = new EventEmitter()
|
||||
WebContents.create = (options = {}) => {
|
||||
return binding.create(options)
|
||||
}
|
||||
WebContents.fromId = (id) => {
|
||||
return binding.fromId(id)
|
||||
}
|
||||
|
||||
let nextId = 0
|
||||
const getNextId = function () {
|
||||
return ++nextId
|
||||
|
@ -223,6 +231,8 @@ const wrapWebContents = function (webContents) {
|
|||
|
||||
this._printToPDF(printingSetting, callback)
|
||||
}
|
||||
|
||||
WebContents.emit('web-contents-created', webContents)
|
||||
}
|
||||
|
||||
binding._setWrapWebContents(wrapWebContents)
|
||||
|
@ -235,12 +245,4 @@ const wrapDebugger = function (webContentsDebugger) {
|
|||
|
||||
debuggerBinding._setWrapDebugger(wrapDebugger)
|
||||
|
||||
module.exports = {
|
||||
create (options = {}) {
|
||||
return binding.create(options)
|
||||
},
|
||||
|
||||
fromId (id) {
|
||||
return binding.fromId(id)
|
||||
}
|
||||
}
|
||||
module.exports = WebContents
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const {app, ipcMain, protocol, webContents, BrowserWindow} = require('electron')
|
||||
const {app, ipcMain, session, webContents, BrowserWindow} = require('electron')
|
||||
const {getAllWebContents} = process.atomBinding('web_contents')
|
||||
const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllBrowserWindow()
|
||||
|
||||
const fs = require('fs')
|
||||
|
@ -20,10 +21,27 @@ const generateExtensionIdFromName = function (name) {
|
|||
|
||||
// Create or get manifest object from |srcDirectory|.
|
||||
const getManifestFromPath = function (srcDirectory) {
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(srcDirectory, 'manifest.json')))
|
||||
let manifest
|
||||
let manifestContent
|
||||
|
||||
try {
|
||||
manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
|
||||
} catch (readError) {
|
||||
console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`)
|
||||
console.warn(readError.stack || readError)
|
||||
throw readError
|
||||
}
|
||||
|
||||
try {
|
||||
manifest = JSON.parse(manifestContent)
|
||||
} catch (parseError) {
|
||||
console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`)
|
||||
console.warn(parseError.stack || parseError)
|
||||
throw parseError
|
||||
}
|
||||
|
||||
if (!manifestNameMap[manifest.name]) {
|
||||
const extensionId = generateExtensionIdFromName(manifest.name)
|
||||
console.log(extensionId)
|
||||
manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
|
||||
Object.assign(manifest, {
|
||||
srcDirectory: srcDirectory,
|
||||
|
@ -38,6 +56,8 @@ const getManifestFromPath = function (srcDirectory) {
|
|||
})
|
||||
})
|
||||
return manifest
|
||||
} else if (manifest && manifest.name) {
|
||||
console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,13 +92,13 @@ const removeBackgroundPages = function (manifest) {
|
|||
}
|
||||
|
||||
// Dispatch tabs events.
|
||||
const hookWindowForTabEvents = function (win) {
|
||||
const tabId = win.webContents.id
|
||||
const hookWebContentsForTabEvents = function (webContents) {
|
||||
const tabId = webContents.id
|
||||
for (const page of objectValues(backgroundPages)) {
|
||||
page.webContents.sendToAll('CHROME_TABS_ONCREATED', tabId)
|
||||
}
|
||||
|
||||
win.once('closed', () => {
|
||||
webContents.once('destroyed', () => {
|
||||
for (const page of objectValues(backgroundPages)) {
|
||||
page.webContents.sendToAll('CHROME_TABS_ONREMOVED', tabId)
|
||||
}
|
||||
|
@ -105,6 +125,10 @@ ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo)
|
|||
page.webContents.sendToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
|
||||
})
|
||||
|
||||
ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) {
|
||||
event.returnValue = manifestMap[extensionId]
|
||||
})
|
||||
|
||||
ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message) {
|
||||
const page = backgroundPages[extensionId]
|
||||
if (!page) {
|
||||
|
@ -212,6 +236,15 @@ const loadDevToolsExtensions = function (win, manifests) {
|
|||
win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
|
||||
}
|
||||
|
||||
webContents.on('web-contents-created', function (webContents) {
|
||||
if (webContents.getType() === 'remote') return
|
||||
|
||||
hookWebContentsForTabEvents(webContents)
|
||||
webContents.on('devtools-opened', function () {
|
||||
loadDevToolsExtensions(webContents, objectValues(manifestMap))
|
||||
})
|
||||
})
|
||||
|
||||
// The persistent path of "DevTools Extensions" preference file.
|
||||
let loadedExtensionsPath = null
|
||||
|
||||
|
@ -261,10 +294,12 @@ app.once('ready', function () {
|
|||
}
|
||||
})
|
||||
}
|
||||
protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||
if (error) {
|
||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||
}
|
||||
session.on('session-created', function (ses) {
|
||||
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||
if (error) {
|
||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Load persisted extensions.
|
||||
|
@ -286,12 +321,15 @@ app.once('ready', function () {
|
|||
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
|
||||
const manifest = getManifestFromPath(srcDirectory)
|
||||
if (manifest) {
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
loadDevToolsExtensions(win, [manifest])
|
||||
for (const webContents of getAllWebContents()) {
|
||||
if (webContents.getType() !== 'remote') {
|
||||
loadDevToolsExtensions(webContents, [manifest])
|
||||
}
|
||||
}
|
||||
return manifest.name
|
||||
}
|
||||
}
|
||||
|
||||
BrowserWindow.removeDevToolsExtension = function (name) {
|
||||
const manifest = manifestNameMap[name]
|
||||
if (!manifest) return
|
||||
|
@ -301,14 +339,4 @@ app.once('ready', function () {
|
|||
delete manifestMap[manifest.extensionId]
|
||||
delete manifestNameMap[name]
|
||||
}
|
||||
|
||||
// Load extensions automatically when devtools is opened.
|
||||
const init = BrowserWindow.prototype._init
|
||||
BrowserWindow.prototype._init = function () {
|
||||
init.call(this)
|
||||
hookWindowForTabEvents(this)
|
||||
this.webContents.on('devtools-opened', () => {
|
||||
loadDevToolsExtensions(this, objectValues(manifestMap))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -4,9 +4,9 @@ const ipcMain = require('electron').ipcMain
|
|||
const webContents = require('electron').webContents
|
||||
|
||||
// Doesn't exist in early initialization.
|
||||
var webViewManager = null
|
||||
let webViewManager = null
|
||||
|
||||
var supportedWebViewEvents = [
|
||||
const supportedWebViewEvents = [
|
||||
'load-commit',
|
||||
'did-finish-load',
|
||||
'did-fail-load',
|
||||
|
@ -36,32 +36,33 @@ var supportedWebViewEvents = [
|
|||
'media-started-playing',
|
||||
'media-paused',
|
||||
'found-in-page',
|
||||
'did-change-theme-color'
|
||||
'did-change-theme-color',
|
||||
'update-target-url'
|
||||
]
|
||||
|
||||
var nextInstanceId = 0
|
||||
var guestInstances = {}
|
||||
var embedderElementsMap = {}
|
||||
var reverseEmbedderElementsMap = {}
|
||||
let nextInstanceId = 0
|
||||
const guestInstances = {}
|
||||
const embedderElementsMap = {}
|
||||
const reverseEmbedderElementsMap = {}
|
||||
|
||||
// Moves the last element of array to the first one.
|
||||
var moveLastToFirst = function (list) {
|
||||
const moveLastToFirst = function (list) {
|
||||
return list.unshift(list.pop())
|
||||
}
|
||||
|
||||
// Generate guestInstanceId.
|
||||
var getNextInstanceId = function () {
|
||||
const getNextInstanceId = function () {
|
||||
return ++nextInstanceId
|
||||
}
|
||||
|
||||
// Create a new guest instance.
|
||||
var createGuest = function (embedder, params) {
|
||||
var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners
|
||||
const createGuest = function (embedder, params) {
|
||||
if (webViewManager == null) {
|
||||
webViewManager = process.atomBinding('web_view_manager')
|
||||
}
|
||||
id = getNextInstanceId(embedder)
|
||||
guest = webContents.create({
|
||||
|
||||
const id = getNextInstanceId(embedder)
|
||||
const guest = webContents.create({
|
||||
isGuest: true,
|
||||
partition: params.partition,
|
||||
embedder: embedder
|
||||
|
@ -72,37 +73,32 @@ var createGuest = function (embedder, params) {
|
|||
}
|
||||
|
||||
// Destroy guest when the embedder is gone or navigated.
|
||||
destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
|
||||
destroy = function () {
|
||||
const destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
|
||||
const destroy = function () {
|
||||
if (guestInstances[id] != null) {
|
||||
return destroyGuest(embedder, id)
|
||||
destroyGuest(embedder, id)
|
||||
}
|
||||
}
|
||||
for (i = 0, len = destroyEvents.length; i < len; i++) {
|
||||
event = destroyEvents[i]
|
||||
for (const event of destroyEvents) {
|
||||
embedder.once(event, destroy)
|
||||
|
||||
// Users might also listen to the crashed event, so We must ensure the guest
|
||||
// Users might also listen to the crashed event, so we must ensure the guest
|
||||
// is destroyed before users' listener gets called. It is done by moving our
|
||||
// listener to the first one in queue.
|
||||
listeners = embedder._events[event]
|
||||
const listeners = embedder._events[event]
|
||||
if (Array.isArray(listeners)) {
|
||||
moveLastToFirst(listeners)
|
||||
}
|
||||
}
|
||||
guest.once('destroyed', function () {
|
||||
var j, len1, results
|
||||
results = []
|
||||
for (j = 0, len1 = destroyEvents.length; j < len1; j++) {
|
||||
event = destroyEvents[j]
|
||||
results.push(embedder.removeListener(event, destroy))
|
||||
for (const event of destroyEvents) {
|
||||
embedder.removeListener(event, destroy)
|
||||
}
|
||||
return results
|
||||
})
|
||||
|
||||
// Init guest web view after attached.
|
||||
guest.once('did-attach', function () {
|
||||
var opts
|
||||
let opts
|
||||
params = this.attachParams
|
||||
delete this.attachParams
|
||||
this.viewInstanceId = params.instanceId
|
||||
|
@ -135,13 +131,12 @@ var createGuest = function (embedder, params) {
|
|||
})
|
||||
|
||||
// Dispatch events to embedder.
|
||||
fn = function (event) {
|
||||
return guest.on(event, function (_, ...args) {
|
||||
const fn = function (event) {
|
||||
guest.on(event, function (_, ...args) {
|
||||
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(args))
|
||||
})
|
||||
}
|
||||
for (j = 0, len1 = supportedWebViewEvents.length; j < len1; j++) {
|
||||
event = supportedWebViewEvents[j]
|
||||
for (const event of supportedWebViewEvents) {
|
||||
fn(event)
|
||||
}
|
||||
|
||||
|
@ -154,12 +149,13 @@ var createGuest = function (embedder, params) {
|
|||
guest.on('size-changed', function (_, ...args) {
|
||||
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args))
|
||||
})
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// Attach the guest to an element of embedder.
|
||||
var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
|
||||
var guest, key, oldGuestInstanceId, ref1, webPreferences
|
||||
const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
|
||||
let guest, key, oldGuestInstanceId, ref1, webPreferences
|
||||
guest = guestInstances[guestInstanceId].guest
|
||||
|
||||
// Destroy the old guest when attaching.
|
||||
|
@ -181,7 +177,8 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params
|
|||
plugins: params.plugins,
|
||||
zoomFactor: params.zoomFactor,
|
||||
webSecurity: !params.disablewebsecurity,
|
||||
blinkFeatures: params.blinkfeatures
|
||||
blinkFeatures: params.blinkfeatures,
|
||||
disableBlinkFeatures: params.disableblinkfeatures
|
||||
}
|
||||
|
||||
if (params.preload) {
|
||||
|
@ -194,12 +191,12 @@ var attachGuest = function (embedder, elementInstanceId, guestInstanceId, params
|
|||
}
|
||||
|
||||
// Destroy an existing guest instance.
|
||||
var destroyGuest = function (embedder, id) {
|
||||
var key
|
||||
const destroyGuest = function (embedder, id) {
|
||||
webViewManager.removeGuest(embedder, id)
|
||||
guestInstances[id].guest.destroy()
|
||||
delete guestInstances[id]
|
||||
key = reverseEmbedderElementsMap[id]
|
||||
|
||||
const key = reverseEmbedderElementsMap[id]
|
||||
if (key != null) {
|
||||
delete reverseEmbedderElementsMap[id]
|
||||
return delete embedderElementsMap[key]
|
||||
|
@ -219,18 +216,18 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, id) {
|
|||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, id, params) {
|
||||
var ref1
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0
|
||||
const guestInstance = guestInstances[id]
|
||||
return guestInstance != null ? guestInstance.guest.setSize(params) : void 0
|
||||
})
|
||||
|
||||
// Returns WebContents from its guest id.
|
||||
exports.getGuest = function (id) {
|
||||
var ref1
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0
|
||||
const guestInstance = guestInstances[id]
|
||||
return guestInstance != null ? guestInstance.guest : void 0
|
||||
}
|
||||
|
||||
// Returns the embedder of the guest.
|
||||
exports.getEmbedder = function (id) {
|
||||
var ref1
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0
|
||||
const guestInstance = guestInstances[id]
|
||||
return guestInstance != null ? guestInstance.embedder : void 0
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
'use strict'
|
||||
|
||||
const ipcMain = require('electron').ipcMain
|
||||
const BrowserWindow = require('electron').BrowserWindow
|
||||
const {BrowserWindow, ipcMain, webContents} = require('electron')
|
||||
|
||||
var hasProp = {}.hasOwnProperty
|
||||
var frameToGuest = {}
|
||||
const hasProp = {}.hasOwnProperty
|
||||
const frameToGuest = {}
|
||||
|
||||
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||
var mergeOptions = function (child, parent) {
|
||||
var key, value
|
||||
const mergeOptions = function (child, parent) {
|
||||
let key, value
|
||||
for (key in parent) {
|
||||
if (!hasProp.call(parent, key)) continue
|
||||
value = parent[key]
|
||||
|
@ -24,7 +23,7 @@ var mergeOptions = function (child, parent) {
|
|||
}
|
||||
|
||||
// Merge |options| with the |embedder|'s window's options.
|
||||
var mergeBrowserWindowOptions = function (embedder, options) {
|
||||
const mergeBrowserWindowOptions = function (embedder, options) {
|
||||
if (embedder.browserWindowOptions != null) {
|
||||
// Inherit the original options if it is a BrowserWindow.
|
||||
mergeOptions(options, embedder.browserWindowOptions)
|
||||
|
@ -45,9 +44,8 @@ var mergeBrowserWindowOptions = function (embedder, options) {
|
|||
}
|
||||
|
||||
// Create a new guest created by |embedder| with |options|.
|
||||
var createGuest = function (embedder, url, frameName, options) {
|
||||
var closedByEmbedder, closedByUser, guest, guestId, ref1
|
||||
guest = frameToGuest[frameName]
|
||||
const createGuest = function (embedder, url, frameName, options) {
|
||||
let guest = frameToGuest[frameName]
|
||||
if (frameName && (guest != null)) {
|
||||
guest.loadURL(url)
|
||||
return guest.id
|
||||
|
@ -57,24 +55,26 @@ var createGuest = function (embedder, url, frameName, options) {
|
|||
if (options.webPreferences == null) {
|
||||
options.webPreferences = {}
|
||||
}
|
||||
options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0
|
||||
options.webPreferences.openerId = embedder.id
|
||||
guest = new BrowserWindow(options)
|
||||
guest.loadURL(url)
|
||||
|
||||
// When |embedder| is destroyed we should also destroy attached guest, and if
|
||||
// guest is closed by user then we should prevent |embedder| from double
|
||||
// closing guest.
|
||||
guestId = guest.id
|
||||
closedByEmbedder = function () {
|
||||
const guestId = guest.webContents.id
|
||||
|
||||
const closedByEmbedder = function () {
|
||||
guest.removeListener('closed', closedByUser)
|
||||
return guest.destroy()
|
||||
guest.destroy()
|
||||
}
|
||||
closedByUser = function () {
|
||||
const closedByUser = function () {
|
||||
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
||||
return embedder.removeListener('render-view-deleted', closedByEmbedder)
|
||||
embedder.removeListener('render-view-deleted', closedByEmbedder)
|
||||
}
|
||||
embedder.once('render-view-deleted', closedByEmbedder)
|
||||
guest.once('closed', closedByUser)
|
||||
|
||||
if (frameName) {
|
||||
frameToGuest[frameName] = guest
|
||||
guest.frameName = frameName
|
||||
|
@ -82,7 +82,22 @@ var createGuest = function (embedder, url, frameName, options) {
|
|||
delete frameToGuest[frameName]
|
||||
})
|
||||
}
|
||||
return guest.id
|
||||
|
||||
return guestId
|
||||
}
|
||||
|
||||
const getGuestWindow = function (guestId) {
|
||||
const guestContents = webContents.fromId(guestId)
|
||||
if (guestContents == null) return
|
||||
|
||||
let guestWindow = BrowserWindow.fromWebContents(guestContents)
|
||||
if (guestWindow == null) {
|
||||
const hostContents = guestContents.hostWebContents
|
||||
if (hostContents != null) {
|
||||
guestWindow = BrowserWindow.fromWebContents(hostContents)
|
||||
}
|
||||
}
|
||||
return guestWindow
|
||||
}
|
||||
|
||||
// Routed window.open messages.
|
||||
|
@ -97,28 +112,26 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, fr
|
|||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
|
||||
var ref1
|
||||
(ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0
|
||||
const guestWindow = getGuestWindow(guestId)
|
||||
if (guestWindow != null) guestWindow.destroy()
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
|
||||
var ref1
|
||||
event.returnValue = (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0
|
||||
const guestWindow = getGuestWindow(guestId)
|
||||
event.returnValue = guestWindow != null ? guestWindow[method].apply(guestWindow, args) : null
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
|
||||
var guestContents, ref1, ref2, sourceId
|
||||
sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0
|
||||
if (sourceId == null) {
|
||||
return
|
||||
}
|
||||
guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0
|
||||
if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') {
|
||||
guestContents != null ? guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0
|
||||
const guestContents = webContents.fromId(guestId)
|
||||
if (guestContents == null) return
|
||||
|
||||
if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
|
||||
const sourceId = event.sender.id
|
||||
guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
|
||||
var ref1, ref2
|
||||
(ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0
|
||||
const guestContents = webContents.fromId(guestId)
|
||||
if (guestContents != null) guestContents[method].apply(guestContents, args)
|
||||
})
|
||||
|
|
|
@ -99,6 +99,8 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
|||
})
|
||||
|
||||
chrome.runtime = {
|
||||
id: extensionId,
|
||||
|
||||
getURL: function (path) {
|
||||
return url.format({
|
||||
protocol: 'chrome-extension',
|
||||
|
@ -181,12 +183,7 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
|||
onMessage: chrome.runtime.onMessage
|
||||
}
|
||||
|
||||
chrome.storage = {
|
||||
sync: {
|
||||
get () {},
|
||||
set () {}
|
||||
}
|
||||
}
|
||||
chrome.storage = require('./extensions/storage.js')
|
||||
|
||||
chrome.pageAction = {
|
||||
show () {},
|
||||
|
@ -197,4 +194,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
|||
setPopup () {},
|
||||
getPopup () {}
|
||||
}
|
||||
|
||||
chrome.i18n = require('./extensions/i18n.js').setup(extensionId)
|
||||
}
|
||||
|
|
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) {
|
||||
this.guestId = guestId1
|
||||
Object.defineProperty(this, 'guestId', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
writeable: false,
|
||||
value: guestId1
|
||||
})
|
||||
|
||||
this.closed = false
|
||||
ipcRenderer.once('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + this.guestId, () => {
|
||||
BrowserWindowProxy.remove(this.guestId)
|
||||
|
|
|
@ -27,15 +27,16 @@ var WEB_VIEW_EVENTS = {
|
|||
'crashed': [],
|
||||
'gpu-crashed': [],
|
||||
'plugin-crashed': ['name', 'version'],
|
||||
'media-started-playing': [],
|
||||
'media-paused': [],
|
||||
'did-change-theme-color': ['themeColor'],
|
||||
'destroyed': [],
|
||||
'page-title-updated': ['title', 'explicitSet'],
|
||||
'page-favicon-updated': ['favicons'],
|
||||
'enter-html-full-screen': [],
|
||||
'leave-html-full-screen': [],
|
||||
'found-in-page': ['result']
|
||||
'media-started-playing': [],
|
||||
'media-paused': [],
|
||||
'found-in-page': ['result'],
|
||||
'did-change-theme-color': ['themeColor'],
|
||||
'update-target-url': ['url']
|
||||
}
|
||||
|
||||
var DEPRECATED_EVENTS = {
|
||||
|
|
|
@ -264,6 +264,13 @@ class BlinkFeaturesAttribute extends WebViewAttribute {
|
|||
}
|
||||
}
|
||||
|
||||
// Attribute that specifies the blink features to be disabled.
|
||||
class DisableBlinkFeaturesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl) {
|
||||
super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl)
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up all of the webview attributes.
|
||||
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
||||
this.attributes = {}
|
||||
|
@ -278,6 +285,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
|
|||
this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
|
||||
this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)
|
||||
|
||||
const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]
|
||||
autosizeAttributes.forEach((attribute) => {
|
||||
|
|
|
@ -16,6 +16,7 @@ module.exports = {
|
|||
ATTRIBUTE_PRELOAD: 'preload',
|
||||
ATTRIBUTE_USERAGENT: 'useragent',
|
||||
ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
|
||||
ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',
|
||||
|
||||
// Internal attribute.
|
||||
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',
|
||||
|
|
|
@ -379,7 +379,8 @@ var registerWebViewElement = function () {
|
|||
'downloadURL',
|
||||
'inspectServiceWorker',
|
||||
'print',
|
||||
'printToPDF'
|
||||
'printToPDF',
|
||||
'showDefinitionForSelection'
|
||||
]
|
||||
nonblockMethods = [
|
||||
'insertCSS',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "electron",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.2",
|
||||
"devDependencies": {
|
||||
"asar": "^0.11.0",
|
||||
"request": "*",
|
||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
|||
|
||||
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
||||
'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '8f2a0aa0a9dd107e4d574cc9d552781c55c86bab'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '7ae12b2856b81f5bfa1290108f72c3e4b5c13c52'
|
||||
|
||||
PLATFORM = {
|
||||
'cygwin': 'win32',
|
||||
|
|
|
@ -858,12 +858,27 @@ describe('browser-window module', function () {
|
|||
w.loadURL('about:blank')
|
||||
})
|
||||
|
||||
it('throws errors for missing manifest.json files', function () {
|
||||
assert.throws(function () {
|
||||
BrowserWindow.addDevToolsExtension(path.join(__dirname, 'does-not-exist'))
|
||||
}, /ENOENT: no such file or directory/)
|
||||
})
|
||||
|
||||
it('throws errors for invalid manifest.json files', function () {
|
||||
assert.throws(function () {
|
||||
BrowserWindow.addDevToolsExtension(path.join(__dirname, 'fixtures', 'devtools-extensions', 'bad-manifest'))
|
||||
}, /Unexpected token }/)
|
||||
})
|
||||
|
||||
describe('when the devtools is docked', function () {
|
||||
it('creates the extension', function (done) {
|
||||
w.webContents.openDevTools({mode: 'bottom'})
|
||||
|
||||
ipcMain.once('answer', function (event, message) {
|
||||
assert.equal(message, 'extension loaded')
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
assert.equal(message.tabId, w.webContents.id)
|
||||
assert.equal(message.i18nString, 'foo - bar (baz)')
|
||||
assert.deepEqual(message.storageItems, {foo: 'bar'})
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
@ -873,14 +888,54 @@ describe('browser-window module', function () {
|
|||
it('creates the extension', function (done) {
|
||||
w.webContents.openDevTools({mode: 'undocked'})
|
||||
|
||||
ipcMain.once('answer', function (event, message) {
|
||||
assert.equal(message, 'extension loaded')
|
||||
ipcMain.once('answer', function (event, message, extensionId) {
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
assert.equal(message.tabId, w.webContents.id)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('works when used with partitions', function (done) {
|
||||
this.timeout(10000)
|
||||
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
partition: 'temp'
|
||||
}
|
||||
})
|
||||
|
||||
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
|
||||
BrowserWindow.removeDevToolsExtension('foo')
|
||||
BrowserWindow.addDevToolsExtension(extensionPath)
|
||||
|
||||
w.webContents.on('devtools-opened', function () {
|
||||
var showPanelIntevalId = setInterval(function () {
|
||||
if (w && w.devToolsWebContents) {
|
||||
w.devToolsWebContents.executeJavaScript('(' + (function () {
|
||||
var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id
|
||||
WebInspector.inspectorView.showPanel(lastPanelId)
|
||||
}).toString() + ')()')
|
||||
} else {
|
||||
clearInterval(showPanelIntevalId)
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
|
||||
w.loadURL('about:blank')
|
||||
w.webContents.openDevTools({mode: 'bottom'})
|
||||
|
||||
ipcMain.once('answer', function (event, message) {
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('serializes the registered extensions on quit', function () {
|
||||
var extensionName = 'foo'
|
||||
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName)
|
||||
|
|
|
@ -919,6 +919,23 @@ describe('protocol module', function () {
|
|||
})
|
||||
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 w = null
|
||||
var url = 'http://127.0.0.1'
|
||||
var partitionName = 'temp'
|
||||
var protocolName = 'sp'
|
||||
const tempProtocol = session.fromPartition(partitionName).protocol
|
||||
const protocol = session.defaultSession.protocol
|
||||
|
||||
beforeEach(function () {
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
width: 400,
|
||||
|
@ -26,7 +33,10 @@ describe('session module', function () {
|
|||
})
|
||||
|
||||
afterEach(function () {
|
||||
w.destroy()
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = null
|
||||
})
|
||||
|
||||
describe('session.cookies', function () {
|
||||
|
@ -262,4 +272,43 @@ describe('session module', function () {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('session.protocol', function () {
|
||||
beforeEach(function () {
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
width: 400,
|
||||
height: 400,
|
||||
webPreferences: {
|
||||
partition: partitionName
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('handles requests from a partition', function (done) {
|
||||
var handler = function (error, callback) {
|
||||
callback({
|
||||
data: 'test'
|
||||
})
|
||||
}
|
||||
tempProtocol.registerStringProtocol(protocolName, handler, function (error) {
|
||||
if (error) {
|
||||
return done(error)
|
||||
}
|
||||
protocol.isProtocolHandled(protocolName, function (result) {
|
||||
assert.equal(result, false)
|
||||
tempProtocol.isProtocolHandled(protocolName, function (result) {
|
||||
assert.equal(result, true)
|
||||
w.webContents.on('did-finish-load', function () {
|
||||
done()
|
||||
})
|
||||
w.loadURL(protocolName + "://fake-host")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
const assert = require('assert')
|
||||
const http = require('http')
|
||||
const path = require('path')
|
||||
const ws = require('ws')
|
||||
const url = require('url')
|
||||
const remote = require('electron').remote
|
||||
|
||||
const BrowserWindow = remote.require('electron').BrowserWindow
|
||||
const session = remote.require('electron').session
|
||||
const {BrowserWindow, session, webContents} = remote
|
||||
|
||||
const isCI = remote.getGlobal('isCi')
|
||||
|
||||
|
@ -165,6 +164,12 @@ describe('chromium feature', function () {
|
|||
var b = window.open('about:blank', '', 'show=no')
|
||||
assert.equal(b.closed, false)
|
||||
assert.equal(b.constructor.name, 'BrowserWindowProxy')
|
||||
|
||||
// Check that guestId is not writeable
|
||||
assert(b.guestId)
|
||||
b.guestId = 'anotherValue'
|
||||
assert.notEqual(b.guestId, 'anoterValue')
|
||||
|
||||
b.close()
|
||||
})
|
||||
|
||||
|
@ -235,7 +240,7 @@ describe('chromium feature', function () {
|
|||
else
|
||||
targetURL = 'file://' + fixtures + '/pages/base-page.html'
|
||||
b = window.open(targetURL)
|
||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
||||
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||
assert.equal(b.location, targetURL)
|
||||
b.close()
|
||||
done()
|
||||
|
@ -246,10 +251,10 @@ describe('chromium feature', function () {
|
|||
// Load a page that definitely won't redirect
|
||||
var b
|
||||
b = window.open('about:blank')
|
||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
||||
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||
// When it loads, redirect
|
||||
b.location = 'file://' + fixtures + '/pages/base-page.html'
|
||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
||||
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||
// After our second redirect, cleanup and callback
|
||||
b.close()
|
||||
done()
|
||||
|
@ -308,7 +313,7 @@ describe('chromium feature', function () {
|
|||
}
|
||||
window.addEventListener('message', listener)
|
||||
b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no')
|
||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
||||
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||
b.postMessage('testing', '*')
|
||||
})
|
||||
})
|
||||
|
@ -327,6 +332,25 @@ describe('chromium feature', function () {
|
|||
window.addEventListener('message', listener)
|
||||
b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no')
|
||||
})
|
||||
|
||||
it('supports windows opened from a <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 () {
|
||||
|
|
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