mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
367f94aa76 | ||
|
|
1e61743242 | ||
|
|
b8bafbc5a3 | ||
|
|
99b523176b | ||
|
|
7994f087fb | ||
|
|
4dc8e90586 | ||
|
|
0bfd31e7cb | ||
|
|
b3530a55a9 | ||
|
|
9f0299cc31 | ||
|
|
c4e743d207 | ||
|
|
8e1de88512 | ||
|
|
0ff6b87f8c | ||
|
|
b2f9cce297 | ||
|
|
31564f079f | ||
|
|
bde432b64d | ||
|
|
7a1b796dd0 | ||
|
|
3279f5c80a | ||
|
|
8956388804 | ||
|
|
c3f26df577 | ||
|
|
c38f505001 | ||
|
|
3a16a9b0bf | ||
|
|
784bee8faa | ||
|
|
06a354a2eb | ||
|
|
dd9935a9d7 | ||
|
|
e65bc481a8 | ||
|
|
7c1f48808b | ||
|
|
7d11912a03 | ||
|
|
49181403ef | ||
|
|
0cdd764161 | ||
|
|
89de791e9d | ||
|
|
b34deb1d2f | ||
|
|
9bf3150237 | ||
|
|
a4d983c236 | ||
|
|
61d16d5caa | ||
|
|
6cc68638e7 | ||
|
|
2b0cd97bb8 | ||
|
|
d2ce50e3dd | ||
|
|
0da1a772be | ||
|
|
89309244b5 | ||
|
|
8d941a6559 | ||
|
|
b139d5ad1c | ||
|
|
7b6d74fdc2 | ||
|
|
c70ec47138 | ||
|
|
12dba2cb67 | ||
|
|
20e26a9639 | ||
|
|
c730800f0c | ||
|
|
9bc144c880 | ||
|
|
8c7d28de27 | ||
|
|
d6a7ced32c | ||
|
|
b627b8711a | ||
|
|
8eca019157 | ||
|
|
30fbe92970 | ||
|
|
3ad5504194 | ||
|
|
a518c47f4c | ||
|
|
8129c27a4a | ||
|
|
2cc01eea30 | ||
|
|
c365aa85c2 | ||
|
|
729b84f2ca | ||
|
|
66086880e2 | ||
|
|
a30997867f | ||
|
|
72c4952907 | ||
|
|
7a61966423 | ||
|
|
9ddb62c42a | ||
|
|
1c9421bc89 | ||
|
|
eb51e080e5 | ||
|
|
200d09cc70 | ||
|
|
fb251ac09b | ||
|
|
e95da6ad1b | ||
|
|
fd8cc63ca9 | ||
|
|
c497765dea | ||
|
|
fac330fb50 | ||
|
|
2757046e71 | ||
|
|
a2ffab40f9 | ||
|
|
3a9c57231e | ||
|
|
9432cb9170 | ||
|
|
9ca0fbaeb7 | ||
|
|
e6c8900a18 | ||
|
|
c717cd9192 | ||
|
|
c7c6dda122 | ||
|
|
aa6d79a3d8 | ||
|
|
d335d7d706 |
@@ -133,7 +133,7 @@ struct Converter<net::ProxyConfig> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
net::ProxyConfig* out) {
|
||||
std::string proxy_rules;
|
||||
std::string proxy_rules, proxy_bypass_rules;
|
||||
GURL pac_url;
|
||||
mate::Dictionary options;
|
||||
// Fallback to previous API when passed String.
|
||||
@@ -143,6 +143,7 @@ struct Converter<net::ProxyConfig> {
|
||||
} else if (ConvertFromV8(isolate, val, &options)) {
|
||||
options.Get("pacScript", &pac_url);
|
||||
options.Get("proxyRules", &proxy_rules);
|
||||
options.Get("proxyBypassRules", &proxy_bypass_rules);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -152,6 +153,7 @@ struct Converter<net::ProxyConfig> {
|
||||
out->set_pac_url(pac_url);
|
||||
} else {
|
||||
out->proxy_rules().ParseFromString(proxy_rules);
|
||||
out->proxy_rules().bypass_rules.ParseFromString(proxy_bypass_rules);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "atom/browser/api/atom_api_menu.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/ui/tray_icon.h"
|
||||
#include "atom/common/api/atom_api_native_image.h"
|
||||
#include "atom/common/native_mate_converters/gfx_converter.h"
|
||||
#include "atom/common/native_mate_converters/image_converter.h"
|
||||
@@ -18,6 +17,45 @@
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<atom::TrayIcon::HighlightMode> {
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
|
||||
atom::TrayIcon::HighlightMode* out) {
|
||||
std::string mode;
|
||||
if (ConvertFromV8(isolate, val, &mode)) {
|
||||
if (mode == "always") {
|
||||
*out = atom::TrayIcon::HighlightMode::ALWAYS;
|
||||
return true;
|
||||
}
|
||||
if (mode == "selection") {
|
||||
*out = atom::TrayIcon::HighlightMode::SELECTION;
|
||||
return true;
|
||||
}
|
||||
if (mode == "never") {
|
||||
*out = atom::TrayIcon::HighlightMode::NEVER;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Support old boolean parameter
|
||||
// TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings
|
||||
bool highlight;
|
||||
if (ConvertFromV8(isolate, val, &highlight)) {
|
||||
if (highlight)
|
||||
*out = atom::TrayIcon::HighlightMode::SELECTION;
|
||||
else
|
||||
*out = atom::TrayIcon::HighlightMode::NEVER;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace mate
|
||||
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
@@ -117,8 +155,8 @@ void Tray::SetTitle(const std::string& title) {
|
||||
tray_icon_->SetTitle(title);
|
||||
}
|
||||
|
||||
void Tray::SetHighlightMode(bool highlight) {
|
||||
tray_icon_->SetHighlightMode(highlight);
|
||||
void Tray::SetHighlightMode(TrayIcon::HighlightMode mode) {
|
||||
tray_icon_->SetHighlightMode(mode);
|
||||
}
|
||||
|
||||
void Tray::DisplayBalloon(mate::Arguments* args,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "atom/browser/ui/tray_icon.h"
|
||||
#include "atom/browser/ui/tray_icon_observer.h"
|
||||
#include "native_mate/handle.h"
|
||||
|
||||
@@ -62,7 +63,7 @@ class Tray : public mate::TrackableObject<Tray>,
|
||||
void SetPressedImage(v8::Isolate* isolate, mate::Handle<NativeImage> image);
|
||||
void SetToolTip(const std::string& tool_tip);
|
||||
void SetTitle(const std::string& title);
|
||||
void SetHighlightMode(bool highlight);
|
||||
void SetHighlightMode(TrayIcon::HighlightMode mode);
|
||||
void DisplayBalloon(mate::Arguments* args, const mate::Dictionary& options);
|
||||
void PopUpContextMenu(mate::Arguments* args);
|
||||
void SetContextMenu(v8::Isolate* isolate, mate::Handle<Menu> menu);
|
||||
|
||||
@@ -733,6 +733,7 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
|
||||
OnRendererMessageSync)
|
||||
IPC_MESSAGE_HANDLER(AtomViewHostMsg_DidCommitCompositorFrame, OnViewPainted)
|
||||
IPC_MESSAGE_HANDLER_CODE(ViewHostMsg_SetCursor, OnCursorChange,
|
||||
handled = false)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
@@ -1145,6 +1146,12 @@ void WebContents::ShowDefinitionForSelection() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebContents::CopyImageAt(int x, int y) {
|
||||
const auto host = web_contents()->GetRenderViewHost();
|
||||
if (host)
|
||||
host->CopyImageAt(x, y);
|
||||
}
|
||||
|
||||
void WebContents::Focus() {
|
||||
web_contents()->Focus();
|
||||
}
|
||||
@@ -1298,6 +1305,10 @@ void WebContents::OnCursorChange(const content::WebCursor& cursor) {
|
||||
}
|
||||
}
|
||||
|
||||
void WebContents::OnViewPainted() {
|
||||
Emit("view-painted");
|
||||
}
|
||||
|
||||
void WebContents::SetSize(const SetSizeParams& params) {
|
||||
if (guest_delegate_)
|
||||
guest_delegate_->SetSize(params);
|
||||
@@ -1420,6 +1431,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
|
||||
.SetMethod("showDefinitionForSelection",
|
||||
&WebContents::ShowDefinitionForSelection)
|
||||
.SetMethod("copyImageAt", &WebContents::CopyImageAt)
|
||||
.SetMethod("capturePage", &WebContents::CapturePage)
|
||||
.SetMethod("isFocused", &WebContents::IsFocused)
|
||||
.SetProperty("id", &WebContents::ID)
|
||||
|
||||
@@ -126,6 +126,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
uint32_t FindInPage(mate::Arguments* args);
|
||||
void StopFindInPage(content::StopFindAction action);
|
||||
void ShowDefinitionForSelection();
|
||||
void CopyImageAt(int x, int y);
|
||||
|
||||
// Focus.
|
||||
void Focus();
|
||||
@@ -294,6 +295,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
const base::ListValue& args,
|
||||
IPC::Message* message);
|
||||
|
||||
// Called when the hosted view gets graphical updates.
|
||||
void OnViewPainted();
|
||||
|
||||
v8::Global<v8::Value> session_;
|
||||
v8::Global<v8::Value> devtools_web_contents_;
|
||||
v8::Global<v8::Value> debugger_;
|
||||
|
||||
@@ -87,6 +87,9 @@ AtomBrowserContext::AtomBrowserContext(
|
||||
// Read options.
|
||||
use_cache_ = true;
|
||||
options.GetBoolean("cache", &use_cache_);
|
||||
|
||||
// Initialize Pref Registry in brightray.
|
||||
InitPrefs();
|
||||
}
|
||||
|
||||
AtomBrowserContext::~AtomBrowserContext() {
|
||||
|
||||
@@ -40,6 +40,11 @@
|
||||
#include "ui/gfx/geometry/size_conversions.h"
|
||||
#include "ui/gl/gpu_switching_manager.h"
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_WIN)
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "ui/gfx/font_render_params.h"
|
||||
#endif
|
||||
|
||||
DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::NativeWindowRelay);
|
||||
|
||||
namespace atom {
|
||||
@@ -67,6 +72,20 @@ NativeWindow::NativeWindow(
|
||||
if (parent)
|
||||
options.Get("modal", &is_modal_);
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_WIN)
|
||||
auto* prefs = web_contents()->GetMutableRendererPrefs();
|
||||
|
||||
// Update font settings.
|
||||
CR_DEFINE_STATIC_LOCAL(const gfx::FontRenderParams, params,
|
||||
(gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr)));
|
||||
prefs->should_antialias_text = params.antialiasing;
|
||||
prefs->use_subpixel_positioning = params.subpixel_positioning;
|
||||
prefs->hinting = params.hinting;
|
||||
prefs->use_autohinter = params.autohinter;
|
||||
prefs->use_bitmaps = params.use_bitmaps;
|
||||
prefs->subpixel_rendering = params.subpixel_rendering;
|
||||
#endif
|
||||
|
||||
// Tell the content module to initialize renderer widget with transparent
|
||||
// mode.
|
||||
ui::GpuSwitchingManager::SetTransparent(transparent_);
|
||||
|
||||
@@ -121,7 +121,10 @@ bool NativeWindowViews::PreHandleMSG(
|
||||
::GetWindowRect(GetAcceleratedWidget(), (LPRECT)l_param);
|
||||
return false;
|
||||
}
|
||||
|
||||
case WM_MOVE: {
|
||||
last_normal_bounds_ = GetBounds();
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>electron.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.3.0</string>
|
||||
<string>1.3.1</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.0</string>
|
||||
<string>1.3.1</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,3,0,0
|
||||
PRODUCTVERSION 1,3,0,0
|
||||
FILEVERSION 1,3,1,0
|
||||
PRODUCTVERSION 1,3,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -74,12 +74,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "1.3.0"
|
||||
VALUE "FileVersion", "1.3.1"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "1.3.0"
|
||||
VALUE "ProductVersion", "1.3.1"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -18,7 +18,7 @@ void TrayIcon::SetPressedImage(ImageType image) {
|
||||
void TrayIcon::SetTitle(const std::string& title) {
|
||||
}
|
||||
|
||||
void TrayIcon::SetHighlightMode(bool highlight) {
|
||||
void TrayIcon::SetHighlightMode(TrayIcon::HighlightMode mode) {
|
||||
}
|
||||
|
||||
void TrayIcon::DisplayBalloon(ImageType icon,
|
||||
|
||||
@@ -43,9 +43,13 @@ class TrayIcon {
|
||||
// only works on macOS.
|
||||
virtual void SetTitle(const std::string& title);
|
||||
|
||||
// Sets whether the status icon is highlighted when it is clicked. This only
|
||||
// works on macOS.
|
||||
virtual void SetHighlightMode(bool highlight);
|
||||
// Sets the status icon highlight mode. This only works on macOS.
|
||||
enum HighlightMode {
|
||||
ALWAYS, // Always highlight the tray icon
|
||||
NEVER, // Never highlight the tray icon
|
||||
SELECTION // Highlight the tray icon when clicked or the menu is opened
|
||||
};
|
||||
virtual void SetHighlightMode(HighlightMode mode);
|
||||
|
||||
// Displays a notification balloon with the specified contents.
|
||||
// Depending on the platform it might not appear by the icon tray.
|
||||
|
||||
@@ -27,7 +27,7 @@ class TrayIconCocoa : public TrayIcon,
|
||||
void SetPressedImage(const gfx::Image& image) override;
|
||||
void SetToolTip(const std::string& tool_tip) override;
|
||||
void SetTitle(const std::string& title) override;
|
||||
void SetHighlightMode(bool highlight) override;
|
||||
void SetHighlightMode(TrayIcon::HighlightMode mode) override;
|
||||
void PopUpContextMenu(const gfx::Point& pos,
|
||||
AtomMenuModel* menu_model) override;
|
||||
void SetContextMenu(AtomMenuModel* menu_model) override;
|
||||
|
||||
@@ -23,7 +23,7 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
@interface StatusItemView : NSView {
|
||||
atom::TrayIconCocoa* trayIcon_; // weak
|
||||
AtomMenuController* menuController_; // weak
|
||||
BOOL isHighlightEnable_;
|
||||
atom::TrayIcon::HighlightMode highlight_mode_;
|
||||
BOOL forceHighlight_;
|
||||
BOOL inMouseEventSequence_;
|
||||
base::scoped_nsobject<NSImage> image_;
|
||||
@@ -39,7 +39,7 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
- (id)initWithImage:(NSImage*)image icon:(atom::TrayIconCocoa*)icon {
|
||||
image_.reset([image copy]);
|
||||
trayIcon_ = icon;
|
||||
isHighlightEnable_ = YES;
|
||||
highlight_mode_ = atom::TrayIcon::HighlightMode::SELECTION;
|
||||
forceHighlight_ = NO;
|
||||
inMouseEventSequence_ = NO;
|
||||
|
||||
@@ -192,8 +192,9 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
alternateImage_.reset([image copy]);
|
||||
}
|
||||
|
||||
- (void)setHighlight:(BOOL)highlight {
|
||||
isHighlightEnable_ = highlight;
|
||||
- (void)setHighlight:(atom::TrayIcon::HighlightMode)mode {
|
||||
highlight_mode_ = mode;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setTitle:(NSString*)title {
|
||||
@@ -328,10 +329,15 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
}
|
||||
|
||||
- (BOOL)shouldHighlight {
|
||||
if (isHighlightEnable_ && forceHighlight_)
|
||||
return true;
|
||||
BOOL isMenuOpen = menuController_ && [menuController_ isMenuOpen];
|
||||
return isHighlightEnable_ && (inMouseEventSequence_ || isMenuOpen);
|
||||
switch (highlight_mode_) {
|
||||
case atom::TrayIcon::HighlightMode::ALWAYS:
|
||||
return true;
|
||||
case atom::TrayIcon::HighlightMode::NEVER:
|
||||
return false;
|
||||
case atom::TrayIcon::HighlightMode::SELECTION:
|
||||
BOOL isMenuOpen = menuController_ && [menuController_ isMenuOpen];
|
||||
return forceHighlight_ || inMouseEventSequence_ || isMenuOpen;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -369,8 +375,8 @@ void TrayIconCocoa::SetTitle(const std::string& title) {
|
||||
[status_item_view_ setTitle:base::SysUTF8ToNSString(title)];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetHighlightMode(bool highlight) {
|
||||
[status_item_view_ setHighlight:highlight];
|
||||
void TrayIconCocoa::SetHighlightMode(TrayIcon::HighlightMode mode) {
|
||||
[status_item_view_ setHighlight:mode];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::PopUpContextMenu(const gfx::Point& pos,
|
||||
|
||||
@@ -39,5 +39,7 @@ IPC_MESSAGE_ROUTED3(AtomViewMsg_Message,
|
||||
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
|
||||
std::vector<atom::DraggableRegion> /* regions */)
|
||||
|
||||
IPC_MESSAGE_ROUTED0(AtomViewHostMsg_DidCommitCompositorFrame)
|
||||
|
||||
// Update renderer process preferences.
|
||||
IPC_MESSAGE_CONTROL1(AtomMsg_UpdatePreferences, base::ListValue)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#define ATOM_MAJOR_VERSION 1
|
||||
#define ATOM_MINOR_VERSION 3
|
||||
#define ATOM_PATCH_VERSION 0
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ void UnregisterNonABICompliantCodeRange(void* start) {
|
||||
} // namespace
|
||||
|
||||
CrashReporterWin::CrashReporterWin()
|
||||
: skip_system_crash_handler_(false) {
|
||||
: skip_system_crash_handler_(false),
|
||||
code_range_registered_(false) {
|
||||
}
|
||||
|
||||
CrashReporterWin::~CrashReporterWin() {
|
||||
@@ -189,19 +190,20 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name,
|
||||
LOG(ERROR) << "Cannot initialize out-of-process crash handler";
|
||||
|
||||
#ifdef _WIN64
|
||||
bool registered = false;
|
||||
// Hook up V8 to breakpad.
|
||||
{
|
||||
if (!code_range_registered_) {
|
||||
code_range_registered_ = true;
|
||||
// gin::Debug::SetCodeRangeCreatedCallback only runs the callback when
|
||||
// Isolate is just created, so we have to manually run following code here.
|
||||
void* code_range = nullptr;
|
||||
size_t size = 0;
|
||||
v8::Isolate::GetCurrent()->GetCodeRange(&code_range, &size);
|
||||
if (code_range && size)
|
||||
registered = RegisterNonABICompliantCodeRange(code_range, size);
|
||||
if (code_range && size &&
|
||||
RegisterNonABICompliantCodeRange(code_range, size)) {
|
||||
gin::Debug::SetCodeRangeDeletedCallback(
|
||||
UnregisterNonABICompliantCodeRange);
|
||||
}
|
||||
}
|
||||
if (registered)
|
||||
gin::Debug::SetCodeRangeDeletedCallback(UnregisterNonABICompliantCodeRange);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ class CrashReporterWin : public CrashReporter {
|
||||
google_breakpad::CustomClientInfo custom_info_;
|
||||
|
||||
bool skip_system_crash_handler_;
|
||||
bool code_range_registered_;
|
||||
std::unique_ptr<google_breakpad::ExceptionHandler> breakpad_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReporterWin);
|
||||
|
||||
@@ -141,6 +141,10 @@ void AtomRenderViewObserver::DraggableRegionsChanged(blink::WebFrame* frame) {
|
||||
Send(new AtomViewHostMsg_UpdateDraggableRegions(routing_id(), regions));
|
||||
}
|
||||
|
||||
void AtomRenderViewObserver::DidCommitCompositorFrame() {
|
||||
Send(new AtomViewHostMsg_DidCommitCompositorFrame(routing_id()));
|
||||
}
|
||||
|
||||
bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(AtomRenderViewObserver, message)
|
||||
|
||||
@@ -28,6 +28,7 @@ class AtomRenderViewObserver : public content::RenderViewObserver {
|
||||
// content::RenderViewObserver implementation.
|
||||
void DidCreateDocumentElement(blink::WebLocalFrame* frame) override;
|
||||
void DraggableRegionsChanged(blink::WebFrame* frame) override;
|
||||
void DidCommitCompositorFrame() override;
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
void OnBrowserMessage(bool send_to_all,
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
* [Estructura de los directorios del Código Fuente](development/source-code-directory-structure.md)
|
||||
* [Diferencias Técnicas con NW.js (anteriormente conocido como node-webkit)](development/atom-shell-vs-node-webkit.md)
|
||||
* [Repaso del Sistema de Compilación](development/build-system-overview.md)
|
||||
* [Instrucciones de Compilación (Mac)](development/build-instructions-osx.md)
|
||||
* [Instrucciones de Compilación (macOS)](development/build-instructions-osx.md)
|
||||
* [Instrucciones de Compilación (Windows)](development/build-instructions-windows.md)
|
||||
* [Instrucciones de Compilación (Linux)](development/build-instructions-linux.md)
|
||||
* [Configurando un Servidor de Símbolos en el depurador](development/setting-up-symbol-server.md)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Para distribuir tu aplicación con Electron, el directorio que contiene la
|
||||
aplicación deberá llamarse `app`, y ser colocado debajo del directorio de
|
||||
recursos de Electron (en OSX es `Electron.app/Contents/Resources/`, en Linux y
|
||||
recursos de Electron (en macOS es `Electron.app/Contents/Resources/`, en Linux y
|
||||
Windows es `resources/`), de esta forma:
|
||||
|
||||
En macOS:
|
||||
@@ -63,7 +63,7 @@ de distribuirlo a los usuarios.
|
||||
Puedes renombrar `electron.exe` a cualquier nombre que desees, y editar su ícono
|
||||
y otra información con herramientas como [rcedit](https://github.com/atom/rcedit).
|
||||
|
||||
### OSX
|
||||
### macOS
|
||||
|
||||
Puedes renombrar `Electron.app` a cualquier nombre que desees, y tendrás que
|
||||
renombrar los campos `CFBundleDisplayName`, `CFBundleIdentifier` y `CFBundleName`
|
||||
|
||||
@@ -57,7 +57,7 @@ __Menú dock de Terminal.app:__
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/639601/5069962/6032658a-6e9c-11e4-9953-aa84006bdfff.png" height="354" width="341" >
|
||||
|
||||
Para establecer tu menú dock, puedes utilizar la API `app.dock.setMenu`, la cual sólo está disponible para OSX:
|
||||
Para establecer tu menú dock, puedes utilizar la API `app.dock.setMenu`, la cual sólo está disponible para macOS:
|
||||
|
||||
```javascript
|
||||
var app = require('app');
|
||||
|
||||
@@ -35,8 +35,8 @@ porque la gestión de los recursos GUI nativos es peligrosa, y tiende a que ocur
|
||||
Si deseas realizar operaciones GUI en una página web, el proceso renderer de la página web debe comunicarse
|
||||
con el proceso principal, y solicitar a este que realice esas operaciones.
|
||||
|
||||
En Electron, hemos proveído el módulo [ipc](../api/ipc-renderer.md) para la comunicación
|
||||
entre el proceso principal y el proceso renderer. Y también hay un módulo [remote](../api/remote.md)
|
||||
En Electron, hemos proveído el módulo [ipc](../../../docs/api/ipc-renderer.md) para la comunicación
|
||||
entre el proceso principal y el proceso renderer. Y también hay un módulo [remote](../../../docs/api/remote.md)
|
||||
para comunicación al estilo RPC.
|
||||
|
||||
## Escribe tu primera aplicación Electron
|
||||
|
||||
@@ -4,7 +4,7 @@ El plugin Pepper Flash es soportado ahora. Para utilizar pepper flash en Electro
|
||||
|
||||
## Preparar una copia del plugin Flash
|
||||
|
||||
En OSX y Linux, el detalle del plugin puede encontrarse accediendo a `chrome://plugins` en el navegador. Su ubicación y versión son útiles para el soporte. También puedes copiarlo a otro lugar.
|
||||
En macOS y Linux, el detalle del plugin puede encontrarse accediendo a `chrome://plugins` en el navegador. Su ubicación y versión son útiles para el soporte. También puedes copiarlo a otro lugar.
|
||||
|
||||
## Agrega la opción a Electron
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Distribuição de aplicações
|
||||
|
||||
Para distribuir sua aplicação com o Electron, você deve nomear o diretório que contém sua aplicação como
|
||||
`app` e dentro deste diretório colocar os recursos que você está utilizando (no OSX
|
||||
`app` e dentro deste diretório colocar os recursos que você está utilizando (no macOS
|
||||
`Electron.app/Contents/Resources/`,
|
||||
no Linux e no Windows é em `resources/`):
|
||||
|
||||
No OSX:
|
||||
No macOS:
|
||||
|
||||
```text
|
||||
electron/Electron.app/Contents/Resources/app/
|
||||
@@ -37,7 +37,7 @@ Para usar um arquivo `asar` ao invés da pasta `app` você precisa mudar o nome
|
||||
arquivo para `app.asar` e colocá-lo sob o diretório de recursos do Electron como
|
||||
mostrado abaixo, então o Electron vai ler o arquivo e iniciar a aplicação a partir dele.
|
||||
|
||||
No OSX:
|
||||
No macOS:
|
||||
|
||||
```text
|
||||
electron/Electron.app/Contents/Resources/
|
||||
|
||||
@@ -8,12 +8,12 @@ módulos nativos.
|
||||
## Compatibilidade de Módulos Nativos do Node
|
||||
|
||||
Módulos nativos podem quebrar quando utilizar a nova versão do Node, V8.
|
||||
Para ter certeza que o módulo que você está interessado em trabalhar com o
|
||||
Para ter certeza que o módulo que você está interessado em trabalhar com o
|
||||
Electron, você deve checar se a versão do Node utilizada é compatível com a
|
||||
usada pelo Electron.
|
||||
Você pode verificar qual versão do Node foi utilizada no Electron olhando na
|
||||
página [releases](https://github.com/electron/electron/releases) ou usando
|
||||
`process.version` (veja [Quick Start](https://github.com/electron/electron/blob/master/docs/tutorial/quick-start.md)
|
||||
`process.version` (veja em [Introdução](quick-start.md)
|
||||
por exemplo).
|
||||
|
||||
Considere usar [NAN](https://github.com/nodejs/nan/) para seus próprios
|
||||
|
||||
@@ -6,10 +6,10 @@ The following example shows how to quit the application when the last window is
|
||||
closed:
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron');
|
||||
const {app} = require('electron')
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
app.quit()
|
||||
})
|
||||
```
|
||||
|
||||
## Events
|
||||
@@ -192,15 +192,17 @@ certificate you should prevent the default behavior with
|
||||
`event.preventDefault()` and call `callback(true)`.
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
|
||||
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
||||
if (url === 'https://github.com') {
|
||||
// Verification logic.
|
||||
event.preventDefault();
|
||||
callback(true);
|
||||
event.preventDefault()
|
||||
callback(true)
|
||||
} else {
|
||||
callback(false);
|
||||
callback(false)
|
||||
}
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'select-client-certificate'
|
||||
@@ -228,10 +230,12 @@ and `callback` needs to be called with an entry filtered from the list. Using
|
||||
certificate from the store.
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
|
||||
app.on('select-client-certificate', (event, webContents, url, list, callback) => {
|
||||
event.preventDefault();
|
||||
callback(list[0]);
|
||||
});
|
||||
event.preventDefault()
|
||||
callback(list[0])
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'login'
|
||||
@@ -259,10 +263,12 @@ should prevent the default behavior with `event.preventDefault()` and call
|
||||
`callback(username, password)` with the credentials.
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
|
||||
app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||
event.preventDefault();
|
||||
callback('username', 'secret');
|
||||
});
|
||||
event.preventDefault()
|
||||
callback('username', 'secret')
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'gpu-process-crashed'
|
||||
@@ -331,6 +337,8 @@ An example of restarting current instance immediately and adding a new command
|
||||
line argument to the new instance:
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
|
||||
app.relaunch({args: process.argv.slice(1) + ['--relaunch']})
|
||||
app.exit(0)
|
||||
```
|
||||
@@ -537,24 +545,24 @@ An example of activating the window of primary instance when a second instance
|
||||
starts:
|
||||
|
||||
```javascript
|
||||
let myWindow = null;
|
||||
const {app} = require('electron')
|
||||
let myWindow = null
|
||||
|
||||
const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (myWindow) {
|
||||
if (myWindow.isMinimized()) myWindow.restore();
|
||||
myWindow.focus();
|
||||
if (myWindow.isMinimized()) myWindow.restore()
|
||||
myWindow.focus()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
if (shouldQuit) {
|
||||
app.quit();
|
||||
return;
|
||||
app.quit()
|
||||
}
|
||||
|
||||
// Create myWindow, load the rest of the app, etc...
|
||||
app.on('ready', () => {
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### `app.releaseSingleInstance()`
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// In the main process.
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
// Or in the renderer process.
|
||||
const {BrowserWindow} = require('electron').remote
|
||||
// Or use `remote` from the renderer process.
|
||||
// const {BrowserWindow} = require('electron').remote
|
||||
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.on('closed', () => {
|
||||
@@ -35,6 +35,7 @@ process has done drawing for the first time, showing window after this event
|
||||
will have no visual flash:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({show: false})
|
||||
win.once('ready-to-show', () => {
|
||||
win.show()
|
||||
@@ -52,6 +53,8 @@ the app feel slow. In this case, it is recommended to show the window
|
||||
immediately, and use a `backgroundColor` close to your app's background:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let win = new BrowserWindow({backgroundColor: '#2e2c29'})
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
@@ -64,8 +67,12 @@ to set `backgroundColor` to make app feel more native.
|
||||
By using `parent` option, you can create child windows:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let top = new BrowserWindow()
|
||||
let child = new BrowserWindow({parent: top})
|
||||
child.show()
|
||||
top.show()
|
||||
```
|
||||
|
||||
The `child` window will always show on top of the `top` window.
|
||||
@@ -76,6 +83,8 @@ A modal window is a child window that disables parent window, to create a modal
|
||||
window, you have to set both `parent` and `modal` options:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let child = new BrowserWindow({parent: top, modal: true, show: false})
|
||||
child.loadURL('https://github.com')
|
||||
child.once('ready-to-show', () => {
|
||||
@@ -308,14 +317,14 @@ close. For example:
|
||||
|
||||
```javascript
|
||||
window.onbeforeunload = (e) => {
|
||||
console.log('I do not want to be closed');
|
||||
console.log('I do not want to be closed')
|
||||
|
||||
// Unlike usual browsers that a message box will be prompted to users, returning
|
||||
// a non-void value will silently cancel the close.
|
||||
// It is recommended to use the dialog API to let the user confirm closing the
|
||||
// application.
|
||||
e.returnValue = false;
|
||||
};
|
||||
e.returnValue = false
|
||||
}
|
||||
```
|
||||
|
||||
#### Event: 'closed'
|
||||
@@ -414,12 +423,14 @@ Commands are lowercased, underscores are replaced with hyphens, and the
|
||||
e.g. `APPCOMMAND_BROWSER_BACKWARD` is emitted as `browser-backward`.
|
||||
|
||||
```javascript
|
||||
someWindow.on('app-command', (e, cmd) => {
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.on('app-command', (e, cmd) => {
|
||||
// Navigate the window back when the user hits their mouse back button
|
||||
if (cmd === 'browser-backward' && someWindow.webContents.canGoBack()) {
|
||||
someWindow.webContents.goBack();
|
||||
if (cmd === 'browser-backward' && win.webContents.canGoBack()) {
|
||||
win.webContents.goBack()
|
||||
}
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'scroll-touch-begin' _macOS_
|
||||
@@ -496,7 +507,10 @@ an Object containing `name` and `version` properties.
|
||||
To check if a DevTools extension is installed you can run the following:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let installed = BrowserWindow.getDevToolsExtensions().hasOwnProperty('devtron')
|
||||
console.log(installed)
|
||||
```
|
||||
|
||||
**Note:** This API cannot be called before the `ready` event of the `app` module
|
||||
@@ -507,8 +521,10 @@ is emitted.
|
||||
Objects created with `new BrowserWindow` have the following properties:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
// In this example `win` is our instance
|
||||
let win = new BrowserWindow({width: 800, height: 600});
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
|
||||
#### `win.webContents`
|
||||
@@ -809,8 +825,11 @@ attached just below the window frame, but you may want to display them beneath
|
||||
a HTML-rendered toolbar. For example:
|
||||
|
||||
```javascript
|
||||
let toolbarRect = document.getElementById('toolbar').getBoundingClientRect();
|
||||
win.setSheetOffset(toolbarRect.height);
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
|
||||
let toolbarRect = document.getElementById('toolbar').getBoundingClientRect()
|
||||
win.setSheetOffset(toolbarRect.height)
|
||||
```
|
||||
|
||||
#### `win.flashFrame(flag)`
|
||||
|
||||
@@ -7,13 +7,13 @@ your app's main script before the [ready][ready] event of the [app][app] module
|
||||
is emitted:
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron');
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315');
|
||||
app.commandLine.appendSwitch('host-rules', 'MAP * 127.0.0.1');
|
||||
const {app} = require('electron')
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315')
|
||||
app.commandLine.appendSwitch('host-rules', 'MAP * 127.0.0.1')
|
||||
|
||||
app.on('ready', () => {
|
||||
// Your code here
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
## --ignore-connections-limit=`domains`
|
||||
@@ -57,6 +57,7 @@ list of hosts. This flag has an effect only if used in tandem with
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
app.commandLine.appendSwitch('proxy-bypass-list', '<local>;*.google.com;*foo.com;1.2.3.4:5678')
|
||||
```
|
||||
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
The following example shows how to write a string to the clipboard:
|
||||
|
||||
```javascript
|
||||
const {clipboard} = require('electron');
|
||||
clipboard.writeText('Example String');
|
||||
const {clipboard} = require('electron')
|
||||
clipboard.writeText('Example String')
|
||||
```
|
||||
|
||||
On X Window systems, there is also a selection clipboard. To manipulate it
|
||||
you need to pass `selection` to each method:
|
||||
|
||||
```javascript
|
||||
clipboard.writeText('Example String', 'selection');
|
||||
console.log(clipboard.readText('selection'));
|
||||
const {clipboard} = require('electron')
|
||||
clipboard.writeText('Example String', 'selection')
|
||||
console.log(clipboard.readText('selection'))
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -109,7 +110,8 @@ Returns an array of supported formats for the clipboard `type`.
|
||||
Returns whether the clipboard supports the format of specified `data`.
|
||||
|
||||
```javascript
|
||||
console.log(clipboard.has('<p>selection</p>'));
|
||||
const {clipboard} = require('electron')
|
||||
console.log(clipboard.has('<p>selection</p>'))
|
||||
```
|
||||
|
||||
### `clipboard.read(data[, type])` _Experimental_
|
||||
@@ -130,6 +132,7 @@ Reads `data` from the clipboard.
|
||||
* `type` String (optional)
|
||||
|
||||
```javascript
|
||||
clipboard.write({text: 'test', html: "<b>test</b>"});
|
||||
const {clipboard} = require('electron')
|
||||
clipboard.write({text: 'test', html: '<b>test</b>'})
|
||||
```
|
||||
Writes `data` to the clipboard.
|
||||
|
||||
@@ -8,22 +8,22 @@ This module does not include a web interface so you need to open
|
||||
result.
|
||||
|
||||
```javascript
|
||||
const {contentTracing} = require('electron');
|
||||
const {contentTracing} = require('electron')
|
||||
|
||||
const options = {
|
||||
categoryFilter: '*',
|
||||
traceOptions: 'record-until-full,enable-sampling'
|
||||
};
|
||||
}
|
||||
|
||||
contentTracing.startRecording(options, () => {
|
||||
console.log('Tracing started');
|
||||
console.log('Tracing started')
|
||||
|
||||
setTimeout(() => {
|
||||
contentTracing.stopRecording('', (path) => {
|
||||
console.log('Tracing data recorded to ' + path);
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
console.log('Tracing data recorded to ' + path)
|
||||
})
|
||||
}, 5000)
|
||||
})
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -6,14 +6,14 @@ The following is an example of automatically submitting a crash report to a
|
||||
remote server:
|
||||
|
||||
```javascript
|
||||
const {crashReporter} = require('electron');
|
||||
const {crashReporter} = require('electron')
|
||||
|
||||
crashReporter.start({
|
||||
productName: 'YourName',
|
||||
companyName: 'YourCompany',
|
||||
submitURL: 'https://your-domain.com/url-to-submit',
|
||||
autoSubmit: true
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
For setting up a server to accept and process crash reports, you can use
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# desktopCapturer
|
||||
|
||||
> List `getUserMedia` sources for capturing audio, video, and images from a
|
||||
microphone, camera, or screen.
|
||||
> Access information about media sources that can be used to capture audio and
|
||||
> video from the desktop using the [`navigator.webkitGetUserMedia`] API.
|
||||
|
||||
The following example shows how to capture video from a desktop window whose
|
||||
title is `Electron`:
|
||||
|
||||
```javascript
|
||||
// In the renderer process.
|
||||
const {desktopCapturer} = require('electron');
|
||||
const {desktopCapturer} = require('electron')
|
||||
|
||||
desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
|
||||
if (error) throw error;
|
||||
if (error) throw error
|
||||
for (let i = 0; i < sources.length; ++i) {
|
||||
if (sources[i].name === 'Electron') {
|
||||
navigator.webkitGetUserMedia({
|
||||
@@ -23,29 +26,28 @@ desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
|
||||
maxHeight: 720
|
||||
}
|
||||
}
|
||||
}, gotStream, getUserMediaError);
|
||||
return;
|
||||
}, handleStream, handleError)
|
||||
return
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
function gotStream(stream) {
|
||||
document.querySelector('video').src = URL.createObjectURL(stream);
|
||||
function handleStream (stream) {
|
||||
document.querySelector('video').src = URL.createObjectURL(stream)
|
||||
}
|
||||
|
||||
function getUserMediaError(e) {
|
||||
console.log('getUserMediaError');
|
||||
function handleError (e) {
|
||||
console.log(e)
|
||||
}
|
||||
```
|
||||
|
||||
When creating a constraints object for the `navigator.webkitGetUserMedia` call,
|
||||
if you are using a source from `desktopCapturer` your `chromeMediaSource` must
|
||||
be set to `"desktop"` and your `audio` must be set to `false`.
|
||||
To capture video from a source provided by `desktopCapturer` the constraints
|
||||
passed to [`navigator.webkitGetUserMedia`] must include
|
||||
`chromeMediaSource: 'desktop'`, and `audio: false`.
|
||||
|
||||
If you wish to
|
||||
capture the audio and video from the entire desktop you can set
|
||||
`chromeMediaSource` to `"screen"` and `audio` to `true`. When using this method
|
||||
you cannot specify a `chromeMediaSourceId`.
|
||||
To capture both audio and video from the entire desktop the constraints passed
|
||||
to [`navigator.webkitGetUserMedia`] must include `chromeMediaSource: 'screen'`,
|
||||
and `audio: true`, but should not include a `chromeMediaSourceId` constraint.
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -56,24 +58,28 @@ The `desktopCapturer` module has the following methods:
|
||||
* `options` Object
|
||||
* `types` Array - An array of String that lists the types of desktop sources
|
||||
to be captured, available types are `screen` and `window`.
|
||||
* `thumbnailSize` Object (optional) - The suggested size that thumbnail should
|
||||
be scaled, it is `{width: 150, height: 150}` by default.
|
||||
* `thumbnailSize` Object (optional) - The suggested size that the media source
|
||||
thumbnail should be scaled to, defaults to `{width: 150, height: 150}`.
|
||||
* `callback` Function
|
||||
|
||||
Starts a request to get all desktop sources, `callback` will be called with
|
||||
`callback(error, sources)` when the request is completed.
|
||||
Starts gathering information about all available desktop media sources,
|
||||
and calls `callback(error, sources)` when finished.
|
||||
|
||||
The `sources` is an array of `Source` objects, each `Source` represents a
|
||||
captured screen or individual window, and has following properties:
|
||||
`sources` is an array of `Source` objects, each `Source` represents a
|
||||
screen or an individual window that can be captured, and has the following
|
||||
properties:
|
||||
|
||||
* `id` String - The id of the captured window or screen used in
|
||||
`navigator.webkitGetUserMedia`. The format looks like `window:XX` or
|
||||
`screen:XX` where `XX` is a random generated number.
|
||||
* `name` String - The described name of the capturing screen or window. If the
|
||||
source is a screen, the name will be `Entire Screen` or `Screen <index>`; if
|
||||
it is a window, the name will be the window's title.
|
||||
* `thumbnail` [NativeImage](native-image.md) - A thumbnail native image.
|
||||
* `id` String - The identifier of a window or screen that can be used as a
|
||||
`chromeMediaSourceId` constraint when calling
|
||||
[`navigator.webkitGetUserMedia`]. The format of the identifier will be
|
||||
`window:XX` or `screen:XX`, where `XX` is a random generated number.
|
||||
* `name` String - A screen source will be named either `Entire Screen` or
|
||||
`Screen <index>`, while the name of a window source will match the window
|
||||
title.
|
||||
* `thumbnail` [NativeImage](native-image.md) - A thumbnail image. **Note:**
|
||||
There is no guarantee that the size of the thumbnail is the same as the
|
||||
`thumnbailSize` specified in the `options` passed to
|
||||
`desktopCapturer.getSources`. The actual size depends on the scale of the
|
||||
screen or window.
|
||||
|
||||
**Note:** There is no guarantee that the size of `source.thumbnail` is always
|
||||
the same as the `thumnbailSize` in `options`. It also depends on the scale of
|
||||
the screen or window.
|
||||
[`navigator.webkitGetUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/Navigator/getUserMedia
|
||||
|
||||
@@ -5,17 +5,16 @@
|
||||
An example of showing a dialog to select multiple files and directories:
|
||||
|
||||
```javascript
|
||||
let win = ...; // BrowserWindow in which to show the dialog
|
||||
const {dialog} = require('electron');
|
||||
|
||||
console.log(dialog.showOpenDialog({properties: ['openFile', 'openDirectory', 'multiSelections']}));
|
||||
const {dialog} = require('electron')
|
||||
console.log(dialog.showOpenDialog({properties: ['openFile', 'openDirectory', 'multiSelections']}))
|
||||
```
|
||||
|
||||
The Dialog is opened from Electron's main thread. If you want to use the dialog
|
||||
object from a renderer process, remember to access it using the remote:
|
||||
|
||||
```javascript
|
||||
const {dialog} = require('electron').remote;
|
||||
const {dialog} = require('electron').remote
|
||||
console.log(dialog)
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -42,7 +41,7 @@ otherwise it returns `undefined`.
|
||||
The `filters` specifies an array of file types that can be displayed or
|
||||
selected when you want to limit the user to a specific type. For example:
|
||||
|
||||
```javascript
|
||||
```
|
||||
{
|
||||
filters: [
|
||||
{name: 'Images', extensions: ['jpg', 'png', 'gif']},
|
||||
|
||||
@@ -8,6 +8,8 @@ control the download item.
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
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')
|
||||
|
||||
@@ -15,19 +15,19 @@ Example on getting a real path from a dragged-onto-the-app file:
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const holder = document.getElementById('holder');
|
||||
const holder = document.getElementById('holder')
|
||||
holder.ondragover = () => {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
holder.ondragleave = holder.ondragend = () => {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
holder.ondrop = (e) => {
|
||||
e.preventDefault();
|
||||
e.preventDefault()
|
||||
for (let f of e.dataTransfer.files) {
|
||||
console.log('File(s) you dragged here: ', f.path);
|
||||
console.log('File(s) you dragged here: ', f.path)
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -16,6 +16,7 @@ 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})
|
||||
win.show()
|
||||
```
|
||||
|
||||
### Alternatives on macOS
|
||||
@@ -28,7 +29,9 @@ the window controls ("traffic lights") for standard window actions.
|
||||
You can do so by specifying the new `titleBarStyle` option:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({titleBarStyle: 'hidden'})
|
||||
win.show()
|
||||
```
|
||||
|
||||
## Transparent window
|
||||
@@ -37,7 +40,9 @@ By setting the `transparent` option to `true`, you can also make the frameless
|
||||
window transparent:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({transparent: true, frame: false})
|
||||
win.show()
|
||||
```
|
||||
|
||||
### Limitations
|
||||
@@ -66,6 +71,8 @@ events, you can call the [win.setIgnoreMouseEvents(ignore)][ignore-mouse-events]
|
||||
API:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.setIgnoreMouseEvents(true)
|
||||
```
|
||||
|
||||
|
||||
@@ -11,29 +11,29 @@ not have the keyboard focus. You should not use this module until the `ready`
|
||||
event of the app module is emitted.
|
||||
|
||||
```javascript
|
||||
const {app, globalShortcut} = require('electron');
|
||||
const {app, globalShortcut} = require('electron')
|
||||
|
||||
app.on('ready', () => {
|
||||
// Register a 'CommandOrControl+X' shortcut listener.
|
||||
const ret = globalShortcut.register('CommandOrControl+X', () => {
|
||||
console.log('CommandOrControl+X is pressed');
|
||||
});
|
||||
console.log('CommandOrControl+X is pressed')
|
||||
})
|
||||
|
||||
if (!ret) {
|
||||
console.log('registration failed');
|
||||
console.log('registration failed')
|
||||
}
|
||||
|
||||
// Check whether a shortcut is registered.
|
||||
console.log(globalShortcut.isRegistered('CommandOrControl+X'));
|
||||
});
|
||||
console.log(globalShortcut.isRegistered('CommandOrControl+X'))
|
||||
})
|
||||
|
||||
app.on('will-quit', () => {
|
||||
// Unregister a shortcut.
|
||||
globalShortcut.unregister('CommandOrControl+X');
|
||||
globalShortcut.unregister('CommandOrControl+X')
|
||||
|
||||
// Unregister all shortcuts.
|
||||
globalShortcut.unregisterAll();
|
||||
});
|
||||
globalShortcut.unregisterAll()
|
||||
})
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -23,27 +23,27 @@ processes:
|
||||
|
||||
```javascript
|
||||
// In main process.
|
||||
const {ipcMain} = require('electron');
|
||||
const {ipcMain} = require('electron')
|
||||
ipcMain.on('asynchronous-message', (event, arg) => {
|
||||
console.log(arg); // prints "ping"
|
||||
event.sender.send('asynchronous-reply', 'pong');
|
||||
});
|
||||
console.log(arg) // prints "ping"
|
||||
event.sender.send('asynchronous-reply', 'pong')
|
||||
})
|
||||
|
||||
ipcMain.on('synchronous-message', (event, arg) => {
|
||||
console.log(arg); // prints "ping"
|
||||
event.returnValue = 'pong';
|
||||
});
|
||||
console.log(arg) // prints "ping"
|
||||
event.returnValue = 'pong'
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In renderer process (web page).
|
||||
const {ipcRenderer} = require('electron');
|
||||
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')); // prints "pong"
|
||||
const {ipcRenderer} = require('electron')
|
||||
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (event, arg) => {
|
||||
console.log(arg); // prints "pong"
|
||||
});
|
||||
ipcRenderer.send('asynchronous-message', 'ping');
|
||||
console.log(arg) // prints "pong"
|
||||
})
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
```
|
||||
|
||||
## Listening for Messages
|
||||
|
||||
@@ -15,18 +15,18 @@ the user right clicks the page:
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<script>
|
||||
const {remote} = require('electron');
|
||||
const {remote} = require('electron')
|
||||
const {Menu, MenuItem} = remote;
|
||||
|
||||
const menu = new Menu();
|
||||
menu.append(new MenuItem({label: 'MenuItem1', click() { console.log('item 1 clicked'); }}));
|
||||
menu.append(new MenuItem({type: 'separator'}));
|
||||
menu.append(new MenuItem({label: 'MenuItem2', type: 'checkbox', checked: true}));
|
||||
const menu = new Menu()
|
||||
menu.append(new MenuItem({label: 'MenuItem1', click() { console.log('item 1 clicked') }}))
|
||||
menu.append(new MenuItem({type: 'separator'}))
|
||||
menu.append(new MenuItem({label: 'MenuItem2', type: 'checkbox', checked: true}))
|
||||
|
||||
window.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault();
|
||||
menu.popup(remote.getCurrentWindow());
|
||||
}, false);
|
||||
e.preventDefault()
|
||||
menu.popup(remote.getCurrentWindow())
|
||||
}, false)
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -34,6 +34,8 @@ An example of creating the application menu in the render process with the
|
||||
simple template API:
|
||||
|
||||
```javascript
|
||||
const {Menu} = require('electron')
|
||||
|
||||
const template = [
|
||||
{
|
||||
label: 'Edit',
|
||||
@@ -64,7 +66,7 @@ const template = [
|
||||
},
|
||||
{
|
||||
role: 'selectall'
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -73,8 +75,8 @@ const template = [
|
||||
{
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) focusedWindow.reload();
|
||||
click (item, focusedWindow) {
|
||||
if (focusedWindow) focusedWindow.reload()
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -83,11 +85,10 @@ const template = [
|
||||
{
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow)
|
||||
focusedWindow.webContents.toggleDevTools();
|
||||
click (item, focusedWindow) {
|
||||
if (focusedWindow) focusedWindow.webContents.toggleDevTools()
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -98,7 +99,7 @@ const template = [
|
||||
},
|
||||
{
|
||||
role: 'close'
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -106,14 +107,14 @@ const template = [
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click() { require('electron').shell.openExternal('http://electron.atom.io'); }
|
||||
},
|
||||
click () { require('electron').shell.openExternal('http://electron.atom.io') }
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
}
|
||||
]
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const name = require('electron').remote.app.getName();
|
||||
const name = require('electron').remote.app.getName()
|
||||
template.unshift({
|
||||
label: name,
|
||||
submenu: [
|
||||
@@ -144,9 +145,9 @@ if (process.platform === 'darwin') {
|
||||
},
|
||||
{
|
||||
role: 'quit'
|
||||
},
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
// Window menu.
|
||||
template[3].submenu = [
|
||||
{
|
||||
@@ -170,11 +171,11 @@ if (process.platform === 'darwin') {
|
||||
label: 'Bring All to Front',
|
||||
role: 'front'
|
||||
}
|
||||
];
|
||||
]
|
||||
}
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
const menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
```
|
||||
|
||||
## Class: Menu
|
||||
@@ -227,16 +228,14 @@ The `menu` object has the following instance methods:
|
||||
|
||||
#### `menu.popup([browserWindow, x, y, positioningItem])`
|
||||
|
||||
* `browserWindow` BrowserWindow (optional) - Default is `null`.
|
||||
* `x` Number (optional) - Default is -1.
|
||||
* `y` Number (**required** if `x` is used) - Default is -1.
|
||||
* `browserWindow` BrowserWindow (optional) - Default is `BrowserWindow.getFocusedWindow()`.
|
||||
* `x` Number (optional) - Default is the current mouse cursor position.
|
||||
* `y` Number (**required** if `x` is used) - Default is the current mouse cursor position.
|
||||
* `positioningItem` Number (optional) _macOS_ - The index of the menu item to
|
||||
be positioned under the mouse cursor at the specified coordinates. Default is
|
||||
-1.
|
||||
|
||||
Pops up this menu as a context menu in the `browserWindow`. You can optionally
|
||||
provide a `x, y` coordinate to place the menu at, otherwise it will be placed
|
||||
at the current mouse cursor position.
|
||||
Pops up this menu as a context menu in the `browserWindow`.
|
||||
|
||||
#### `menu.append(menuItem)`
|
||||
|
||||
@@ -322,7 +321,7 @@ the first item.
|
||||
|
||||
Template:
|
||||
|
||||
```javascript
|
||||
```
|
||||
[
|
||||
{label: '4', id: '4'},
|
||||
{label: '5', id: '5'},
|
||||
@@ -344,7 +343,7 @@ Menu:
|
||||
|
||||
Template:
|
||||
|
||||
```javascript
|
||||
```
|
||||
[
|
||||
{label: 'a', position: 'endof=letters'},
|
||||
{label: '1', position: 'endof=numbers'},
|
||||
|
||||
@@ -3,21 +3,26 @@
|
||||
> Create tray, dock, and application icons using PNG or JPG files.
|
||||
|
||||
In Electron, for the APIs that take images, you can pass either file paths or
|
||||
`nativeImage` instances. An empty image will be used when `null` is passed.
|
||||
`NativeImage` instances. An empty image will be used when `null` is passed.
|
||||
|
||||
For example, when creating a tray or setting a window's icon, you can pass an
|
||||
image file path as a `String`:
|
||||
|
||||
```javascript
|
||||
const appIcon = new Tray('/Users/somebody/images/icon.png');
|
||||
let win = new BrowserWindow({icon: '/Users/somebody/images/window.png'});
|
||||
const {BrowserWindow, Tray} = require('electron')
|
||||
|
||||
const appIcon = new Tray('/Users/somebody/images/icon.png')
|
||||
let win = new BrowserWindow({icon: '/Users/somebody/images/window.png'})
|
||||
console.log(appIcon, win)
|
||||
```
|
||||
|
||||
Or read the image from the clipboard which returns a `nativeImage`:
|
||||
|
||||
```javascript
|
||||
const image = clipboard.readImage();
|
||||
const appIcon = new Tray(image);
|
||||
const {clipboard, Tray} = require('electron')
|
||||
const image = clipboard.readImage()
|
||||
const appIcon = new Tray(image)
|
||||
console.log(appIcon)
|
||||
```
|
||||
|
||||
## Supported Formats
|
||||
@@ -25,8 +30,8 @@ const appIcon = new Tray(image);
|
||||
Currently `PNG` and `JPEG` image formats are supported. `PNG` is recommended
|
||||
because of its support for transparency and lossless compression.
|
||||
|
||||
On Windows, you can also load `ICO` icons from file paths, to get best visual
|
||||
effects it is recommended to include at least followings sizes in the icon:
|
||||
On Windows, you can also load `ICO` icons from file paths. For best visual
|
||||
quality it is recommended to include at least the following sizes in the icon:
|
||||
|
||||
* 16x16
|
||||
* 32x32
|
||||
@@ -35,8 +40,8 @@ effects it is recommended to include at least followings sizes in the icon:
|
||||
|
||||
## High Resolution Image
|
||||
|
||||
On platforms that have high-DPI support, you can append `@2x` after image's
|
||||
base filename to mark it as a high resolution image.
|
||||
On platforms that have high-DPI support such as Apple Retina displays, you can
|
||||
append `@2x` after image's base filename to mark it as a high resolution image.
|
||||
|
||||
For example if `icon.png` is a normal image that has standard resolution, then
|
||||
`icon@2x.png` will be treated as a high resolution image that has double DPI
|
||||
@@ -55,7 +60,9 @@ images/
|
||||
|
||||
|
||||
```javascript
|
||||
let appIcon = new Tray('/Users/somebody/images/icon.png');
|
||||
const {Tray} = require('electron')
|
||||
let appIcon = new Tray('/Users/somebody/images/icon.png')
|
||||
console.log(appIcon)
|
||||
```
|
||||
|
||||
Following suffixes for DPI are also supported:
|
||||
@@ -91,22 +98,24 @@ To mark an image as a template image, its filename should end with the word
|
||||
|
||||
## Methods
|
||||
|
||||
The `nativeImage` class has the following methods:
|
||||
The `nativeImage` module has the following methods, all of which return
|
||||
an instance of the `NativeImage` class:
|
||||
|
||||
### `nativeImage.createEmpty()`
|
||||
|
||||
Creates an empty `nativeImage` instance.
|
||||
Creates an empty `NativeImage` instance.
|
||||
|
||||
### `nativeImage.createFromPath(path)`
|
||||
|
||||
* `path` String
|
||||
|
||||
Creates a new `nativeImage` instance from a file located at `path`.
|
||||
Creates a new `NativeImage` instance from a file located at `path`.
|
||||
|
||||
```javascript
|
||||
const nativeImage = require('electron').nativeImage;
|
||||
const nativeImage = require('electron').nativeImage
|
||||
|
||||
let image = nativeImage.createFromPath('/Users/somebody/images/icon.png');
|
||||
let image = nativeImage.createFromPath('/Users/somebody/images/icon.png')
|
||||
console.log(image)
|
||||
```
|
||||
|
||||
### `nativeImage.createFromBuffer(buffer[, scaleFactor])`
|
||||
@@ -114,34 +123,38 @@ let image = nativeImage.createFromPath('/Users/somebody/images/icon.png');
|
||||
* `buffer` [Buffer][buffer]
|
||||
* `scaleFactor` Double (optional)
|
||||
|
||||
Creates a new `nativeImage` instance from `buffer`. The default `scaleFactor` is
|
||||
Creates a new `NativeImage` instance from `buffer`. The default `scaleFactor` is
|
||||
1.0.
|
||||
|
||||
### `nativeImage.createFromDataURL(dataURL)`
|
||||
|
||||
* `dataURL` String
|
||||
|
||||
Creates a new `nativeImage` instance from `dataURL`.
|
||||
Creates a new `NativeImage` instance from `dataURL`.
|
||||
|
||||
## Instance Methods
|
||||
## Class: NativeImage
|
||||
|
||||
The following methods are available on instances of `nativeImage`:
|
||||
A native wrapper for images such as tray, dock, and application icons.
|
||||
|
||||
### `image.toPNG()`
|
||||
### Instance Methods
|
||||
|
||||
The following methods are available on instances of the `NativeImage` class:
|
||||
|
||||
#### `image.toPNG()`
|
||||
|
||||
Returns a [Buffer][buffer] that contains the image's `PNG` encoded data.
|
||||
|
||||
### `image.toJPEG(quality)`
|
||||
#### `image.toJPEG(quality)`
|
||||
|
||||
* `quality` Integer (**required**) - Between 0 - 100.
|
||||
|
||||
Returns a [Buffer][buffer] that contains the image's `JPEG` encoded data.
|
||||
|
||||
### `image.toDataURL()`
|
||||
#### `image.toDataURL()`
|
||||
|
||||
Returns the data URL of the image.
|
||||
|
||||
### `image.getNativeHandle()` _macOS_
|
||||
#### `image.getNativeHandle()` _macOS_
|
||||
|
||||
Returns a [Buffer][buffer] that stores C pointer to underlying native handle of
|
||||
the image. On macOS, a pointer to `NSImage` instance would be returned.
|
||||
@@ -150,22 +163,22 @@ Notice that the returned pointer is a weak pointer to the underlying native
|
||||
image instead of a copy, so you _must_ ensure that the associated
|
||||
`nativeImage` instance is kept around.
|
||||
|
||||
### `image.isEmpty()`
|
||||
#### `image.isEmpty()`
|
||||
|
||||
Returns a boolean whether the image is empty.
|
||||
|
||||
### `image.getSize()`
|
||||
#### `image.getSize()`
|
||||
|
||||
Returns the size of the image.
|
||||
|
||||
[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
|
||||
|
||||
### `image.setTemplateImage(option)`
|
||||
#### `image.setTemplateImage(option)`
|
||||
|
||||
* `option` Boolean
|
||||
|
||||
Marks the image as template image.
|
||||
Marks the image as a template image.
|
||||
|
||||
### `image.isTemplateImage()`
|
||||
#### `image.isTemplateImage()`
|
||||
|
||||
Returns a boolean whether the image is a template image.
|
||||
|
||||
@@ -8,11 +8,13 @@ event of the `app` module is emitted.
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
|
||||
app.on('ready', () => {
|
||||
require('electron').powerMonitor.on('suspend', () => {
|
||||
console.log('The system is going to sleep');
|
||||
});
|
||||
});
|
||||
console.log('The system is going to sleep')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const {powerSaveBlocker} = require('electron');
|
||||
const {powerSaveBlocker} = require('electron')
|
||||
|
||||
const id = powerSaveBlocker.start('prevent-display-sleep');
|
||||
console.log(powerSaveBlocker.isStarted(id));
|
||||
const id = powerSaveBlocker.start('prevent-display-sleep')
|
||||
console.log(powerSaveBlocker.isStarted(id))
|
||||
|
||||
powerSaveBlocker.stop(id);
|
||||
powerSaveBlocker.stop(id)
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -16,12 +16,12 @@ the global scope when node integration is turned off:
|
||||
|
||||
```javascript
|
||||
// preload.js
|
||||
const _setImmediate = setImmediate;
|
||||
const _clearImmediate = clearImmediate;
|
||||
const _setImmediate = setImmediate
|
||||
const _clearImmediate = clearImmediate
|
||||
process.once('loaded', () => {
|
||||
global.setImmediate = _setImmediate;
|
||||
global.clearImmediate = _clearImmediate;
|
||||
});
|
||||
global.setImmediate = _setImmediate
|
||||
global.clearImmediate = _clearImmediate
|
||||
})
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -6,19 +6,19 @@ An example of implementing a protocol that has the same effect as the
|
||||
`file://` protocol:
|
||||
|
||||
```javascript
|
||||
const {app, protocol} = require('electron');
|
||||
const path = require('path');
|
||||
const {app, protocol} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
app.on('ready', () => {
|
||||
protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const url = request.url.substr(7);
|
||||
callback({path: path.normalize(__dirname + '/' + url)});
|
||||
const url = request.url.substr(7)
|
||||
callback({path: path.normalize(`${__dirname}/${url}`)})
|
||||
}, (error) => {
|
||||
if (error)
|
||||
console.error('Failed to register protocol');
|
||||
});
|
||||
});
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Note:** All methods unless specified can only be used after the `ready` event
|
||||
of the `app` module gets emitted.
|
||||
|
||||
@@ -52,10 +52,12 @@ So if you want to register a custom protocol to replace the `http` protocol, you
|
||||
have to register it as standard scheme:
|
||||
|
||||
```javascript
|
||||
protocol.registerStandardSchemes(['atom']);
|
||||
const {app, protocol} = require('electron')
|
||||
|
||||
protocol.registerStandardSchemes(['atom'])
|
||||
app.on('ready', () => {
|
||||
protocol.registerHttpProtocol('atom', ...);
|
||||
});
|
||||
protocol.registerHttpProtocol('atom', '...')
|
||||
})
|
||||
```
|
||||
|
||||
**Note:** This method can only be used before the `ready` event of the `app`
|
||||
@@ -119,12 +121,13 @@ should be called with either a `Buffer` object or an object that has the `data`,
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const {protocol} = require('electron')
|
||||
|
||||
protocol.registerBufferProtocol('atom', (request, callback) => {
|
||||
callback({mimeType: 'text/html', data: new Buffer('<h5>Response</h5>')});
|
||||
callback({mimeType: 'text/html', data: new Buffer('<h5>Response</h5>')})
|
||||
}, (error) => {
|
||||
if (error)
|
||||
console.error('Failed to register protocol');
|
||||
});
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.registerStringProtocol(scheme, handler[, completion])`
|
||||
|
||||
@@ -14,10 +14,9 @@ similar to Java's [RMI][rmi]. An example of creating a browser window from a
|
||||
renderer process:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron').remote;
|
||||
|
||||
let win = new BrowserWindow({width: 800, height: 600});
|
||||
win.loadURL('https://github.com');
|
||||
const {BrowserWindow} = require('electron').remote
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
|
||||
**Note:** for the reverse (access the renderer process from the main process),
|
||||
@@ -70,23 +69,22 @@ For instance you can't use a function from the renderer process in an
|
||||
```javascript
|
||||
// main process mapNumbers.js
|
||||
exports.withRendererCallback = (mapper) => {
|
||||
return [1,2,3].map(mapper);
|
||||
};
|
||||
return [1, 2, 3].map(mapper)
|
||||
}
|
||||
|
||||
exports.withLocalCallback = () => {
|
||||
return [1,2,3].map(x => x + 1);
|
||||
};
|
||||
return [1, 2, 3].map(x => x + 1)
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// renderer process
|
||||
const mapNumbers = require('electron').remote.require('./mapNumbers');
|
||||
const mapNumbers = require('electron').remote.require('./mapNumbers')
|
||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1)
|
||||
const withLocalCb = mapNumbers.withLocalCallback()
|
||||
|
||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1);
|
||||
|
||||
const withLocalCb = mapNumbers.withLocalCallback();
|
||||
|
||||
console.log(withRendererCb, withLocalCb); // [undefined, undefined, undefined], [2, 3, 4]
|
||||
console.log(withRendererCb, withLocalCb)
|
||||
// [undefined, undefined, undefined], [2, 3, 4]
|
||||
```
|
||||
|
||||
As you can see, the renderer callback's synchronous return value was not as
|
||||
@@ -100,9 +98,9 @@ For example, the following code seems innocent at first glance. It installs a
|
||||
callback for the `close` event on a remote object:
|
||||
|
||||
```javascript
|
||||
remote.getCurrentWindow().on('close', () => {
|
||||
// blabla...
|
||||
});
|
||||
require('electron').remote.getCurrentWindow().on('close', () => {
|
||||
// window was closed...
|
||||
})
|
||||
```
|
||||
|
||||
But remember the callback is referenced by the main process until you
|
||||
@@ -124,7 +122,8 @@ The built-in modules in the main process are added as getters in the `remote`
|
||||
module, so you can use them directly like the `electron` module.
|
||||
|
||||
```javascript
|
||||
const app = remote.app;
|
||||
const app = require('electron').remote.app
|
||||
console.log(app)
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -21,7 +21,8 @@ let win
|
||||
app.on('ready', () => {
|
||||
const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
|
||||
win = new BrowserWindow({width, height})
|
||||
});
|
||||
win.loadURL('https://github.com')
|
||||
})
|
||||
```
|
||||
|
||||
Another example of creating a window in the external display:
|
||||
@@ -43,6 +44,7 @@ app.on('ready', () => {
|
||||
x: externalDisplay.bounds.x + 50,
|
||||
y: externalDisplay.bounds.y + 50
|
||||
})
|
||||
win.loadURL('https://github.com')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# session
|
||||
# session
|
||||
|
||||
> Manage browser sessions, cookies, cache, proxy settings, etc.
|
||||
|
||||
@@ -8,12 +8,13 @@ You can also access the `session` of existing pages by using the `session`
|
||||
property of [`WebContents`](web-contents.md), or from the `session` module.
|
||||
|
||||
```javascript
|
||||
const {session, BrowserWindow} = require('electron')
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
const ses = win.webContents.session
|
||||
console.log(ses.getUserAgent())
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -52,9 +53,9 @@ Returns the default session object of the app.
|
||||
You can create a `Session` object in the `session` module:
|
||||
|
||||
```javascript
|
||||
const session = require('electron').session;
|
||||
|
||||
const ses = session.fromPartition('persist:name');
|
||||
const {session} = require('electron')
|
||||
const ses = session.fromPartition('persist:name')
|
||||
console.log(ses.getUserAgent())
|
||||
```
|
||||
|
||||
### Instance Events
|
||||
@@ -73,12 +74,13 @@ Calling `event.preventDefault()` will cancel the download and `item` will not be
|
||||
available from next tick of the process.
|
||||
|
||||
```javascript
|
||||
const {session} = require('electron')
|
||||
session.defaultSession.on('will-download', (event, item, webContents) => {
|
||||
event.preventDefault();
|
||||
event.preventDefault()
|
||||
require('request')(item.getURL(), (data) => {
|
||||
require('fs').writeFileSync('/somewhere', data);
|
||||
});
|
||||
});
|
||||
require('fs').writeFileSync('/somewhere', data)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Methods
|
||||
@@ -121,6 +123,8 @@ Writes any unwritten DOMStorage data to disk.
|
||||
* `config` Object
|
||||
* `pacScript` String - The URL associated with the PAC file.
|
||||
* `proxyRules` String - Rules indicating which proxies to use.
|
||||
* `proxyBypassRules` String - Rules indicating which URLs should
|
||||
bypass the proxy settings.
|
||||
* `callback` Function - Called when operation is done.
|
||||
|
||||
Sets the proxy settings.
|
||||
@@ -153,6 +157,43 @@ For example:
|
||||
* `http=foopy;socks=foopy2` - Use HTTP proxy `foopy` for http URLs, and use
|
||||
`socks4://foopy2` for all other URLs.
|
||||
|
||||
The `proxyBypassRules` is a comma separated list of rules described below:
|
||||
|
||||
* `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]`
|
||||
|
||||
Match all hostnames that match the pattern HOSTNAME_PATTERN.
|
||||
|
||||
Examples:
|
||||
"foobar.com", "*foobar.com", "*.foobar.com", "*foobar.com:99",
|
||||
"https://x.*.y.com:99"
|
||||
|
||||
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
|
||||
|
||||
Match a particular domain suffix.
|
||||
|
||||
Examples:
|
||||
".google.com", ".com", "http://.google.com"
|
||||
|
||||
* `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]`
|
||||
|
||||
Match URLs which are IP address literals.
|
||||
|
||||
Examples:
|
||||
"127.0.1", "[0:0::1]", "[::1]", "http://[::1]:99"
|
||||
|
||||
* `IP_LITERAL "/" PREFIX_LENGHT_IN_BITS`
|
||||
|
||||
Match any URL that is to an IP literal that falls between the
|
||||
given range. IP range is specified using CIDR notation.
|
||||
|
||||
Examples:
|
||||
"192.168.1.1/16", "fefe:13::abc/33".
|
||||
|
||||
* `<local>`
|
||||
|
||||
Match local addresses. The meaning of `<local>` is whether the
|
||||
host matches one of: "127.0.0.1", "::1", "localhost".
|
||||
|
||||
### `ses.resolveProxy(url, callback)`
|
||||
|
||||
* `url` URL
|
||||
@@ -181,13 +222,13 @@ Emulates network with the given configuration for the `session`.
|
||||
```javascript
|
||||
// To emulate a GPRS connection with 50kbps throughput and 500 ms latency.
|
||||
window.webContents.session.enableNetworkEmulation({
|
||||
latency: 500,
|
||||
downloadThroughput: 6400,
|
||||
uploadThroughput: 6400
|
||||
});
|
||||
latency: 500,
|
||||
downloadThroughput: 6400,
|
||||
uploadThroughput: 6400
|
||||
})
|
||||
|
||||
// To emulate a network outage.
|
||||
window.webContents.session.enableNetworkEmulation({offline: true});
|
||||
window.webContents.session.enableNetworkEmulation({offline: true})
|
||||
```
|
||||
|
||||
#### `ses.disableNetworkEmulation()`
|
||||
@@ -208,12 +249,12 @@ Calling `setCertificateVerifyProc(null)` will revert back to default certificate
|
||||
verify proc.
|
||||
|
||||
```javascript
|
||||
myWindow.webContents.session.setCertificateVerifyProc((hostname, cert, callback) => {
|
||||
if (hostname === 'github.com')
|
||||
callback(true);
|
||||
else
|
||||
callback(false);
|
||||
});
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
|
||||
win.webContents.session.setCertificateVerifyProc((hostname, cert, callback) => {
|
||||
callback(hostname === 'github.com')
|
||||
})
|
||||
```
|
||||
|
||||
#### `ses.setPermissionRequestHandler(handler)`
|
||||
@@ -228,16 +269,14 @@ Sets the handler which can be used to respond to permission requests for the `se
|
||||
Calling `callback(true)` will allow the permission and `callback(false)` will reject it.
|
||||
|
||||
```javascript
|
||||
session.fromPartition(partition).setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
if (webContents.getURL() === host) {
|
||||
if (permission === 'notifications') {
|
||||
callback(false); // denied.
|
||||
return;
|
||||
}
|
||||
const {session} = require('electron')
|
||||
session.fromPartition('some-partition').setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
if (webContents.getURL() === 'some-host' && permission === 'notifications') {
|
||||
return callback(false) // denied.
|
||||
}
|
||||
|
||||
callback(true);
|
||||
});
|
||||
callback(true)
|
||||
})
|
||||
```
|
||||
|
||||
#### `ses.clearHostResolverCache([callback])`
|
||||
@@ -255,6 +294,7 @@ Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate
|
||||
authentication.
|
||||
|
||||
```javascript
|
||||
const {session} = require('electron')
|
||||
// consider any url ending with `example.com`, `foobar.com`, `baz`
|
||||
// for integrated authentication.
|
||||
session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com, *baz')
|
||||
@@ -301,13 +341,12 @@ const {app, session} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
app.on('ready', function () {
|
||||
const protocol = session.fromPartition(partitionName).protocol
|
||||
const protocol = session.fromPartition('some-partition').protocol
|
||||
protocol.registerFileProtocol('atom', function (request, callback) {
|
||||
var url = request.url.substr(7)
|
||||
callback({path: path.normalize(__dirname + '/' + url)})
|
||||
callback({path: path.normalize(`${__dirname}/${url}`)})
|
||||
}, function (error) {
|
||||
if (error)
|
||||
console.error('Failed to register protocol')
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
})
|
||||
```
|
||||
@@ -320,22 +359,23 @@ The `Cookies` class gives you ability to query and modify cookies. Instances of
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const {session} = require('electron')
|
||||
|
||||
// Query all cookies.
|
||||
session.defaultSession.cookies.get({}, (error, cookies) => {
|
||||
console.log(cookies)
|
||||
console.log(error, cookies)
|
||||
})
|
||||
|
||||
// Query all cookies associated with a specific url.
|
||||
session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => {
|
||||
console.log(cookies)
|
||||
console.log(error, cookies)
|
||||
})
|
||||
|
||||
// Set a cookie with the given cookie data;
|
||||
// may overwrite equivalent cookies if they exist.
|
||||
const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}
|
||||
session.defaultSession.cookies.set(cookie, (error) => {
|
||||
if (error)
|
||||
console.error(error)
|
||||
if (error) console.error(error)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -425,13 +465,15 @@ called with an `response` object when `listener` has done its work.
|
||||
An example of adding `User-Agent` header for requests:
|
||||
|
||||
```javascript
|
||||
const {session} = require('electron')
|
||||
|
||||
// Modify the user agent for all requests to the following urls.
|
||||
const filter = {
|
||||
urls: ['https://*.github.com/*', '*://electron.github.io']
|
||||
}
|
||||
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
||||
details.requestHeaders['User-Agent'] = "MyAgent"
|
||||
details.requestHeaders['User-Agent'] = 'MyAgent'
|
||||
callback({cancel: false, requestHeaders: details.requestHeaders})
|
||||
})
|
||||
```
|
||||
|
||||
@@ -7,9 +7,9 @@ The `shell` module provides functions related to desktop integration.
|
||||
An example of opening a URL in the user's default browser:
|
||||
|
||||
```javascript
|
||||
const {shell} = require('electron');
|
||||
const {shell} = require('electron')
|
||||
|
||||
shell.openExternal('https://github.com');
|
||||
shell.openExternal('https://github.com')
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -19,14 +19,13 @@ scripts to be able to use those modules.
|
||||
The main process script is just like a normal Node.js script:
|
||||
|
||||
```javascript
|
||||
const {app, BrowserWindow} = require('electron');
|
||||
|
||||
let win = null;
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
let win = null
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow({width: 800, height: 600});
|
||||
win.loadURL('https://github.com');
|
||||
});
|
||||
win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('https://github.com')
|
||||
})
|
||||
```
|
||||
|
||||
The renderer process is no different than a normal web page, except for the
|
||||
@@ -37,8 +36,8 @@ extra ability to use node modules:
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
const {app} = require('electron').remote;
|
||||
console.log(app.getVersion());
|
||||
const {app} = require('electron').remote
|
||||
console.log(app.getVersion())
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -53,23 +52,43 @@ As of 0.37, you can use
|
||||
built-in modules.
|
||||
|
||||
```javascript
|
||||
const {app, BrowserWindow} = require('electron');
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
let win
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow()
|
||||
win.loadURL('https://github.com')
|
||||
})
|
||||
```
|
||||
|
||||
If you need the entire `electron` module, you can require it and then using
|
||||
destructuring to access the individual modules from `electron`.
|
||||
|
||||
```javascript
|
||||
const electron = require('electron');
|
||||
const {app, BrowserWindow} = electron;
|
||||
const electron = require('electron')
|
||||
const {app, BrowserWindow} = electron
|
||||
|
||||
let win
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow()
|
||||
win.loadURL('https://github.com')
|
||||
})
|
||||
```
|
||||
|
||||
This is equivalent to the following code:
|
||||
|
||||
```javascript
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
const electron = require('electron')
|
||||
const app = electron.app
|
||||
const BrowserWindow = electron.BrowserWindow
|
||||
let win
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow()
|
||||
win.loadURL('https://github.com')
|
||||
})
|
||||
```
|
||||
|
||||
[gui]: https://en.wikipedia.org/wiki/Graphical_user_interface
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
> Get system preferences.
|
||||
|
||||
```javascript
|
||||
const {systemPreferences} = require('electron');
|
||||
console.log(systemPreferences.isDarkMode());
|
||||
const {systemPreferences} = require('electron')
|
||||
console.log(systemPreferences.isDarkMode())
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -79,23 +79,24 @@ An example of using it to determine if you should create a transparent window or
|
||||
not (transparent windows won't work correctly when DWM composition is disabled):
|
||||
|
||||
```javascript
|
||||
let browserOptions = {width: 1000, height: 800};
|
||||
const {BrowserWindow, systemPreferences} = require('electron')
|
||||
let browserOptions = {width: 1000, height: 800}
|
||||
|
||||
// Make the window transparent only if the platform supports it.
|
||||
if (process.platform !== 'win32' || systemPreferences.isAeroGlassEnabled()) {
|
||||
browserOptions.transparent = true;
|
||||
browserOptions.frame = false;
|
||||
browserOptions.transparent = true
|
||||
browserOptions.frame = false
|
||||
}
|
||||
|
||||
// Create the window.
|
||||
let win = new BrowserWindow(browserOptions);
|
||||
let win = new BrowserWindow(browserOptions)
|
||||
|
||||
// Navigate.
|
||||
if (browserOptions.transparent) {
|
||||
win.loadURL('file://' + __dirname + '/index.html');
|
||||
win.loadURL(`file://${__dirname}/index.html`)
|
||||
} else {
|
||||
// No transparency, so we load a fallback that uses basic styles.
|
||||
win.loadURL('file://' + __dirname + '/fallback.html');
|
||||
win.loadURL(`file://${__dirname}/fallback.html`)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ app.on('ready', () => {
|
||||
{label: 'Item2', type: 'radio'},
|
||||
{label: 'Item3', type: 'radio', checked: true},
|
||||
{label: 'Item4', type: 'radio'}
|
||||
]);
|
||||
])
|
||||
tray.setToolTip('This is my application.')
|
||||
tray.setContextMenu(contextMenu)
|
||||
})
|
||||
@@ -31,8 +31,18 @@ __Platform limitations:__
|
||||
you have to call `setContextMenu` again. For example:
|
||||
|
||||
```javascript
|
||||
contextMenu.items[2].checked = false;
|
||||
appIcon.setContextMenu(contextMenu);
|
||||
const {Menu, Tray} = require('electron')
|
||||
const appIcon = new Tray('/path/to/my/icon')
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{label: 'Item1', type: 'radio'},
|
||||
{label: 'Item2', type: 'radio'}
|
||||
])
|
||||
|
||||
// Make a change to the context menu
|
||||
contextMenu.items[2].checked = false
|
||||
|
||||
// Call this again for Linux because we modified the context menu
|
||||
appIcon.setContextMenu(contextMenu)
|
||||
```
|
||||
* On Windows it is recommended to use `ICO` icons to get best visual effects.
|
||||
|
||||
@@ -173,12 +183,15 @@ Sets the hover text for this tray icon.
|
||||
|
||||
Sets the title displayed aside of the tray icon in the status bar.
|
||||
|
||||
#### `tray.setHighlightMode(highlight)` _macOS_
|
||||
#### `tray.setHighlightMode(mode)` _macOS_
|
||||
|
||||
* `highlight` Boolean
|
||||
* `mode` String highlight mode with one of the following values:
|
||||
* `'selection'` - Highlight the tray icon when it is clicked and also when
|
||||
its context menu is open. This is the default.
|
||||
* `'always'` - Always highlight the tray icon.
|
||||
* `'never'` - Never highlight the tray icon.
|
||||
|
||||
Sets whether the tray icon's background becomes highlighted (in blue)
|
||||
when the tray icon is clicked. Defaults to true.
|
||||
Sets when the tray's icon background becomes highlighted (in blue).
|
||||
|
||||
#### `tray.displayBalloon(options)` _Windows_
|
||||
|
||||
|
||||
@@ -9,12 +9,13 @@ the [`BrowserWindow`](browser-window.md) object. An example of accessing the
|
||||
`webContents` object:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron');
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let win = new BrowserWindow({width: 800, height: 1500});
|
||||
win.loadURL('http://github.com');
|
||||
let win = new BrowserWindow({width: 800, height: 1500})
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
let contents = win.webContents;
|
||||
let contents = win.webContents
|
||||
console.log(contents)
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -22,7 +23,8 @@ let contents = win.webContents;
|
||||
These methods can be accessed from the `webContents` module:
|
||||
|
||||
```js
|
||||
const {webContents} = require('electron');
|
||||
const {webContents} = require('electron')
|
||||
console.log(webContents)
|
||||
```
|
||||
|
||||
### `webContents.getAllWebContents()`
|
||||
@@ -431,6 +433,7 @@ first available device will be selected. `callback` should be called with
|
||||
cancel the request.
|
||||
|
||||
```javascript
|
||||
const {app, webContents} = require('electron')
|
||||
app.commandLine.appendSwitch('enable-web-bluetooth')
|
||||
|
||||
app.on('ready', () => {
|
||||
@@ -448,6 +451,10 @@ app.on('ready', () => {
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'view-painted'
|
||||
|
||||
Emitted when a page's view is repainted.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
#### `contents.loadURL(url[, options])`
|
||||
@@ -463,8 +470,9 @@ e.g. the `http://` or `file://`. If the load should bypass http cache then
|
||||
use the `pragma` header to achieve it.
|
||||
|
||||
```javascript
|
||||
const options = {extraHeaders: 'pragma: no-cache\n'};
|
||||
webContents.loadURL(url, options);
|
||||
const {webContents} = require('electron')
|
||||
const options = {extraHeaders: 'pragma: no-cache\n'}
|
||||
webContents.loadURL('https://github.com', options)
|
||||
```
|
||||
|
||||
#### `contents.downloadURL(url)`
|
||||
@@ -479,10 +487,12 @@ Initiates a download of the resource at `url` without navigating. The
|
||||
Returns URL of the current web page.
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({width: 800, height: 600});
|
||||
win.loadURL('http://github.com');
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
let currentURL = win.webContents.getURL();
|
||||
let currentURL = win.webContents.getURL()
|
||||
console.log(currentURL)
|
||||
```
|
||||
|
||||
#### `contents.getTitle()`
|
||||
@@ -616,6 +626,13 @@ Executes the editing command `cut` in web page.
|
||||
|
||||
Executes the editing command `copy` in web page.
|
||||
|
||||
### `contents.copyImageAt(x, y)`
|
||||
|
||||
* `x` Integer
|
||||
* `y` Integer
|
||||
|
||||
Copy the image at the given position to the clipboard.
|
||||
|
||||
#### `contents.paste()`
|
||||
|
||||
Executes the editing command `paste` in web page.
|
||||
@@ -686,12 +703,13 @@ the request can be obtained by subscribing to
|
||||
Stops any `findInPage` request for the `webContents` with the provided `action`.
|
||||
|
||||
```javascript
|
||||
const {webContents} = require('electron')
|
||||
webContents.on('found-in-page', (event, result) => {
|
||||
if (result.finalUpdate)
|
||||
webContents.stopFindInPage('clearSelection');
|
||||
});
|
||||
if (result.finalUpdate) webContents.stopFindInPage('clearSelection')
|
||||
})
|
||||
|
||||
const requestId = webContents.findInPage('api');
|
||||
const requestId = webContents.findInPage('api')
|
||||
console.log(requestId)
|
||||
```
|
||||
|
||||
#### `contents.capturePage([rect, ]callback)`
|
||||
@@ -757,7 +775,7 @@ The `callback` will be called with `callback(error, data)` on completion. The
|
||||
|
||||
By default, an empty `options` will be regarded as:
|
||||
|
||||
```javascript
|
||||
```
|
||||
{
|
||||
marginsType: 0,
|
||||
printBackground: false,
|
||||
@@ -769,23 +787,22 @@ By default, an empty `options` will be regarded as:
|
||||
An example of `webContents.printToPDF`:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron');
|
||||
const fs = require('fs');
|
||||
const {BrowserWindow} = require('electron')
|
||||
const fs = require('fs')
|
||||
|
||||
let win = new BrowserWindow({width: 800, height: 600});
|
||||
win.loadURL('http://github.com');
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
// Use default printing options
|
||||
win.webContents.printToPDF({}, (error, data) => {
|
||||
if (error) throw error;
|
||||
if (error) throw error
|
||||
fs.writeFile('/tmp/print.pdf', data, (error) => {
|
||||
if (error)
|
||||
throw error;
|
||||
console.log('Write PDF successfully.');
|
||||
});
|
||||
});
|
||||
});
|
||||
if (error) throw error
|
||||
console.log('Write PDF successfully.')
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### `contents.addWorkSpace(path)`
|
||||
@@ -796,9 +813,11 @@ Adds the specified path to DevTools workspace. Must be used after DevTools
|
||||
creation:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.webContents.on('devtools-opened', () => {
|
||||
win.webContents.addWorkSpace(__dirname);
|
||||
});
|
||||
win.webContents.addWorkSpace(__dirname)
|
||||
})
|
||||
```
|
||||
|
||||
#### `contents.removeWorkSpace(path)`
|
||||
@@ -859,15 +878,16 @@ An example of sending messages from the main process to the renderer process:
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
let win = null;
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
let win = null
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow({width: 800, height: 600});
|
||||
win.loadURL(`file://${__dirname}/index.html`);
|
||||
win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL(`file://${__dirname}/index.html`)
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
win.webContents.send('ping', 'whoooooooh!');
|
||||
});
|
||||
});
|
||||
win.webContents.send('ping', 'whoooooooh!')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -876,8 +896,8 @@ app.on('ready', () => {
|
||||
<body>
|
||||
<script>
|
||||
require('electron').ipcRenderer.on('ping', (event, message) => {
|
||||
console.log(message); // Prints "whoooooooh!"
|
||||
});
|
||||
console.log(message) // Prints 'whoooooooh!'
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1006,14 +1026,16 @@ the cursor when dragging.
|
||||
Returns true if the process of saving page has been initiated successfully.
|
||||
|
||||
```javascript
|
||||
win.loadURL('https://github.com');
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
|
||||
win.loadURL('https://github.com')
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
win.webContents.savePage('/tmp/test.html', 'HTMLComplete', (error) => {
|
||||
if (!error)
|
||||
console.log('Save page successfully');
|
||||
});
|
||||
});
|
||||
if (!error) console.log('Save page successfully')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### `contents.showDefinitionForSelection()` _macOS_
|
||||
@@ -1050,24 +1072,28 @@ Get the debugger instance for this webContents.
|
||||
Debugger API serves as an alternate transport for [remote debugging protocol][rdp].
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
|
||||
try {
|
||||
win.webContents.debugger.attach('1.1');
|
||||
} catch(err) {
|
||||
console.log('Debugger attach failed : ', err);
|
||||
};
|
||||
win.webContents.debugger.attach('1.1')
|
||||
} catch (err) {
|
||||
console.log('Debugger attach failed : ', err)
|
||||
}
|
||||
|
||||
win.webContents.debugger.on('detach', (event, reason) => {
|
||||
console.log('Debugger detached due to : ', reason);
|
||||
});
|
||||
console.log('Debugger detached due to : ', reason)
|
||||
})
|
||||
|
||||
win.webContents.debugger.on('message', (event, method, params) => {
|
||||
if (method === 'Network.requestWillBeSent') {
|
||||
if (params.request.url === 'https://www.github.com')
|
||||
win.webContents.debugger.detach();
|
||||
if (params.request.url === 'https://www.github.com') {
|
||||
win.webContents.debugger.detach()
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
win.webContents.debugger.sendCommand('Network.enable');
|
||||
win.webContents.debugger.sendCommand('Network.enable')
|
||||
```
|
||||
|
||||
### Instance Methods
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
An example of zooming current page to 200%.
|
||||
|
||||
```javascript
|
||||
const {webFrame} = require('electron');
|
||||
const {webFrame} = require('electron')
|
||||
|
||||
webFrame.setZoomFactor(2);
|
||||
webFrame.setZoomFactor(2)
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -58,11 +58,12 @@ whether the word passed is correctly spelled.
|
||||
An example of using [node-spellchecker][spellchecker] as provider:
|
||||
|
||||
```javascript
|
||||
const {webFrame} = require('electron')
|
||||
webFrame.setSpellCheckProvider('en-US', true, {
|
||||
spellCheck(text) {
|
||||
return !(require('spellchecker').isMisspelled(text));
|
||||
spellCheck (text) {
|
||||
return !(require('spellchecker').isMisspelled(text))
|
||||
}
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### `webFrame.registerURLSchemeAsSecure(scheme)`
|
||||
@@ -112,12 +113,13 @@ Returns an object describing usage information of Blink's internal memory
|
||||
caches.
|
||||
|
||||
```javascript
|
||||
const {webFrame} = require('electron')
|
||||
console.log(webFrame.getResourceUsage())
|
||||
```
|
||||
|
||||
This will generate:
|
||||
|
||||
```javascript
|
||||
```
|
||||
{
|
||||
images: {
|
||||
count: 22,
|
||||
@@ -130,8 +132,8 @@ This will generate:
|
||||
cssStyleSheets: { /* same with "images" */ },
|
||||
xslStyleSheets: { /* same with "images" */ },
|
||||
fonts: { /* same with "images" */ },
|
||||
other: { /* same with "images" */ },
|
||||
}
|
||||
other: { /* same with "images" */ }
|
||||
})
|
||||
```
|
||||
|
||||
### `webFrame.clearCache()`
|
||||
|
||||
@@ -12,7 +12,7 @@ app. It doesn't have the same permissions as your web page and all interactions
|
||||
between your app and embedded content will be asynchronous. This keeps your app
|
||||
safe from the embedded content.
|
||||
|
||||
For security purpose, `webview` can only be used in `BrowserWindow`s that have
|
||||
For security purposes, `webview` can only be used in `BrowserWindow`s that have
|
||||
`nodeIntegration` enabled.
|
||||
|
||||
## Example
|
||||
@@ -35,20 +35,20 @@ and displays a "loading..." message during the load time:
|
||||
```html
|
||||
<script>
|
||||
onload = () => {
|
||||
const webview = document.getElementById('foo');
|
||||
const indicator = document.querySelector('.indicator');
|
||||
const webview = document.getElementById('foo')
|
||||
const indicator = document.querySelector('.indicator')
|
||||
|
||||
const loadstart = () => {
|
||||
indicator.innerText = 'loading...';
|
||||
};
|
||||
indicator.innerText = 'loading...'
|
||||
}
|
||||
|
||||
const loadstop = () => {
|
||||
indicator.innerText = '';
|
||||
};
|
||||
indicator.innerText = ''
|
||||
}
|
||||
|
||||
webview.addEventListener('did-start-loading', loadstart);
|
||||
webview.addEventListener('did-stop-loading', loadstop);
|
||||
};
|
||||
webview.addEventListener('did-start-loading', loadstart)
|
||||
webview.addEventListener('did-stop-loading', loadstop)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -223,9 +223,10 @@ The `webview` tag has the following methods:
|
||||
**Example**
|
||||
|
||||
```javascript
|
||||
const webview = document.getElementById('foo')
|
||||
webview.addEventListener('dom-ready', () => {
|
||||
webview.openDevTools();
|
||||
});
|
||||
webview.openDevTools()
|
||||
})
|
||||
```
|
||||
|
||||
### `<webview>.loadURL(url[, options])`
|
||||
@@ -618,9 +619,10 @@ The following example code forwards all log messages to the embedder's console
|
||||
without regard for log level or other properties.
|
||||
|
||||
```javascript
|
||||
const webview = document.getElementById('foo')
|
||||
webview.addEventListener('console-message', (e) => {
|
||||
console.log('Guest page logged a message:', e.message);
|
||||
});
|
||||
console.log('Guest page logged a message:', e.message)
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'found-in-page'
|
||||
@@ -638,12 +640,13 @@ Fired when a result is available for
|
||||
[`webview.findInPage`](web-view-tag.md#webviewtagfindinpage) request.
|
||||
|
||||
```javascript
|
||||
const webview = document.getElementById('foo')
|
||||
webview.addEventListener('found-in-page', (e) => {
|
||||
if (e.result.finalUpdate)
|
||||
webview.stopFindInPage('keepSelection');
|
||||
});
|
||||
if (e.result.finalUpdate) webview.stopFindInPage('keepSelection')
|
||||
})
|
||||
|
||||
const requestId = webview.findInPage('test');
|
||||
const requestId = webview.findInPage('test')
|
||||
console.log(requestId)
|
||||
```
|
||||
|
||||
### Event: 'new-window'
|
||||
@@ -662,14 +665,15 @@ Fired when the guest page attempts to open a new browser window.
|
||||
The following example code opens the new url in system's default browser.
|
||||
|
||||
```javascript
|
||||
const {shell} = require('electron');
|
||||
const {shell} = require('electron')
|
||||
const webview = document.getElementById('foo')
|
||||
|
||||
webview.addEventListener('new-window', (e) => {
|
||||
const protocol = require('url').parse(e.url).protocol;
|
||||
const protocol = require('url').parse(e.url).protocol
|
||||
if (protocol === 'http:' || protocol === 'https:') {
|
||||
shell.openExternal(e.url);
|
||||
shell.openExternal(e.url)
|
||||
}
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'will-navigate'
|
||||
@@ -722,9 +726,10 @@ The following example code navigates the `webview` to `about:blank` when the
|
||||
guest attempts to close itself.
|
||||
|
||||
```javascript
|
||||
const webview = document.getElementById('foo')
|
||||
webview.addEventListener('close', () => {
|
||||
webview.src = 'about:blank';
|
||||
});
|
||||
webview.src = 'about:blank'
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'ipc-message'
|
||||
@@ -741,19 +746,20 @@ between guest page and embedder page:
|
||||
|
||||
```javascript
|
||||
// In embedder page.
|
||||
const webview = document.getElementById('foo')
|
||||
webview.addEventListener('ipc-message', (event) => {
|
||||
console.log(event.channel);
|
||||
console.log(event.channel)
|
||||
// Prints "pong"
|
||||
});
|
||||
webview.send('ping');
|
||||
})
|
||||
webview.send('ping')
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In guest page.
|
||||
const {ipcRenderer} = require('electron');
|
||||
const {ipcRenderer} = require('electron')
|
||||
ipcRenderer.on('ping', () => {
|
||||
ipcRenderer.sendToHost('pong');
|
||||
});
|
||||
ipcRenderer.sendToHost('pong')
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'crashed'
|
||||
|
||||
@@ -10,8 +10,8 @@ The proxy has limited standard functionality implemented to be
|
||||
compatible with traditional web pages. For full control of the new window
|
||||
you should create a `BrowserWindow` directly.
|
||||
|
||||
The newly created `BrowserWindow` will inherit parent window's options by
|
||||
default, to override inherited options you can set them in the `features`
|
||||
The newly created `BrowserWindow` will inherit the parent window's options by
|
||||
default. To override inherited options you can set them in the `features`
|
||||
string.
|
||||
|
||||
### `window.open(url[, frameName][, features])`
|
||||
@@ -45,33 +45,33 @@ limited functionality with the child window.
|
||||
|
||||
The `BrowserWindowProxy` object has the following instance methods:
|
||||
|
||||
#### `BrowserWindowProxy.blur()`
|
||||
#### `win.blur()`
|
||||
|
||||
Removes focus from the child window.
|
||||
|
||||
#### `BrowserWindowProxy.close()`
|
||||
#### `win.close()`
|
||||
|
||||
Forcefully closes the child window without calling its unload event.
|
||||
|
||||
#### `BrowserWindowProxy.closed`
|
||||
#### `win.closed`
|
||||
|
||||
Set to true after the child window gets closed.
|
||||
|
||||
#### `BrowserWindowProxy.eval(code)`
|
||||
#### `win.eval(code)`
|
||||
|
||||
* `code` String
|
||||
|
||||
Evaluates the code in the child window.
|
||||
|
||||
#### `BrowserWindowProxy.focus()`
|
||||
#### `win.focus()`
|
||||
|
||||
Focuses the child window (brings the window to front).
|
||||
|
||||
#### `BrowserWindowProxy.print()`
|
||||
#### `win.print()`
|
||||
|
||||
Invokes the print dialog on the child window.
|
||||
|
||||
#### `BrowserWindowProxy.postMessage(message, targetOrigin)`
|
||||
#### `win.postMessage(message, targetOrigin)`
|
||||
|
||||
* `message` String
|
||||
* `targetOrigin` String
|
||||
|
||||
@@ -58,17 +58,25 @@ $ python script\build.py -c D
|
||||
After building is done, you can find `electron.exe` under `out\D` (debug
|
||||
target) or under `out\R` (release target).
|
||||
|
||||
## 64bit Build
|
||||
## 32bit Build
|
||||
|
||||
To build for the 64bit target, you need to pass `--target_arch=x64` when running
|
||||
the bootstrap script:
|
||||
To build for the 32bit target, you need to pass `--target_arch=ia32` when
|
||||
running the bootstrap script:
|
||||
|
||||
```powershell
|
||||
$ python script\bootstrap.py -v --target_arch=x64
|
||||
$ python script\bootstrap.py -v --target_arch=ia32
|
||||
```
|
||||
|
||||
The other building steps are exactly the same.
|
||||
|
||||
## Visual Studio project
|
||||
|
||||
To generate a Visual Studio project, you can pass the `--msvs` parameter:
|
||||
|
||||
```powershell
|
||||
$ python script\bootstrap.py --msvs
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Test your changes conform to the project coding style using:
|
||||
|
||||
28
docs/faq.md
28
docs/faq.md
@@ -36,17 +36,17 @@ renderers through the `remote` property of `electron` module:
|
||||
// In the main process.
|
||||
global.sharedObject = {
|
||||
someProperty: 'default value'
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In page 1.
|
||||
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value';
|
||||
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value'
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In page 2.
|
||||
console.log(require('electron').remote.getGlobal('sharedObject').someProperty);
|
||||
console.log(require('electron').remote.getGlobal('sharedObject').someProperty)
|
||||
```
|
||||
|
||||
## My app's window/tray disappeared after a few minutes.
|
||||
@@ -63,18 +63,22 @@ If you want a quick fix, you can make the variables global by changing your
|
||||
code from this:
|
||||
|
||||
```javascript
|
||||
const {app, Tray} = require('electron')
|
||||
app.on('ready', () => {
|
||||
const tray = new Tray('/path/to/icon.png');
|
||||
});
|
||||
const tray = new Tray('/path/to/icon.png')
|
||||
tray.setTitle('hello world')
|
||||
})
|
||||
```
|
||||
|
||||
to this:
|
||||
|
||||
```javascript
|
||||
let tray = null;
|
||||
const {app, Tray} = require('electron')
|
||||
let tray = null
|
||||
app.on('ready', () => {
|
||||
tray = new Tray('/path/to/icon.png');
|
||||
});
|
||||
tray = new Tray('/path/to/icon.png')
|
||||
tray.setTitle('hello world')
|
||||
})
|
||||
```
|
||||
|
||||
## I can not use jQuery/RequireJS/Meteor/AngularJS in Electron.
|
||||
@@ -87,11 +91,13 @@ To solve this, you can turn off node integration in Electron:
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: false
|
||||
}
|
||||
});
|
||||
})
|
||||
win.show()
|
||||
```
|
||||
|
||||
But if you want to keep the abilities of using Node.js and Electron APIs, you
|
||||
@@ -114,7 +120,7 @@ delete window.module;
|
||||
When using Electron's built-in module you might encounter an error like this:
|
||||
|
||||
```
|
||||
> require('electron').webFrame.setZoomFactor(1.0);
|
||||
> require('electron').webFrame.setZoomFactor(1.0)
|
||||
Uncaught TypeError: Cannot read property 'setZoomLevel' of undefined
|
||||
```
|
||||
|
||||
@@ -125,7 +131,7 @@ To verify whether you are using the correct built-in module, you can print the
|
||||
path of the `electron` module:
|
||||
|
||||
```javascript
|
||||
console.log(require.resolve('electron'));
|
||||
console.log(require.resolve('electron'))
|
||||
```
|
||||
|
||||
and then check if it is in the following form:
|
||||
|
||||
@@ -51,29 +51,29 @@ $ asar list /path/to/example.asar
|
||||
Read a file in the `asar` archive:
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
fs.readFileSync('/path/to/example.asar/file.txt');
|
||||
const fs = require('fs')
|
||||
fs.readFileSync('/path/to/example.asar/file.txt')
|
||||
```
|
||||
|
||||
List all files under the root of the archive:
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
fs.readdirSync('/path/to/example.asar');
|
||||
const fs = require('fs')
|
||||
fs.readdirSync('/path/to/example.asar')
|
||||
```
|
||||
|
||||
Use a module from the archive:
|
||||
|
||||
```javascript
|
||||
require('/path/to/example.asar/dir/module.js');
|
||||
require('/path/to/example.asar/dir/module.js')
|
||||
```
|
||||
|
||||
You can also display a web page in an `asar` archive with `BrowserWindow`:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron');
|
||||
let win = new BrowserWindow({width: 800, height: 600});
|
||||
win.loadURL('file:///path/to/example.asar/static/index.html');
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow({width: 800, height: 600})
|
||||
win.loadURL('file:///path/to/example.asar/static/index.html')
|
||||
```
|
||||
|
||||
### Web API
|
||||
@@ -85,33 +85,34 @@ For example, to get a file with `$.get`:
|
||||
|
||||
```html
|
||||
<script>
|
||||
let $ = require('./jquery.min.js');
|
||||
let $ = require('./jquery.min.js')
|
||||
$.get('file:///path/to/example.asar/file.txt', (data) => {
|
||||
console.log(data);
|
||||
});
|
||||
console.log(data)
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### Treating an `asar` Archive as a Normal File
|
||||
|
||||
For some cases like verifying the `asar` archive's checksum, we need to read the
|
||||
content of `asar` archive as file. For this purpose you can use the built-in
|
||||
content of an `asar` archive as a file. For this purpose you can use the built-in
|
||||
`original-fs` module which provides original `fs` APIs without `asar` support:
|
||||
|
||||
```javascript
|
||||
const originalFs = require('original-fs');
|
||||
originalFs.readFileSync('/path/to/example.asar');
|
||||
const originalFs = require('original-fs')
|
||||
originalFs.readFileSync('/path/to/example.asar')
|
||||
```
|
||||
|
||||
You can also set `process.noAsar` to `true` to disable the support for `asar` in
|
||||
the `fs` module:
|
||||
|
||||
```javascript
|
||||
process.noAsar = true;
|
||||
fs.readFileSync('/path/to/example.asar');
|
||||
const fs = require('fs')
|
||||
process.noAsar = true
|
||||
fs.readFileSync('/path/to/example.asar')
|
||||
```
|
||||
|
||||
## Limitations on Node API
|
||||
## Limitations of the Node API
|
||||
|
||||
Even though we tried hard to make `asar` archives in the Node API work like
|
||||
directories as much as possible, there are still limitations due to the
|
||||
|
||||
@@ -20,11 +20,11 @@ the currently running operating system's native notification APIs to display it.
|
||||
```javascript
|
||||
let myNotification = new Notification('Title', {
|
||||
body: 'Lorem Ipsum Dolor Sit Amet'
|
||||
});
|
||||
})
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked');
|
||||
};
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
```
|
||||
|
||||
While code and user experience across operating systems are similar, there
|
||||
@@ -75,14 +75,16 @@ To add a file to recent documents, you can use the
|
||||
[app.addRecentDocument][addrecentdocument] API:
|
||||
|
||||
```javascript
|
||||
app.addRecentDocument('/Users/USERNAME/Desktop/work.type');
|
||||
const {app} = require('electron')
|
||||
app.addRecentDocument('/Users/USERNAME/Desktop/work.type')
|
||||
```
|
||||
|
||||
And you can use [app.clearRecentDocuments][clearrecentdocuments] API to empty
|
||||
the recent documents list:
|
||||
|
||||
```javascript
|
||||
app.clearRecentDocuments();
|
||||
const {app} = require('electron')
|
||||
app.clearRecentDocuments()
|
||||
```
|
||||
|
||||
### Windows Notes
|
||||
@@ -113,19 +115,17 @@ To set your custom dock menu, you can use the `app.dock.setMenu` API, which is
|
||||
only available on macOS:
|
||||
|
||||
```javascript
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const Menu = electron.Menu;
|
||||
const {app, Menu} = require('electron')
|
||||
|
||||
const dockMenu = Menu.buildFromTemplate([
|
||||
{ label: 'New Window', click() { console.log('New Window'); } },
|
||||
{ label: 'New Window with Settings', submenu: [
|
||||
{ label: 'Basic' },
|
||||
{ label: 'Pro'}
|
||||
{label: 'New Window', click () { console.log('New Window') }},
|
||||
{label: 'New Window with Settings', submenu: [
|
||||
{label: 'Basic'},
|
||||
{label: 'Pro'}
|
||||
]},
|
||||
{ label: 'New Command...'}
|
||||
]);
|
||||
app.dock.setMenu(dockMenu);
|
||||
{label: 'New Command...'}
|
||||
])
|
||||
app.dock.setMenu(dockMenu)
|
||||
```
|
||||
|
||||
## User Tasks (Windows)
|
||||
@@ -162,6 +162,7 @@ To set user tasks for your application, you can use
|
||||
[app.setUserTasks][setusertaskstasks] API:
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
app.setUserTasks([
|
||||
{
|
||||
program: process.execPath,
|
||||
@@ -171,13 +172,14 @@ app.setUserTasks([
|
||||
title: 'New Window',
|
||||
description: 'Create a new window'
|
||||
}
|
||||
]);
|
||||
])
|
||||
```
|
||||
|
||||
To clean your tasks list, just call `app.setUserTasks` with an empty array:
|
||||
|
||||
```javascript
|
||||
app.setUserTasks([]);
|
||||
const {app} = require('electron')
|
||||
app.setUserTasks([])
|
||||
```
|
||||
|
||||
The user tasks will still show even after your application closes, so the icon
|
||||
@@ -209,34 +211,36 @@ You can use [BrowserWindow.setThumbarButtons][setthumbarbuttons] to set
|
||||
thumbnail toolbar in your application:
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron');
|
||||
const path = require('path');
|
||||
const {BrowserWindow} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
let win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600
|
||||
});
|
||||
})
|
||||
|
||||
win.setThumbarButtons([
|
||||
{
|
||||
tooltip: 'button1',
|
||||
icon: path.join(__dirname, 'button1.png'),
|
||||
click() { console.log('button1 clicked'); }
|
||||
click () { console.log('button1 clicked') }
|
||||
},
|
||||
{
|
||||
tooltip: 'button2',
|
||||
icon: path.join(__dirname, 'button2.png'),
|
||||
flags: ['enabled', 'dismissonclick'],
|
||||
click() { console.log('button2 clicked.'); }
|
||||
click () { console.log('button2 clicked.') }
|
||||
}
|
||||
]);
|
||||
])
|
||||
```
|
||||
|
||||
To clean thumbnail toolbar buttons, just call `BrowserWindow.setThumbarButtons`
|
||||
with an empty array:
|
||||
|
||||
```javascript
|
||||
win.setThumbarButtons([]);
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.setThumbarButtons([])
|
||||
```
|
||||
|
||||
## Unity Launcher Shortcuts (Linux)
|
||||
@@ -267,8 +271,9 @@ To set the progress bar for a Window, you can use the
|
||||
[BrowserWindow.setProgressBar][setprogressbar] API:
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({...});
|
||||
win.setProgressBar(0.5);
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.setProgressBar(0.5)
|
||||
```
|
||||
|
||||
## Icon Overlays in Taskbar (Windows)
|
||||
@@ -294,8 +299,9 @@ To set the overlay icon for a window, you can use the
|
||||
[BrowserWindow.setOverlayIcon][setoverlayicon] API:
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({...});
|
||||
win.setOverlayIcon('path/to/overlay.png', 'Description for overlay');
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.setOverlayIcon('path/to/overlay.png', 'Description for overlay')
|
||||
```
|
||||
|
||||
## Represented File of Window (macOS)
|
||||
@@ -316,9 +322,10 @@ To set the represented file of window, you can use the
|
||||
[BrowserWindow.setDocumentEdited][setdocumentedited] APIs:
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({...});
|
||||
win.setRepresentedFilename('/etc/passwd');
|
||||
win.setDocumentEdited(true);
|
||||
const {BrowserWindow} = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
win.setRepresentedFilename('/etc/passwd')
|
||||
win.setDocumentEdited(true)
|
||||
```
|
||||
|
||||
## Dragging files out of the window
|
||||
@@ -342,6 +349,7 @@ In web page:
|
||||
In the main process:
|
||||
|
||||
```javascript
|
||||
const {ipcMain} = require('electron')
|
||||
ipcMain.on('ondragstart', (event, filePath) => {
|
||||
event.sender.startDrag({
|
||||
file: filePath,
|
||||
|
||||
@@ -6,16 +6,14 @@ using standard HTML5 APIs, as shown in the following example.
|
||||
_main.js_
|
||||
|
||||
```javascript
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
let onlineStatusWindow;
|
||||
let onlineStatusWindow
|
||||
|
||||
app.on('ready', () => {
|
||||
onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false });
|
||||
onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`);
|
||||
});
|
||||
onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false })
|
||||
onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`)
|
||||
})
|
||||
```
|
||||
|
||||
_online-status.html_
|
||||
@@ -26,13 +24,13 @@ _online-status.html_
|
||||
<body>
|
||||
<script>
|
||||
const alertOnlineStatus = () => {
|
||||
window.alert(navigator.onLine ? 'online' : 'offline');
|
||||
};
|
||||
window.alert(navigator.onLine ? 'online' : 'offline')
|
||||
}
|
||||
|
||||
window.addEventListener('online', alertOnlineStatus);
|
||||
window.addEventListener('offline', alertOnlineStatus);
|
||||
window.addEventListener('online', alertOnlineStatus)
|
||||
window.addEventListener('offline', alertOnlineStatus)
|
||||
|
||||
alertOnlineStatus();
|
||||
alertOnlineStatus()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -47,21 +45,17 @@ to the main process and handled as needed, as shown in the following example.
|
||||
_main.js_
|
||||
|
||||
```javascript
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const ipcMain = electron.ipcMain;
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
let onlineStatusWindow;
|
||||
const {app, BrowserWindow, ipcMain} = require('electron')
|
||||
let onlineStatusWindow
|
||||
|
||||
app.on('ready', () => {
|
||||
onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false });
|
||||
onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`);
|
||||
});
|
||||
onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false })
|
||||
onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`)
|
||||
})
|
||||
|
||||
ipcMain.on('online-status-changed', (event, status) => {
|
||||
console.log(status);
|
||||
});
|
||||
console.log(status)
|
||||
})
|
||||
```
|
||||
|
||||
_online-status.html_
|
||||
@@ -71,15 +65,15 @@ _online-status.html_
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
const {ipcRenderer} = require('electron');
|
||||
const {ipcRenderer} = require('electron')
|
||||
const updateOnlineStatus = () => {
|
||||
ipcRenderer.send('online-status-changed', navigator.onLine ? 'online' : 'offline');
|
||||
};
|
||||
ipcRenderer.send('online-status-changed', navigator.onLine ? 'online' : 'offline')
|
||||
}
|
||||
|
||||
window.addEventListener('online', updateOnlineStatus);
|
||||
window.addEventListener('offline', updateOnlineStatus);
|
||||
window.addEventListener('online', updateOnlineStatus)
|
||||
window.addEventListener('offline', updateOnlineStatus)
|
||||
|
||||
updateOnlineStatus();
|
||||
updateOnlineStatus()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -80,56 +80,52 @@ The `main.js` should create windows and handle system events, a typical
|
||||
example being:
|
||||
|
||||
```javascript
|
||||
const electron = require('electron');
|
||||
// Module to control application life.
|
||||
const {app} = electron;
|
||||
// Module to create native browser window.
|
||||
const {BrowserWindow} = electron;
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win;
|
||||
let win
|
||||
|
||||
function createWindow() {
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({width: 800, height: 600});
|
||||
win = new BrowserWindow({width: 800, height: 600})
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadURL(`file://${__dirname}/index.html`);
|
||||
win.loadURL(`file://${__dirname}/index.html`)
|
||||
|
||||
// Open the DevTools.
|
||||
win.webContents.openDevTools();
|
||||
win.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
win.on('closed', () => {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
win = null;
|
||||
});
|
||||
win = 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);
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
app.quit()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
createWindow();
|
||||
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.
|
||||
|
||||
@@ -20,6 +20,9 @@ before the app ready event. Also, turn on `plugins` option of `BrowserWindow`.
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
// Specify flash path, supposing it is placed in the same directory with main.js.
|
||||
let pluginName
|
||||
switch (process.platform) {
|
||||
@@ -39,7 +42,7 @@ app.commandLine.appendSwitch('ppapi-flash-path', path.join(__dirname, pluginName
|
||||
app.commandLine.appendSwitch('ppapi-flash-version', '17.0.0.169')
|
||||
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow({
|
||||
let win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
@@ -48,7 +51,7 @@ app.on('ready', () => {
|
||||
})
|
||||
win.loadURL(`file://${__dirname}/index.html`)
|
||||
// Something else
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
You can also try loading the system wide Pepper Flash plugin instead of shipping
|
||||
|
||||
@@ -79,7 +79,7 @@ upstream, except that you have to manually specify how to connect chrome driver
|
||||
and where to find Electron's binary:
|
||||
|
||||
```javascript
|
||||
const webdriver = require('selenium-webdriver');
|
||||
const webdriver = require('selenium-webdriver')
|
||||
|
||||
const driver = new webdriver.Builder()
|
||||
// The "9515" is the port opened by chrome driver.
|
||||
@@ -87,22 +87,22 @@ const driver = new webdriver.Builder()
|
||||
.withCapabilities({
|
||||
chromeOptions: {
|
||||
// Here is the path to your Electron binary.
|
||||
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron',
|
||||
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
|
||||
}
|
||||
})
|
||||
.forBrowser('electron')
|
||||
.build();
|
||||
.build()
|
||||
|
||||
driver.get('http://www.google.com');
|
||||
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
|
||||
driver.findElement(webdriver.By.name('btnG')).click();
|
||||
driver.get('http://www.google.com')
|
||||
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver')
|
||||
driver.findElement(webdriver.By.name('btnG')).click()
|
||||
driver.wait(() => {
|
||||
return driver.getTitle().then((title) => {
|
||||
return title === 'webdriver - Google Search';
|
||||
});
|
||||
}, 1000);
|
||||
return driver.getTitle().then((title) => {
|
||||
return title === 'webdriver - Google Search'
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
driver.quit();
|
||||
driver.quit()
|
||||
```
|
||||
|
||||
## Setting up with WebdriverIO
|
||||
@@ -132,7 +132,7 @@ $ npm install webdriverio
|
||||
### 3. Connect to chrome driver
|
||||
|
||||
```javascript
|
||||
const webdriverio = require('webdriverio');
|
||||
const webdriverio = require('webdriverio')
|
||||
const options = {
|
||||
host: 'localhost', // Use localhost as chrome driver server
|
||||
port: 9515, // "9515" is the port opened by chrome driver.
|
||||
@@ -143,9 +143,9 @@ const options = {
|
||||
args: [/* cli arguments */] // Optional, perhaps 'app=' + /path/to/your/app/
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let client = webdriverio.remote(options);
|
||||
let client = webdriverio.remote(options)
|
||||
|
||||
client
|
||||
.init()
|
||||
@@ -153,9 +153,9 @@ client
|
||||
.setValue('#q', 'webdriverio')
|
||||
.click('#btnG')
|
||||
.getTitle().then((title) => {
|
||||
console.log('Title was: ' + title);
|
||||
console.log('Title was: ' + title)
|
||||
})
|
||||
.end();
|
||||
.end()
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -51,23 +51,26 @@ enabled.
|
||||
Example code:
|
||||
|
||||
```javascript
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
// You have to pass the filename of `widevinecdmadapter` here, it is
|
||||
// * `widevinecdmadapter.plugin` on macOS,
|
||||
// * `libwidevinecdmadapter.so` on Linux,
|
||||
// * `widevinecdmadapter.dll` on Windows.
|
||||
app.commandLine.appendSwitch('widevine-cdm-path', '/path/to/widevinecdmadapter.plugin');
|
||||
app.commandLine.appendSwitch('widevine-cdm-path', '/path/to/widevinecdmadapter.plugin')
|
||||
// The version of plugin can be got from `chrome://plugins` page in Chrome.
|
||||
app.commandLine.appendSwitch('widevine-cdm-version', '1.4.8.866');
|
||||
app.commandLine.appendSwitch('widevine-cdm-version', '1.4.8.866')
|
||||
|
||||
let win = null;
|
||||
let win = null
|
||||
app.on('ready', () => {
|
||||
win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
// The `plugins` have to be enabled.
|
||||
plugins: true
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
win.show()
|
||||
})
|
||||
```
|
||||
|
||||
## Verifying the plugin
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '1.3.0',
|
||||
'version%': '1.3.1',
|
||||
},
|
||||
'includes': [
|
||||
'filenames.gypi',
|
||||
|
||||
@@ -1,71 +1,66 @@
|
||||
'use strict'
|
||||
|
||||
const app = require('electron').app
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const {app} = require('electron')
|
||||
const {EventEmitter} = require('events')
|
||||
const squirrelUpdate = require('./squirrel-update-win')
|
||||
const util = require('util')
|
||||
|
||||
function AutoUpdater () {
|
||||
EventEmitter.call(this)
|
||||
}
|
||||
|
||||
util.inherits(AutoUpdater, EventEmitter)
|
||||
|
||||
AutoUpdater.prototype.quitAndInstall = function () {
|
||||
if (!this.updateAvailable) {
|
||||
return this.emitError('No update available, can\'t quit and install')
|
||||
}
|
||||
squirrelUpdate.processStart()
|
||||
return app.quit()
|
||||
}
|
||||
|
||||
AutoUpdater.prototype.getFeedURL = function () {
|
||||
return this.updateURL
|
||||
}
|
||||
|
||||
AutoUpdater.prototype.setFeedURL = function (updateURL, headers) {
|
||||
this.updateURL = updateURL
|
||||
}
|
||||
|
||||
AutoUpdater.prototype.checkForUpdates = function () {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set')
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel')
|
||||
}
|
||||
this.emit('checking-for-update')
|
||||
squirrelUpdate.download(this.updateURL, (error, update) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
class AutoUpdater extends EventEmitter {
|
||||
quitAndInstall () {
|
||||
if (!this.updateAvailable) {
|
||||
return this.emitError('No update available, can\'t quit and install')
|
||||
}
|
||||
if (update == null) {
|
||||
this.updateAvailable = false
|
||||
return this.emit('update-not-available')
|
||||
squirrelUpdate.processStart()
|
||||
return app.quit()
|
||||
}
|
||||
|
||||
getFeedURL () {
|
||||
return this.updateURL
|
||||
}
|
||||
|
||||
setFeedURL (updateURL, headers) {
|
||||
this.updateURL = updateURL
|
||||
}
|
||||
|
||||
checkForUpdates () {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set')
|
||||
}
|
||||
this.updateAvailable = true
|
||||
this.emit('update-available')
|
||||
squirrelUpdate.update(this.updateURL, (error) => {
|
||||
var date, releaseNotes, version
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel')
|
||||
}
|
||||
this.emit('checking-for-update')
|
||||
squirrelUpdate.download(this.updateURL, (error, update) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
}
|
||||
releaseNotes = update.releaseNotes
|
||||
version = update.version
|
||||
if (update == null) {
|
||||
this.updateAvailable = false
|
||||
return this.emit('update-not-available')
|
||||
}
|
||||
this.updateAvailable = true
|
||||
this.emit('update-available')
|
||||
squirrelUpdate.update(this.updateURL, (error) => {
|
||||
var date, releaseNotes, version
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
}
|
||||
releaseNotes = update.releaseNotes
|
||||
version = update.version
|
||||
|
||||
// Following information is not available on Windows, so fake them.
|
||||
date = new Date()
|
||||
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
|
||||
this.quitAndInstall()
|
||||
// Following information is not available on Windows, so fake them.
|
||||
date = new Date()
|
||||
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
|
||||
this.quitAndInstall()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
AutoUpdater.prototype.emitError = function (message) {
|
||||
return this.emit('error', new Error(message), message)
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
emitError (message) {
|
||||
return this.emit('error', new Error(message), message)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AutoUpdater()
|
||||
|
||||
@@ -7,7 +7,7 @@ const roles = {
|
||||
}
|
||||
},
|
||||
close: {
|
||||
label: 'Close',
|
||||
label: process.platform === 'darwin' ? 'Close Window' : 'Close',
|
||||
accelerator: 'CommandOrControl+W',
|
||||
windowMethod: 'close'
|
||||
},
|
||||
|
||||
@@ -93,8 +93,8 @@ if (process.platform === 'win32') {
|
||||
|
||||
if (fs.statSyncNoException(updateDotExe)) {
|
||||
var packageDir = path.dirname(path.resolve(updateDotExe))
|
||||
var packageName = path.basename(packageDir)
|
||||
var exeName = path.basename(process.execPath).replace(/\.exe$/i, '')
|
||||
var packageName = path.basename(packageDir).replace(/\s/g, '')
|
||||
var exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '')
|
||||
|
||||
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`)
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
// Recognize certain types of objects.
|
||||
if (value === null) {
|
||||
meta.type = 'value'
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
} else if (ArrayBuffer.isView(value)) {
|
||||
meta.type = 'buffer'
|
||||
} else if (Array.isArray(value)) {
|
||||
meta.type = 'array'
|
||||
@@ -148,7 +148,7 @@ const unwrapArgs = function (sender, args) {
|
||||
case 'array':
|
||||
return unwrapArgs(sender, meta.value)
|
||||
case 'buffer':
|
||||
return new Buffer(meta.value)
|
||||
return Buffer.from(meta.value)
|
||||
case 'date':
|
||||
return new Date(meta.value)
|
||||
case 'promise':
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
|
||||
var hasProp = {}.hasOwnProperty
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
||||
// Cache asar archive objects.
|
||||
var cachedArchives = {}
|
||||
const cachedArchives = {}
|
||||
|
||||
var getOrCreateArchive = function (p) {
|
||||
var archive
|
||||
archive = cachedArchives[p]
|
||||
const getOrCreateArchive = function (p) {
|
||||
let archive = cachedArchives[p]
|
||||
if (archive != null) {
|
||||
return archive
|
||||
}
|
||||
@@ -25,31 +24,33 @@
|
||||
|
||||
// Clean cache on quit.
|
||||
process.on('exit', function () {
|
||||
var archive, p
|
||||
for (p in cachedArchives) {
|
||||
for (let p in cachedArchives) {
|
||||
if (!hasProp.call(cachedArchives, p)) continue
|
||||
archive = cachedArchives[p]
|
||||
archive.destroy()
|
||||
cachedArchives[p].destroy()
|
||||
}
|
||||
})
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
var splitPath = function (p) {
|
||||
var index
|
||||
|
||||
const splitPath = function (p) {
|
||||
// shortcut to disable asar.
|
||||
if (process.noAsar) {
|
||||
return [false]
|
||||
}
|
||||
|
||||
if (Buffer.isBuffer(p)) {
|
||||
p = p.toString()
|
||||
}
|
||||
|
||||
if (typeof p !== 'string') {
|
||||
return [false]
|
||||
}
|
||||
|
||||
if (p.substr(-5) === '.asar') {
|
||||
return [true, p, '']
|
||||
}
|
||||
|
||||
p = path.normalize(p)
|
||||
index = p.lastIndexOf('.asar' + path.sep)
|
||||
const index = p.lastIndexOf('.asar' + path.sep)
|
||||
if (index === -1) {
|
||||
return [false]
|
||||
}
|
||||
@@ -57,15 +58,15 @@
|
||||
}
|
||||
|
||||
// Convert asar archive's Stats object to fs's Stats object.
|
||||
var nextInode = 0
|
||||
let nextInode = 0
|
||||
|
||||
var uid = process.getuid != null ? process.getuid() : 0
|
||||
const uid = process.getuid != null ? process.getuid() : 0
|
||||
|
||||
var gid = process.getgid != null ? process.getgid() : 0
|
||||
const gid = process.getgid != null ? process.getgid() : 0
|
||||
|
||||
var fakeTime = new Date()
|
||||
const fakeTime = new Date()
|
||||
|
||||
var asarStatsToFsStats = function (stats) {
|
||||
const asarStatsToFsStats = function (stats) {
|
||||
return {
|
||||
dev: 1,
|
||||
ino: ++nextInode,
|
||||
@@ -104,97 +105,110 @@
|
||||
}
|
||||
|
||||
// Create a ENOENT error.
|
||||
var notFoundError = function (asarPath, filePath, callback) {
|
||||
var error
|
||||
error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`)
|
||||
const notFoundError = function (asarPath, filePath, callback) {
|
||||
const error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`)
|
||||
error.code = 'ENOENT'
|
||||
error.errno = -2
|
||||
if (typeof callback !== 'function') {
|
||||
throw error
|
||||
}
|
||||
return process.nextTick(function () {
|
||||
return callback(error)
|
||||
process.nextTick(function () {
|
||||
callback(error)
|
||||
})
|
||||
}
|
||||
|
||||
// Create a ENOTDIR error.
|
||||
var notDirError = function (callback) {
|
||||
var error
|
||||
error = new Error('ENOTDIR, not a directory')
|
||||
const notDirError = function (callback) {
|
||||
const error = new Error('ENOTDIR, not a directory')
|
||||
error.code = 'ENOTDIR'
|
||||
error.errno = -20
|
||||
if (typeof callback !== 'function') {
|
||||
throw error
|
||||
}
|
||||
return process.nextTick(function () {
|
||||
return callback(error)
|
||||
process.nextTick(function () {
|
||||
callback(error)
|
||||
})
|
||||
}
|
||||
|
||||
// Create a EACCES error.
|
||||
const accessError = function (asarPath, filePath, callback) {
|
||||
const error = new Error(`EACCES: permission denied, access '${filePath}'`)
|
||||
error.code = 'EACCES'
|
||||
error.errno = -13
|
||||
if (typeof callback !== 'function') {
|
||||
throw error
|
||||
}
|
||||
process.nextTick(function () {
|
||||
callback(error)
|
||||
})
|
||||
}
|
||||
|
||||
// Create invalid archive error.
|
||||
var invalidArchiveError = function (asarPath, callback) {
|
||||
var error
|
||||
error = new Error(`Invalid package ${asarPath}`)
|
||||
const invalidArchiveError = function (asarPath, callback) {
|
||||
const error = new Error(`Invalid package ${asarPath}`)
|
||||
if (typeof callback !== 'function') {
|
||||
throw error
|
||||
}
|
||||
return process.nextTick(function () {
|
||||
return callback(error)
|
||||
process.nextTick(function () {
|
||||
callback(error)
|
||||
})
|
||||
}
|
||||
|
||||
// Override APIs that rely on passing file path instead of content to C++.
|
||||
var overrideAPISync = function (module, name, arg) {
|
||||
var old
|
||||
const overrideAPISync = function (module, name, arg) {
|
||||
if (arg == null) {
|
||||
arg = 0
|
||||
}
|
||||
old = module[name]
|
||||
const old = module[name]
|
||||
module[name] = function () {
|
||||
var archive, newPath, p
|
||||
p = arguments[arg]
|
||||
const p = arguments[arg]
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return old.apply(this, arguments)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath)
|
||||
}
|
||||
newPath = archive.copyFileOut(filePath)
|
||||
|
||||
const newPath = archive.copyFileOut(filePath)
|
||||
if (!newPath) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
|
||||
arguments[arg] = newPath
|
||||
return old.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
var overrideAPI = function (module, name, arg) {
|
||||
var old
|
||||
const overrideAPI = function (module, name, arg) {
|
||||
if (arg == null) {
|
||||
arg = 0
|
||||
}
|
||||
old = module[name]
|
||||
const old = module[name]
|
||||
module[name] = function () {
|
||||
var archive, callback, newPath, p
|
||||
p = arguments[arg]
|
||||
const p = arguments[arg]
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return old.apply(this, arguments)
|
||||
}
|
||||
callback = arguments[arguments.length - 1]
|
||||
|
||||
const callback = arguments[arguments.length - 1]
|
||||
if (typeof callback !== 'function') {
|
||||
return overrideAPISync(module, name, arg)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
newPath = archive.copyFileOut(filePath)
|
||||
|
||||
const newPath = archive.copyFileOut(filePath)
|
||||
if (!newPath) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
|
||||
arguments[arg] = newPath
|
||||
return old.apply(this, arguments)
|
||||
}
|
||||
@@ -202,61 +216,58 @@
|
||||
|
||||
// Override fs APIs.
|
||||
exports.wrapFsWithAsar = function (fs) {
|
||||
var exists, existsSync, internalModuleReadFile, internalModuleStat, lstat, lstatSync, mkdir, mkdirSync, readFile, readFileSync, readdir, readdirSync, realpath, realpathSync, stat, statSync, statSyncNoException, logFDs, logASARAccess
|
||||
|
||||
logFDs = {}
|
||||
logASARAccess = function (asarPath, filePath, offset) {
|
||||
const logFDs = {}
|
||||
const logASARAccess = function (asarPath, filePath, offset) {
|
||||
if (!process.env.ELECTRON_LOG_ASAR_READS) {
|
||||
return
|
||||
}
|
||||
if (!logFDs[asarPath]) {
|
||||
var logFilename, logPath
|
||||
const path = require('path')
|
||||
logFilename = path.basename(asarPath, '.asar') + '-access-log.txt'
|
||||
logPath = path.join(require('os').tmpdir(), logFilename)
|
||||
const logFilename = path.basename(asarPath, '.asar') + '-access-log.txt'
|
||||
const logPath = path.join(require('os').tmpdir(), logFilename)
|
||||
logFDs[asarPath] = fs.openSync(logPath, 'a')
|
||||
console.log('Logging ' + asarPath + ' access to ' + logPath)
|
||||
}
|
||||
fs.writeSync(logFDs[asarPath], offset + ': ' + filePath + '\n')
|
||||
}
|
||||
|
||||
lstatSync = fs.lstatSync
|
||||
const {lstatSync} = fs
|
||||
fs.lstatSync = function (p) {
|
||||
var archive, stats
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return lstatSync(p)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath)
|
||||
}
|
||||
stats = archive.stat(filePath)
|
||||
const stats = archive.stat(filePath)
|
||||
if (!stats) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
return asarStatsToFsStats(stats)
|
||||
}
|
||||
lstat = fs.lstat
|
||||
|
||||
const {lstat} = fs
|
||||
fs.lstat = function (p, callback) {
|
||||
var archive, stats
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return lstat(p, callback)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
stats = getOrCreateArchive(asarPath).stat(filePath)
|
||||
const stats = getOrCreateArchive(asarPath).stat(filePath)
|
||||
if (!stats) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
return process.nextTick(function () {
|
||||
return callback(null, asarStatsToFsStats(stats))
|
||||
process.nextTick(function () {
|
||||
callback(null, asarStatsToFsStats(stats))
|
||||
})
|
||||
}
|
||||
statSync = fs.statSync
|
||||
|
||||
const {statSync} = fs
|
||||
fs.statSync = function (p) {
|
||||
const [isAsar] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
@@ -266,7 +277,8 @@
|
||||
// Do not distinguish links for now.
|
||||
return fs.lstatSync(p)
|
||||
}
|
||||
stat = fs.stat
|
||||
|
||||
const {stat} = fs
|
||||
fs.stat = function (p, callback) {
|
||||
const [isAsar] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
@@ -274,47 +286,47 @@
|
||||
}
|
||||
|
||||
// Do not distinguish links for now.
|
||||
return process.nextTick(function () {
|
||||
return fs.lstat(p, callback)
|
||||
process.nextTick(function () {
|
||||
fs.lstat(p, callback)
|
||||
})
|
||||
}
|
||||
statSyncNoException = fs.statSyncNoException
|
||||
|
||||
const {statSyncNoException} = fs
|
||||
fs.statSyncNoException = function (p) {
|
||||
var archive, stats
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return statSyncNoException(p)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return false
|
||||
}
|
||||
stats = archive.stat(filePath)
|
||||
const stats = archive.stat(filePath)
|
||||
if (!stats) {
|
||||
return false
|
||||
}
|
||||
return asarStatsToFsStats(stats)
|
||||
}
|
||||
realpathSync = fs.realpathSync
|
||||
|
||||
const {realpathSync} = fs
|
||||
fs.realpathSync = function (p) {
|
||||
var archive, real
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return realpathSync.apply(this, arguments)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath)
|
||||
}
|
||||
real = archive.realpath(filePath)
|
||||
const real = archive.realpath(filePath)
|
||||
if (real === false) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
return path.join(realpathSync(asarPath), real)
|
||||
}
|
||||
realpath = fs.realpath
|
||||
|
||||
const {realpath} = fs
|
||||
fs.realpath = function (p, cache, callback) {
|
||||
var archive, real
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return realpath.apply(this, arguments)
|
||||
@@ -323,11 +335,11 @@
|
||||
callback = cache
|
||||
cache = void 0
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
real = archive.realpath(filePath)
|
||||
const real = archive.realpath(filePath)
|
||||
if (real === false) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
@@ -338,37 +350,101 @@
|
||||
return callback(null, path.join(p, real))
|
||||
})
|
||||
}
|
||||
exists = fs.exists
|
||||
|
||||
const {exists} = fs
|
||||
fs.exists = function (p, callback) {
|
||||
var archive
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return exists(p, callback)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
return process.nextTick(function () {
|
||||
return callback(archive.stat(filePath) !== false)
|
||||
process.nextTick(function () {
|
||||
callback(archive.stat(filePath) !== false)
|
||||
})
|
||||
}
|
||||
existsSync = fs.existsSync
|
||||
|
||||
const {existsSync} = fs
|
||||
fs.existsSync = function (p) {
|
||||
var archive
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return existsSync(p)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return false
|
||||
}
|
||||
return archive.stat(filePath) !== false
|
||||
}
|
||||
readFile = fs.readFile
|
||||
|
||||
const {access} = fs
|
||||
fs.access = function (p, mode, callback) {
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return access.apply(this, arguments)
|
||||
}
|
||||
if (typeof mode === 'function') {
|
||||
callback = mode
|
||||
mode = fs.constants.F_OK
|
||||
}
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
const info = archive.getFileInfo(filePath)
|
||||
if (!info) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath)
|
||||
return fs.access(realPath, mode, callback)
|
||||
}
|
||||
const stats = getOrCreateArchive(asarPath).stat(filePath)
|
||||
if (!stats) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
if (mode & fs.constants.W_OK) {
|
||||
return accessError(asarPath, filePath, callback)
|
||||
}
|
||||
process.nextTick(function () {
|
||||
callback()
|
||||
})
|
||||
}
|
||||
|
||||
const {accessSync} = fs
|
||||
fs.accessSync = function (p, mode) {
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return accessSync.apply(this, arguments)
|
||||
}
|
||||
if (mode == null) {
|
||||
mode = fs.constants.F_OK
|
||||
}
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath)
|
||||
}
|
||||
const info = archive.getFileInfo(filePath)
|
||||
if (!info) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath)
|
||||
return fs.accessSync(realPath, mode)
|
||||
}
|
||||
const stats = getOrCreateArchive(asarPath).stat(filePath)
|
||||
if (!stats) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
if (mode & fs.constants.W_OK) {
|
||||
accessError(asarPath, filePath)
|
||||
}
|
||||
}
|
||||
|
||||
const {readFile} = fs
|
||||
fs.readFile = function (p, options, callback) {
|
||||
var archive, buffer, encoding, fd, info, realPath
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return readFile.apply(this, arguments)
|
||||
@@ -377,21 +453,21 @@
|
||||
callback = options
|
||||
options = void 0
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
info = archive.getFileInfo(filePath)
|
||||
const info = archive.getFileInfo(filePath)
|
||||
if (!info) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
if (info.size === 0) {
|
||||
return process.nextTick(function () {
|
||||
return callback(null, new Buffer(0))
|
||||
callback(null, new Buffer(0))
|
||||
})
|
||||
}
|
||||
if (info.unpacked) {
|
||||
realPath = archive.copyFileOut(filePath)
|
||||
const realPath = archive.copyFileOut(filePath)
|
||||
return fs.readFile(realPath, options, callback)
|
||||
}
|
||||
if (!options) {
|
||||
@@ -405,31 +481,29 @@
|
||||
} else if (!util.isObject(options)) {
|
||||
throw new TypeError('Bad arguments')
|
||||
}
|
||||
encoding = options.encoding
|
||||
buffer = new Buffer(info.size)
|
||||
fd = archive.getFd()
|
||||
const {encoding} = options
|
||||
const buffer = new Buffer(info.size)
|
||||
const fd = archive.getFd()
|
||||
if (!(fd >= 0)) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
logASARAccess(asarPath, filePath, info.offset)
|
||||
return fs.read(fd, buffer, 0, info.size, info.offset, function (error) {
|
||||
return callback(error, encoding ? buffer.toString(encoding) : buffer)
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, function (error) {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer)
|
||||
})
|
||||
}
|
||||
readFileSync = fs.readFileSync
|
||||
fs.readFileSync = function (p, opts) {
|
||||
// this allows v8 to optimize this function
|
||||
var archive, buffer, encoding, fd, info, options, realPath
|
||||
options = opts
|
||||
|
||||
const {readFileSync} = fs
|
||||
fs.readFileSync = function (p, options) {
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return readFileSync.apply(this, arguments)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath)
|
||||
}
|
||||
info = archive.getFileInfo(filePath)
|
||||
const info = archive.getFileInfo(filePath)
|
||||
if (!info) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
@@ -441,7 +515,7 @@
|
||||
}
|
||||
}
|
||||
if (info.unpacked) {
|
||||
realPath = archive.copyFileOut(filePath)
|
||||
const realPath = archive.copyFileOut(filePath)
|
||||
return fs.readFileSync(realPath, options)
|
||||
}
|
||||
if (!options) {
|
||||
@@ -455,9 +529,9 @@
|
||||
} else if (!util.isObject(options)) {
|
||||
throw new TypeError('Bad arguments')
|
||||
}
|
||||
encoding = options.encoding
|
||||
buffer = new Buffer(info.size)
|
||||
fd = archive.getFd()
|
||||
const {encoding} = options
|
||||
const buffer = new Buffer(info.size)
|
||||
const fd = archive.getFd()
|
||||
if (!(fd >= 0)) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
@@ -469,89 +543,89 @@
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
readdir = fs.readdir
|
||||
|
||||
const {readdir} = fs
|
||||
fs.readdir = function (p, callback) {
|
||||
var archive, files
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return readdir.apply(this, arguments)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback)
|
||||
}
|
||||
files = archive.readdir(filePath)
|
||||
const files = archive.readdir(filePath)
|
||||
if (!files) {
|
||||
return notFoundError(asarPath, filePath, callback)
|
||||
}
|
||||
return process.nextTick(function () {
|
||||
return callback(null, files)
|
||||
process.nextTick(function () {
|
||||
callback(null, files)
|
||||
})
|
||||
}
|
||||
readdirSync = fs.readdirSync
|
||||
|
||||
const {readdirSync} = fs
|
||||
fs.readdirSync = function (p) {
|
||||
var archive, files
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return readdirSync.apply(this, arguments)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath)
|
||||
}
|
||||
files = archive.readdir(filePath)
|
||||
const files = archive.readdir(filePath)
|
||||
if (!files) {
|
||||
notFoundError(asarPath, filePath)
|
||||
}
|
||||
return files
|
||||
}
|
||||
internalModuleReadFile = process.binding('fs').internalModuleReadFile
|
||||
|
||||
const {internalModuleReadFile} = process.binding('fs')
|
||||
process.binding('fs').internalModuleReadFile = function (p) {
|
||||
var archive, buffer, fd, info, realPath
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return internalModuleReadFile(p)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return void 0
|
||||
return
|
||||
}
|
||||
info = archive.getFileInfo(filePath)
|
||||
const info = archive.getFileInfo(filePath)
|
||||
if (!info) {
|
||||
return void 0
|
||||
return
|
||||
}
|
||||
if (info.size === 0) {
|
||||
return ''
|
||||
}
|
||||
if (info.unpacked) {
|
||||
realPath = archive.copyFileOut(filePath)
|
||||
const realPath = archive.copyFileOut(filePath)
|
||||
return fs.readFileSync(realPath, {
|
||||
encoding: 'utf8'
|
||||
})
|
||||
}
|
||||
buffer = new Buffer(info.size)
|
||||
fd = archive.getFd()
|
||||
const buffer = new Buffer(info.size)
|
||||
const fd = archive.getFd()
|
||||
if (!(fd >= 0)) {
|
||||
return void 0
|
||||
return
|
||||
}
|
||||
logASARAccess(asarPath, filePath, info.offset)
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset)
|
||||
return buffer.toString('utf8')
|
||||
}
|
||||
internalModuleStat = process.binding('fs').internalModuleStat
|
||||
|
||||
const {internalModuleStat} = process.binding('fs')
|
||||
process.binding('fs').internalModuleStat = function (p) {
|
||||
var archive, stats
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return internalModuleStat(p)
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath)
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
|
||||
// -ENOENT
|
||||
if (!archive) {
|
||||
return -34
|
||||
}
|
||||
stats = archive.stat(filePath)
|
||||
const stats = archive.stat(filePath)
|
||||
|
||||
// -ENOENT
|
||||
if (!stats) {
|
||||
@@ -569,7 +643,7 @@
|
||||
// This is to work around the recursive looping bug of mkdirp since it is
|
||||
// widely used.
|
||||
if (process.platform === 'win32') {
|
||||
mkdir = fs.mkdir
|
||||
const {mkdir} = fs
|
||||
fs.mkdir = function (p, mode, callback) {
|
||||
if (typeof mode === 'function') {
|
||||
callback = mode
|
||||
@@ -578,9 +652,10 @@
|
||||
if (isAsar && filePath.length) {
|
||||
return notDirError(callback)
|
||||
}
|
||||
return mkdir(p, mode, callback)
|
||||
mkdir(p, mode, callback)
|
||||
}
|
||||
mkdirSync = fs.mkdirSync
|
||||
|
||||
const {mkdirSync} = fs
|
||||
fs.mkdirSync = function (p, mode) {
|
||||
const [isAsar, , filePath] = splitPath(p)
|
||||
if (isAsar && filePath.length) {
|
||||
@@ -595,12 +670,12 @@
|
||||
// called by `childProcess.{exec,execSync}`, causing
|
||||
// Electron to consider the full command as a single path
|
||||
// to an archive.
|
||||
[ 'exec', 'execSync' ].forEach(function (functionName) {
|
||||
var old = childProcess[functionName]
|
||||
['exec', 'execSync'].forEach(function (functionName) {
|
||||
const old = childProcess[functionName]
|
||||
childProcess[functionName] = function () {
|
||||
var processNoAsarOriginalValue = process.noAsar
|
||||
const processNoAsarOriginalValue = process.noAsar
|
||||
process.noAsar = true
|
||||
var result = old.apply(this, arguments)
|
||||
const result = old.apply(this, arguments)
|
||||
process.noAsar = processNoAsarOriginalValue
|
||||
return result
|
||||
}
|
||||
@@ -611,6 +686,6 @@
|
||||
overrideAPISync(process, 'dlopen', 1)
|
||||
overrideAPISync(require('module')._extensions, '.node', 1)
|
||||
overrideAPISync(fs, 'openSync')
|
||||
return overrideAPISync(childProcess, 'execFileSync')
|
||||
overrideAPISync(childProcess, 'execFileSync')
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -30,7 +30,7 @@ const wrapArgs = function (args, visited) {
|
||||
}
|
||||
visited.delete(value)
|
||||
return meta
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
} else if (ArrayBuffer.isView(value)) {
|
||||
return {
|
||||
type: 'buffer',
|
||||
value: Array.prototype.slice.call(value, 0)
|
||||
@@ -163,7 +163,7 @@ const metaToValue = function (meta) {
|
||||
}
|
||||
return results
|
||||
case 'buffer':
|
||||
return new Buffer(meta.value)
|
||||
return Buffer.from(meta.value)
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"devDependencies": {
|
||||
"asar": "^0.11.0",
|
||||
"request": "*",
|
||||
"standard": "^7.1.2"
|
||||
"standard": "^7.1.2",
|
||||
"standard-markdown": "^1.1.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"runas": "^3.0.0"
|
||||
@@ -23,9 +24,10 @@
|
||||
"scripts": {
|
||||
"bootstrap": "python ./script/bootstrap.py",
|
||||
"build": "python ./script/build.py -c D",
|
||||
"lint": "npm run lint-js && npm run lint-cpp",
|
||||
"lint": "npm run lint-js && npm run lint-cpp && npm run lint-docs",
|
||||
"lint-js": "standard && cd spec && standard",
|
||||
"lint-cpp": "python ./script/cpplint.py",
|
||||
"lint-docs": "standard-markdown docs",
|
||||
"preinstall": "node -e 'process.exit(0)'",
|
||||
"repl": "python ./script/start.py --interactive",
|
||||
"start": "python ./script/start.py",
|
||||
|
||||
@@ -65,7 +65,7 @@ def main():
|
||||
|
||||
create_chrome_version_h()
|
||||
touch_config_gypi()
|
||||
run_update(defines, args.disable_clang, args.clang_dir)
|
||||
run_update(defines, args.msvs)
|
||||
update_electron_modules('spec', args.target_arch)
|
||||
|
||||
|
||||
@@ -86,6 +86,8 @@ def parse_args():
|
||||
action='store_true',
|
||||
help='Run non-interactively by assuming "yes" to all ' \
|
||||
'prompts.')
|
||||
parser.add_argument('--msvs', action='store_true',
|
||||
help='Generate Visual Studio project')
|
||||
parser.add_argument('--target_arch', default=get_target_arch(),
|
||||
help='Manually specify the arch to build for')
|
||||
parser.add_argument('--clang_dir', default='', help='Path to clang binaries')
|
||||
@@ -249,14 +251,14 @@ def touch_config_gypi():
|
||||
f.write(content)
|
||||
|
||||
|
||||
def run_update(defines, disable_clang, clang_dir):
|
||||
env = os.environ.copy()
|
||||
if not disable_clang and clang_dir == '':
|
||||
# Build with prebuilt clang.
|
||||
set_clang_env(env)
|
||||
def run_update(defines, msvs):
|
||||
args = [sys.executable, os.path.join(SOURCE_ROOT, 'script', 'update.py')]
|
||||
if defines:
|
||||
args += ['--defines', defines]
|
||||
if msvs:
|
||||
args += ['--msvs']
|
||||
|
||||
update = os.path.join(SOURCE_ROOT, 'script', 'update.py')
|
||||
execute_stdout([sys.executable, update, '--defines', defines], env)
|
||||
execute_stdout(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -35,6 +35,13 @@ def main():
|
||||
if os.environ.has_key('JANKY_SHA1'):
|
||||
setup_nodenv()
|
||||
|
||||
# Ignore the CXX and CC env in CI.
|
||||
try:
|
||||
del os.environ['CC']
|
||||
del os.environ['CXX']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
target_arch = 'x64'
|
||||
if os.environ.has_key('TARGET_ARCH'):
|
||||
target_arch = os.environ['TARGET_ARCH']
|
||||
|
||||
@@ -28,6 +28,8 @@ def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Update build configurations')
|
||||
parser.add_argument('--defines', default='',
|
||||
help='The definetions passed to gyp')
|
||||
parser.add_argument('--msvs', action='store_true',
|
||||
help='Generate Visual Studio project')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -86,7 +88,11 @@ def run_gyp(target_arch, component):
|
||||
if define:
|
||||
defines += ['-D' + define]
|
||||
|
||||
return subprocess.call([python, gyp, '-f', 'ninja', '--depth', '.',
|
||||
generator = 'ninja'
|
||||
if args.msvs:
|
||||
generator = 'msvs-ninja'
|
||||
|
||||
return subprocess.call([python, gyp, '-f', generator, '--depth', '.',
|
||||
'electron.gyp', '-Icommon.gypi'] + defines, env=env)
|
||||
|
||||
|
||||
|
||||
@@ -88,5 +88,19 @@ describe('crash-reporter module', function () {
|
||||
})
|
||||
}, /companyName is a required option to crashReporter\.start/)
|
||||
})
|
||||
|
||||
it('can be called multiple times', function () {
|
||||
assert.doesNotThrow(function () {
|
||||
crashReporter.start({
|
||||
companyName: 'Umbrella Corporation',
|
||||
submitURL: 'http://127.0.0.1/crashes'
|
||||
})
|
||||
|
||||
crashReporter.start({
|
||||
companyName: 'Umbrella Corporation 2',
|
||||
submitURL: 'http://127.0.0.1/more-crashes'
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -142,20 +142,25 @@ describe('ipc module', function () {
|
||||
})
|
||||
|
||||
describe('remote value in browser', function () {
|
||||
var print = path.join(fixtures, 'module', 'print_name.js')
|
||||
const print = path.join(fixtures, 'module', 'print_name.js')
|
||||
const printName = remote.require(print)
|
||||
|
||||
it('keeps its constructor name for objects', function () {
|
||||
var buf = new Buffer('test')
|
||||
var printName = remote.require(print)
|
||||
const buf = new Buffer('test')
|
||||
assert.equal(printName.print(buf), 'Buffer')
|
||||
})
|
||||
|
||||
it('supports instanceof Date', function () {
|
||||
var now = new Date()
|
||||
var printName = remote.require(print)
|
||||
const now = new Date()
|
||||
assert.equal(printName.print(now), 'Date')
|
||||
assert.deepEqual(printName.echo(now), now)
|
||||
})
|
||||
|
||||
it('supports TypedArray', function () {
|
||||
const values = [1, 2, 3, 4]
|
||||
const typedArray = printName.typedArray(values)
|
||||
assert.deepEqual(values, typedArray)
|
||||
})
|
||||
})
|
||||
|
||||
describe('remote promise', function () {
|
||||
|
||||
@@ -399,7 +399,7 @@ describe('menu module', function () {
|
||||
describe('MenuItem role', function () {
|
||||
it('includes a default label and accelerator', function () {
|
||||
var item = new MenuItem({role: 'close'})
|
||||
assert.equal(item.label, 'Close')
|
||||
assert.equal(item.label, process.platform === 'darwin' ? 'Close Window' : 'Close')
|
||||
assert.equal(item.accelerator, undefined)
|
||||
assert.equal(item.getDefaultRoleAccelerator(), 'CommandOrControl+W')
|
||||
|
||||
|
||||
@@ -342,4 +342,31 @@ describe('session module', function () {
|
||||
w.loadURL(`${protocolName}://fake-host`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ses.setProxy(options, callback)', function () {
|
||||
it('allows configuring proxy settings', function (done) {
|
||||
const config = {
|
||||
proxyRules: 'http=myproxy:80'
|
||||
}
|
||||
session.defaultSession.setProxy(config, function () {
|
||||
session.defaultSession.resolveProxy('http://localhost', function (proxy) {
|
||||
assert.equal(proxy, 'PROXY myproxy:80')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('allows bypassing proxy settings', function (done) {
|
||||
const config = {
|
||||
proxyRules: 'http=myproxy:80',
|
||||
proxyBypassRules: '<local>'
|
||||
}
|
||||
session.defaultSession.setProxy(config, function () {
|
||||
session.defaultSession.resolveProxy('http://localhost', function (proxy) {
|
||||
assert.equal(proxy, 'DIRECT')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,6 +13,11 @@ describe('asar package', function () {
|
||||
var fixtures = path.join(__dirname, 'fixtures')
|
||||
|
||||
describe('node api', function () {
|
||||
it('supports paths specified as a Buffer', function () {
|
||||
var file = new Buffer(path.join(fixtures, 'asar', 'a.asar', 'file1'))
|
||||
assert.equal(fs.existsSync(file), true)
|
||||
})
|
||||
|
||||
describe('fs.readFileSync', function () {
|
||||
it('does not leak fd', function () {
|
||||
var readCalls = 1
|
||||
@@ -534,6 +539,70 @@ describe('asar package', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('fs.access', function () {
|
||||
it('accesses a normal file', function (done) {
|
||||
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
|
||||
fs.access(p, function (err) {
|
||||
assert(err == null)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when called with write mode', function (done) {
|
||||
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
|
||||
fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) {
|
||||
assert.equal(err.code, 'EACCES')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when called on non-existent file', function (done) {
|
||||
var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
|
||||
fs.access(p, function (err) {
|
||||
assert.equal(err.code, 'ENOENT')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('allows write mode for unpacked files', function (done) {
|
||||
var p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt')
|
||||
fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) {
|
||||
assert(err == null)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('fs.accessSync', function () {
|
||||
it('accesses a normal file', function () {
|
||||
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
|
||||
assert.doesNotThrow(function () {
|
||||
fs.accessSync(p)
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when called with write mode', function () {
|
||||
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
|
||||
assert.throws(function () {
|
||||
fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK)
|
||||
}, /EACCES/)
|
||||
})
|
||||
|
||||
it('throws an error when called on non-existent file', function () {
|
||||
var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
|
||||
assert.throws(function () {
|
||||
fs.accessSync(p)
|
||||
}, /ENOENT/)
|
||||
})
|
||||
|
||||
it('allows write mode for unpacked files', function () {
|
||||
var p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt')
|
||||
assert.doesNotThrow(function () {
|
||||
fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('child_process.fork', function () {
|
||||
it('opens a normal js file', function (done) {
|
||||
var child = ChildProcess.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js'))
|
||||
|
||||
8
spec/fixtures/module/print_name.js
vendored
8
spec/fixtures/module/print_name.js
vendored
@@ -5,3 +5,11 @@ exports.print = function (obj) {
|
||||
exports.echo = function (obj) {
|
||||
return obj
|
||||
}
|
||||
|
||||
exports.typedArray = function (name) {
|
||||
const int16 = new Int16Array(name.length)
|
||||
for (let i = 0; i < name.length; ++i) {
|
||||
int16[i] = name[i]
|
||||
}
|
||||
return int16
|
||||
}
|
||||
|
||||
2
vendor/brightray
vendored
2
vendor/brightray
vendored
Submodule vendor/brightray updated: 278143846e...689e41c331
Reference in New Issue
Block a user