mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
24 Commits
roller/chr
...
v1.7.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01ca2252cd | ||
|
|
53db3862c0 | ||
|
|
6099ab222e | ||
|
|
830cc7ecd1 | ||
|
|
eef8ff09e2 | ||
|
|
b15392e1c1 | ||
|
|
fd1bb3f95d | ||
|
|
53eb4d68c5 | ||
|
|
331a1759d2 | ||
|
|
b69d76258a | ||
|
|
680bf0076b | ||
|
|
d42d856b9a | ||
|
|
e6f6862ae8 | ||
|
|
44b4cc374b | ||
|
|
9824c88d2d | ||
|
|
96bc46c255 | ||
|
|
873a8902af | ||
|
|
7dac300305 | ||
|
|
2a536d2aa2 | ||
|
|
ccd03c6675 | ||
|
|
5515092944 | ||
|
|
fb7661d2d2 | ||
|
|
75b31f0bb6 | ||
|
|
cfee5ba8c8 |
41
.circleci/config.yml
Normal file
41
.circleci/config.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
electron-linux-arm:
|
||||
docker:
|
||||
- image: electronbuilds/electron:0.0.3
|
||||
environment:
|
||||
TARGET_ARCH: arm
|
||||
steps:
|
||||
- checkout
|
||||
- run: script/cibuild
|
||||
|
||||
electron-linux-ia32:
|
||||
docker:
|
||||
- image: electronbuilds/electron:0.0.3
|
||||
environment:
|
||||
TARGET_ARCH: ia32
|
||||
steps:
|
||||
- checkout
|
||||
- run: script/cibuild
|
||||
|
||||
electron-linux-x64:
|
||||
docker:
|
||||
- image: electronbuilds/electron:0.0.3
|
||||
environment:
|
||||
TARGET_ARCH: x64
|
||||
steps:
|
||||
- checkout
|
||||
- run: script/cibuild
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-arm:
|
||||
jobs:
|
||||
- electron-linux-arm
|
||||
build-ia32:
|
||||
jobs:
|
||||
- electron-linux-ia32
|
||||
build-x64:
|
||||
jobs:
|
||||
- electron-linux-x64
|
||||
@@ -95,6 +95,25 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
// Chromium has its own TLS subsystem which supports automatic destruction
|
||||
// of thread-local data, and also depends on memory allocation routines
|
||||
// provided by the CRT. The problem is that the auto-destruction mechanism
|
||||
// uses a hidden feature of the OS loader which calls a callback on thread
|
||||
// exit, but only after all loaded DLLs have been detached. Since the CRT is
|
||||
// also a DLL, it happens that by the time Chromium's `OnThreadExit` function
|
||||
// is called, the heap functions, though still in memory, no longer perform
|
||||
// their duties, and when Chromium calls `free` on its buffer, it triggers
|
||||
// an access violation error.
|
||||
// We work around this problem by invoking Chromium's `OnThreadExit` in time
|
||||
// from within the CRT's atexit facility, ensuring the heap functions are
|
||||
// still active. The second invocation from the OS loader will be a no-op.
|
||||
extern void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved);
|
||||
atexit([]() {
|
||||
OnThreadExit(nullptr, DLL_THREAD_DETACH, nullptr);
|
||||
});
|
||||
#endif
|
||||
|
||||
if (run_as_node) {
|
||||
// Now that argv conversion is done, we can finally start.
|
||||
base::AtExitManager atexit_manager;
|
||||
|
||||
@@ -575,6 +575,12 @@ void App::OnFinishLaunching(const base::DictionaryValue& launch_info) {
|
||||
Emit("ready", launch_info);
|
||||
}
|
||||
|
||||
void App::OnPreMainMessageLoopRun() {
|
||||
if (process_singleton_) {
|
||||
process_singleton_->OnBrowserReady();
|
||||
}
|
||||
}
|
||||
|
||||
void App::OnAccessibilitySupportChanged() {
|
||||
Emit("accessibility-support-changed", IsAccessibilitySupportEnabled());
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ class App : public AtomBrowserClient::Delegate,
|
||||
base::FilePath GetAppPath() const;
|
||||
void RenderProcessReady(content::RenderProcessHost* host);
|
||||
void RenderProcessDisconnected(base::ProcessId host_pid);
|
||||
void PreMainMessageLoopRun();
|
||||
|
||||
protected:
|
||||
explicit App(v8::Isolate* isolate);
|
||||
@@ -112,6 +113,7 @@ class App : public AtomBrowserClient::Delegate,
|
||||
void OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) override;
|
||||
void OnAccessibilitySupportChanged() override;
|
||||
void OnPreMainMessageLoopRun() override;
|
||||
#if defined(OS_MACOSX)
|
||||
void OnContinueUserActivity(
|
||||
bool* prevent_default,
|
||||
|
||||
@@ -67,6 +67,7 @@ Notification::Notification(v8::Isolate* isolate,
|
||||
opts.Get("replyPlaceholder", &reply_placeholder_);
|
||||
opts.Get("hasReply", &has_reply_);
|
||||
opts.Get("actions", &actions_);
|
||||
opts.Get("sound", &sound_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +114,10 @@ std::vector<brightray::NotificationAction> Notification::GetActions() const {
|
||||
return actions_;
|
||||
}
|
||||
|
||||
base::string16 Notification::GetSound() const {
|
||||
return sound_;
|
||||
}
|
||||
|
||||
// Setters
|
||||
void Notification::SetTitle(const base::string16& new_title) {
|
||||
title_ = new_title;
|
||||
@@ -143,6 +148,10 @@ void Notification::SetActions(
|
||||
actions_ = actions;
|
||||
}
|
||||
|
||||
void Notification::SetSound(const base::string16& new_sound) {
|
||||
sound_ = new_sound;
|
||||
}
|
||||
|
||||
void Notification::NotificationAction(int index) {
|
||||
Emit("action", index);
|
||||
}
|
||||
@@ -181,6 +190,7 @@ void Notification::Show() {
|
||||
options.has_reply = has_reply_;
|
||||
options.reply_placeholder = reply_placeholder_;
|
||||
options.actions = actions_;
|
||||
options.sound = sound_;
|
||||
notification_->Show(options);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +217,9 @@ void Notification::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetProperty("hasReply", &Notification::GetHasReply,
|
||||
&Notification::SetHasReply)
|
||||
.SetProperty("actions", &Notification::GetActions,
|
||||
&Notification::SetActions);
|
||||
&Notification::SetActions)
|
||||
.SetProperty("sound", &Notification::GetSound,
|
||||
&Notification::SetSound);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -54,6 +54,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||
base::string16 GetReplyPlaceholder() const;
|
||||
bool GetHasReply() const;
|
||||
std::vector<brightray::NotificationAction> GetActions() const;
|
||||
base::string16 GetSound() const;
|
||||
|
||||
// Prop Setters
|
||||
void SetTitle(const base::string16& new_title);
|
||||
@@ -63,6 +64,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||
void SetReplyPlaceholder(const base::string16& new_reply_placeholder);
|
||||
void SetHasReply(bool new_has_reply);
|
||||
void SetActions(const std::vector<brightray::NotificationAction>& actions);
|
||||
void SetSound(const base::string16& sound);
|
||||
|
||||
private:
|
||||
base::string16 title_;
|
||||
@@ -75,6 +77,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||
base::string16 reply_placeholder_;
|
||||
bool has_reply_ = false;
|
||||
std::vector<brightray::NotificationAction> actions_;
|
||||
base::string16 sound_;
|
||||
|
||||
brightray::NotificationPresenter* presenter_;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
|
||||
#include "atom/browser/api/atom_api_app.h"
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "atom/browser/atom_access_token_store.h"
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
@@ -183,6 +184,8 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||
std::unique_ptr<base::DictionaryValue> empty_info(new base::DictionaryValue);
|
||||
Browser::Get()->DidFinishLaunching(*empty_info);
|
||||
#endif
|
||||
|
||||
Browser::Get()->PreMainMessageLoopRun();
|
||||
}
|
||||
|
||||
bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
|
||||
|
||||
@@ -171,6 +171,12 @@ void Browser::RequestLogin(
|
||||
observer.OnLogin(login_handler, *(request_details.get()));
|
||||
}
|
||||
|
||||
void Browser::PreMainMessageLoopRun() {
|
||||
for (BrowserObserver& observer : observers_) {
|
||||
observer.OnPreMainMessageLoopRun();
|
||||
}
|
||||
}
|
||||
|
||||
void Browser::NotifyAndShutdown() {
|
||||
if (is_shutdown_)
|
||||
return;
|
||||
|
||||
@@ -202,6 +202,8 @@ class Browser : public WindowListObserver {
|
||||
void RequestLogin(LoginHandler* login_handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details);
|
||||
|
||||
void PreMainMessageLoopRun();
|
||||
|
||||
void AddObserver(BrowserObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ class BrowserObserver {
|
||||
// The browser's accessibility suppport has changed.
|
||||
virtual void OnAccessibilitySupportChanged() {}
|
||||
|
||||
// The app message loop is ready
|
||||
virtual void OnPreMainMessageLoopRun() {}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// The browser wants to resume a user activity via handoff. (macOS only)
|
||||
virtual void OnContinueUserActivity(
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/native_browser_view.h"
|
||||
|
||||
#include "atom/browser/api/atom_api_web_contents.h"
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#ifndef ATOM_BROWSER_NATIVE_BROWSER_VIEW_H_
|
||||
#define ATOM_BROWSER_NATIVE_BROWSER_VIEW_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/draggable_region.h"
|
||||
#include "base/macros.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
|
||||
@@ -38,6 +41,10 @@ class NativeBrowserView {
|
||||
virtual void SetBounds(const gfx::Rect& bounds) = 0;
|
||||
virtual void SetBackgroundColor(SkColor color) = 0;
|
||||
|
||||
// Called when the window needs to update its draggable region.
|
||||
virtual void UpdateDraggableRegions(
|
||||
const std::vector<gfx::Rect>& system_drag_exclude_areas) {}
|
||||
|
||||
protected:
|
||||
explicit NativeBrowserView(
|
||||
brightray::InspectableWebContentsView* web_contents_view);
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
#define ATOM_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/native_browser_view.h"
|
||||
#include "atom/common/draggable_region.h"
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -20,6 +23,8 @@ class NativeBrowserViewMac : public NativeBrowserView {
|
||||
void SetAutoResizeFlags(uint8_t flags) override;
|
||||
void SetBounds(const gfx::Rect& bounds) override;
|
||||
void SetBackgroundColor(SkColor color) override;
|
||||
void UpdateDraggableRegions(
|
||||
const std::vector<gfx::Rect>& system_drag_exclude_areas) override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeBrowserViewMac);
|
||||
|
||||
@@ -12,6 +12,101 @@
|
||||
const NSAutoresizingMaskOptions kDefaultAutoResizingMask =
|
||||
NSViewMaxXMargin | NSViewMinYMargin;
|
||||
|
||||
@interface DragRegionView : NSView
|
||||
|
||||
@property (assign) NSPoint initialLocation;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSWindow ()
|
||||
- (void)performWindowDragWithEvent:(NSEvent *)event;
|
||||
@end
|
||||
|
||||
@implementation DragRegionView
|
||||
|
||||
- (BOOL)mouseDownCanMoveWindow
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSView *)hitTest:(NSPoint)aPoint
|
||||
{
|
||||
// Pass-through events that don't hit one of the exclusion zones
|
||||
for (NSView *exlusion_zones in [self subviews]) {
|
||||
if ([exlusion_zones hitTest:aPoint])
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)event
|
||||
{
|
||||
if ([self.window respondsToSelector:@selector(performWindowDragWithEvent)]) {
|
||||
[self.window performWindowDragWithEvent:event];
|
||||
return;
|
||||
}
|
||||
|
||||
self.initialLocation = [event locationInWindow];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(NSEvent *)theEvent
|
||||
{
|
||||
if ([self.window respondsToSelector:@selector(performWindowDragWithEvent)]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSPoint currentLocation = [NSEvent mouseLocation];
|
||||
NSPoint newOrigin;
|
||||
|
||||
NSRect screenFrame = [[NSScreen mainScreen] frame];
|
||||
NSRect windowFrame = [self.window frame];
|
||||
|
||||
newOrigin.x = currentLocation.x - self.initialLocation.x;
|
||||
newOrigin.y = currentLocation.y - self.initialLocation.y;
|
||||
|
||||
// Don't let window get dragged up under the menu bar
|
||||
if ((newOrigin.y + windowFrame.size.height) > (screenFrame.origin.y + screenFrame.size.height)) {
|
||||
newOrigin.y = screenFrame.origin.y + (screenFrame.size.height - windowFrame.size.height);
|
||||
}
|
||||
|
||||
// Move the window to the new location
|
||||
[self.window setFrameOrigin:newOrigin];
|
||||
}
|
||||
|
||||
// Debugging tips:
|
||||
// Uncomment the following four lines to color DragRegionView bright red
|
||||
// #ifdef DEBUG_DRAG_REGIONS
|
||||
// - (void)drawRect:(NSRect)aRect
|
||||
// {
|
||||
// [[NSColor redColor] set];
|
||||
// NSRectFill([self bounds]);
|
||||
// }
|
||||
// #endif
|
||||
|
||||
@end
|
||||
|
||||
@interface ExcludeDragRegionView : NSView
|
||||
@end
|
||||
|
||||
@implementation ExcludeDragRegionView
|
||||
|
||||
- (BOOL)mouseDownCanMoveWindow {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Debugging tips:
|
||||
// Uncomment the following four lines to color ExcludeDragRegionView bright red
|
||||
// #ifdef DEBUG_DRAG_REGIONS
|
||||
// - (void)drawRect:(NSRect)aRect
|
||||
// {
|
||||
// [[NSColor greenColor] set];
|
||||
// NSRectFill([self bounds]);
|
||||
// }
|
||||
// #endif
|
||||
|
||||
@end
|
||||
|
||||
namespace atom {
|
||||
|
||||
NativeBrowserViewMac::NativeBrowserViewMac(
|
||||
@@ -51,6 +146,59 @@ void NativeBrowserViewMac::SetBackgroundColor(SkColor color) {
|
||||
view.layer.backgroundColor = skia::CGColorCreateFromSkColor(color);
|
||||
}
|
||||
|
||||
void NativeBrowserViewMac::UpdateDraggableRegions(
|
||||
const std::vector<gfx::Rect>& system_drag_exclude_areas) {
|
||||
NSView* webView = GetInspectableWebContentsView()->GetNativeView();
|
||||
|
||||
NSInteger superViewHeight = NSHeight([webView.superview bounds]);
|
||||
NSInteger webViewHeight = NSHeight([webView bounds]);
|
||||
NSInteger webViewWidth = NSWidth([webView bounds]);
|
||||
NSInteger webViewX = NSMinX([webView frame]);
|
||||
NSInteger webViewY = 0;
|
||||
|
||||
// Apple's NSViews have their coordinate system originate at the bottom left,
|
||||
// meaning that we need to be a bit smarter when it comes to calculating our
|
||||
// current top offset
|
||||
if (webViewHeight > superViewHeight) {
|
||||
webViewY = std::abs(webViewHeight - superViewHeight - (std::abs(NSMinY([webView frame]))));
|
||||
} else {
|
||||
webViewY = superViewHeight - NSMaxY([webView frame]);
|
||||
}
|
||||
|
||||
// Remove all DraggableRegionViews that are added last time.
|
||||
// Note that [webView subviews] returns the view's mutable internal array and
|
||||
// it should be copied to avoid mutating the original array while enumerating
|
||||
// it.
|
||||
base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
|
||||
for (NSView* subview in subviews.get())
|
||||
if ([subview isKindOfClass:[DragRegionView class]])
|
||||
[subview removeFromSuperview];
|
||||
|
||||
// Create one giant NSView that is draggable.
|
||||
base::scoped_nsobject<NSView> dragRegion(
|
||||
[[DragRegionView alloc] initWithFrame:NSZeroRect]);
|
||||
[dragRegion setFrame:NSMakeRect(0,
|
||||
0,
|
||||
webViewWidth,
|
||||
webViewHeight)];
|
||||
|
||||
// Then, on top of that, add "exclusion zones"
|
||||
for (auto iter = system_drag_exclude_areas.begin();
|
||||
iter != system_drag_exclude_areas.end();
|
||||
++iter) {
|
||||
base::scoped_nsobject<NSView> controlRegion(
|
||||
[[ExcludeDragRegionView alloc] initWithFrame:NSZeroRect]);
|
||||
[controlRegion setFrame:NSMakeRect(iter->x() - webViewX,
|
||||
webViewHeight - iter->bottom() + webViewY,
|
||||
iter->width(),
|
||||
iter->height())];
|
||||
[dragRegion addSubview:controlRegion];
|
||||
}
|
||||
|
||||
// Add the DragRegion to the WebView
|
||||
[webView addSubview:dragRegion];
|
||||
}
|
||||
|
||||
// static
|
||||
NativeBrowserView* NativeBrowserView::Create(
|
||||
brightray::InspectableWebContentsView* web_contents_view) {
|
||||
|
||||
@@ -154,7 +154,7 @@ class NativeWindowMac : public NativeWindow,
|
||||
void UninstallView();
|
||||
|
||||
// Install the drag view, which will cover the whole window and decides
|
||||
// whehter we can drag.
|
||||
// whether we can drag.
|
||||
void UpdateDraggableRegionViews(const std::vector<DraggableRegion>& regions);
|
||||
|
||||
void RegisterInputEventObserver(content::RenderViewHost* host);
|
||||
|
||||
@@ -1767,6 +1767,10 @@ void NativeWindowMac::UpdateDraggableRegionViews(
|
||||
std::vector<gfx::Rect> system_drag_exclude_areas =
|
||||
CalculateNonDraggableRegions(regions, webViewWidth, webViewHeight);
|
||||
|
||||
if (browser_view_) {
|
||||
browser_view_->UpdateDraggableRegions(system_drag_exclude_areas);
|
||||
}
|
||||
|
||||
// Create and add a ControlRegionView for each region that needs to be
|
||||
// excluded from the dragging.
|
||||
for (std::vector<gfx::Rect>::const_iterator iter =
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>electron.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.7.6</string>
|
||||
<string>1.7.8</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.6</string>
|
||||
<string>1.7.8</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,7,6,0
|
||||
PRODUCTVERSION 1,7,6,0
|
||||
FILEVERSION 1,7,8,0
|
||||
PRODUCTVERSION 1,7,8,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -74,12 +74,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "1.7.6"
|
||||
VALUE "FileVersion", "1.7.8"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "1.7.6"
|
||||
VALUE "ProductVersion", "1.7.8"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -40,7 +40,6 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
if ([file_type_set count])
|
||||
file_types = [file_type_set allObjects];
|
||||
|
||||
[dialog setExtensionHidden:NO];
|
||||
[dialog setAllowedFileTypes:file_types];
|
||||
}
|
||||
|
||||
@@ -84,11 +83,14 @@ void SetupDialog(NSSavePanel* dialog,
|
||||
SetAllowedFileTypes(dialog, settings.filters);
|
||||
}
|
||||
|
||||
// Make sure the extension is always visible. Without this, the extension in
|
||||
// the default filename will not be used in the saved file.
|
||||
[dialog setExtensionHidden:NO];
|
||||
|
||||
if (default_dir)
|
||||
[dialog setDirectoryURL:[NSURL fileURLWithPath:default_dir]];
|
||||
if (default_filename)
|
||||
[dialog setNameFieldStringValue:default_filename];
|
||||
|
||||
}
|
||||
|
||||
void SetupDialogForProperties(NSOpenPanel* dialog, int properties) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#define ATOM_MAJOR_VERSION 1
|
||||
#define ATOM_MINOR_VERSION 7
|
||||
#define ATOM_PATCH_VERSION 6
|
||||
#define ATOM_PATCH_VERSION 8
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ void CocoaNotification::Show(const NotificationOptions& options) {
|
||||
|
||||
if (options.silent) {
|
||||
[notification_ setSoundName:nil];
|
||||
} else if (options.sound != nil) {
|
||||
[notification_ setSoundName:base::SysUTF16ToNSString(options.sound)];
|
||||
} else {
|
||||
[notification_ setSoundName:NSUserNotificationDefaultSoundName];
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ struct NotificationOptions {
|
||||
bool silent;
|
||||
bool has_reply;
|
||||
base::string16 reply_placeholder;
|
||||
base::string16 sound;
|
||||
std::vector<NotificationAction> actions;
|
||||
};
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ class ProcessSingleton : public base::NonThreadSafe {
|
||||
// TODO(brettw): Make the implementation of this method non-platform-specific
|
||||
// by making Linux re-use the Windows implementation.
|
||||
NotifyResult NotifyOtherProcessOrCreate();
|
||||
void StartListeningOnSocket();
|
||||
void OnBrowserReady();
|
||||
|
||||
// Sets ourself up as the singleton instance. Returns true on success. If
|
||||
// false is returned, we are not the singleton instance and the caller must
|
||||
@@ -173,6 +175,8 @@ class ProcessSingleton : public base::NonThreadSafe {
|
||||
// because it posts messages between threads.
|
||||
class LinuxWatcher;
|
||||
scoped_refptr<LinuxWatcher> watcher_;
|
||||
int sock_;
|
||||
bool listen_on_ready_ = false;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessSingleton);
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/common/atom_command_line.h"
|
||||
|
||||
#include "base/base_paths.h"
|
||||
@@ -719,8 +720,7 @@ ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
current_pid_(base::GetCurrentProcId()) {
|
||||
// The user_data_dir may have not been created yet.
|
||||
base::CreateDirectoryAndGetError(user_data_dir, nullptr);
|
||||
|
||||
@@ -881,6 +881,23 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
|
||||
base::TimeDelta::FromSeconds(kTimeoutInSeconds));
|
||||
}
|
||||
|
||||
void ProcessSingleton::StartListeningOnSocket() {
|
||||
watcher_ = new LinuxWatcher(this);
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&ProcessSingleton::LinuxWatcher::StartListening,
|
||||
watcher_,
|
||||
sock_));
|
||||
}
|
||||
|
||||
void ProcessSingleton::OnBrowserReady() {
|
||||
if (listen_on_ready_) {
|
||||
StartListeningOnSocket();
|
||||
listen_on_ready_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessSingleton::NotifyResult
|
||||
ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
|
||||
const base::CommandLine& command_line,
|
||||
@@ -1031,13 +1048,13 @@ bool ProcessSingleton::Create() {
|
||||
if (listen(sock, 5) < 0)
|
||||
NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
|
||||
|
||||
DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&ProcessSingleton::LinuxWatcher::StartListening,
|
||||
watcher_,
|
||||
sock));
|
||||
sock_ = sock;
|
||||
|
||||
if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
|
||||
StartListeningOnSocket();
|
||||
} else {
|
||||
listen_on_ready_ = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -258,6 +258,9 @@ ProcessSingleton::NotifyOtherProcessOrCreate() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessSingleton::StartListeningOnSocket() {}
|
||||
void ProcessSingleton::OnBrowserReady() {}
|
||||
|
||||
// Look for a Chrome instance that uses the same profile directory. If there
|
||||
// isn't one, create a message window with its title set to the profile
|
||||
// directory path.
|
||||
|
||||
@@ -37,6 +37,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||
* `icon` [NativeImage](native-image.md) - (optional) An icon to use in the notification
|
||||
* `hasReply` Boolean - (optional) Whether or not to add an inline reply option to the notification. _macOS_
|
||||
* `replyPlaceholder` String - (optional) The placeholder to write in the inline reply input field. _macOS_
|
||||
* `sound` String - (optional) The name of the sound file to play when the notification is shown. _macOS_
|
||||
* `actions` [NotificationAction[]](structures/notification-action.md) - (optional) Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation _macOS_
|
||||
|
||||
|
||||
@@ -102,3 +103,18 @@ Immediately shows the notification to the user, please note this means unlike th
|
||||
HTML5 Notification implementation, simply instantiating a `new Notification` does
|
||||
not immediately show it to the user, you need to call this method before the OS
|
||||
will display it.
|
||||
|
||||
### Playing Sounds
|
||||
|
||||
On macOS, you can specify the name of the sound you'd like to play when the
|
||||
notification is shown. Any of the default sounds (under System Preferences >
|
||||
Sound) can be used, in addition to custom sound files. Be sure that the sound
|
||||
file is copied under the app bundle (e.g., `YourApp.app/Contents/Resources`),
|
||||
or one of the following locations:
|
||||
|
||||
* `~/Library/Sounds`
|
||||
* `/Library/Sounds`
|
||||
* `/Network/Library/Sounds`
|
||||
* `/System/Library/Sounds`
|
||||
|
||||
See the [`NSSound`](https://developer.apple.com/documentation/appkit/nssound) docs for more information.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '1.7.6',
|
||||
'version%': '1.7.8',
|
||||
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
||||
},
|
||||
'includes': [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "1.7.6",
|
||||
"version": "1.7.8",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -63,10 +63,9 @@ def main():
|
||||
deps += LINUX_DEPS_NO_ARM
|
||||
execute(['sudo', 'apt-get', 'install'] + deps)
|
||||
|
||||
execute(['sh', '-e', '/etc/init.d/xvfb', 'start'])
|
||||
|
||||
if PLATFORM == 'linux':
|
||||
if PLATFORM == 'linux' and target_arch == 'x64':
|
||||
os.environ['DISPLAY'] = ':99.0'
|
||||
execute(['sh', '-e', '/etc/init.d/xvfb', 'start'])
|
||||
|
||||
# CI's npm is not reliable.
|
||||
npm = 'npm.cmd' if PLATFORM == 'win32' else 'npm'
|
||||
|
||||
21
script/upload-to-github.js
Normal file
21
script/upload-to-github.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const GitHub = require('github')
|
||||
const github = new GitHub()
|
||||
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
||||
|
||||
let filePath = process.argv[2]
|
||||
let fileName = process.argv[3]
|
||||
let releaseId = process.argv[4]
|
||||
|
||||
let githubOpts = {
|
||||
owner: 'electron',
|
||||
repo: 'electron',
|
||||
id: releaseId,
|
||||
filePath: filePath,
|
||||
name: fileName
|
||||
}
|
||||
github.repos.uploadAsset(githubOpts).then(() => {
|
||||
process.exit()
|
||||
}).catch((err) => {
|
||||
console.log(`Error uploading ${fileName} to GitHub:`, err)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -67,7 +67,7 @@ def main():
|
||||
run_python_script('upload-index-json.py')
|
||||
|
||||
# Create and upload the Electron SHASUMS*.txt
|
||||
release_electron_checksums(github, release)
|
||||
release_electron_checksums(release)
|
||||
|
||||
# Press the publish button.
|
||||
publish_release(github, release['id'])
|
||||
@@ -198,11 +198,14 @@ def create_release_draft(github, tag):
|
||||
return r
|
||||
|
||||
|
||||
def release_electron_checksums(github, release):
|
||||
def release_electron_checksums(release):
|
||||
checksums = run_python_script('merge-electron-checksums.py',
|
||||
'-v', ELECTRON_VERSION)
|
||||
upload_io_to_github(github, release, 'SHASUMS256.txt',
|
||||
StringIO(checksums.decode('utf-8')), 'text/plain')
|
||||
filename = 'SHASUMS256.txt'
|
||||
filepath = os.path.join(SOURCE_ROOT, filename)
|
||||
with open(filepath, 'w') as sha_file:
|
||||
sha_file.write(checksums.decode('utf-8'))
|
||||
upload_io_to_github(release, filename, filepath)
|
||||
|
||||
|
||||
def upload_electron(github, release, file_path):
|
||||
@@ -217,8 +220,7 @@ def upload_electron(github, release, file_path):
|
||||
pass
|
||||
|
||||
# Upload the file.
|
||||
with open(file_path, 'rb') as f:
|
||||
upload_io_to_github(github, release, filename, f, 'application/zip')
|
||||
upload_io_to_github(release, filename, file_path)
|
||||
|
||||
# Upload the checksum file.
|
||||
upload_sha256_checksum(release['tag_name'], file_path)
|
||||
@@ -232,11 +234,11 @@ def upload_electron(github, release, file_path):
|
||||
upload_electron(github, release, arm_file_path)
|
||||
|
||||
|
||||
def upload_io_to_github(github, release, name, io, content_type):
|
||||
params = {'name': name}
|
||||
headers = {'Content-Type': content_type}
|
||||
github.repos(ELECTRON_REPO).releases(release['id']).assets.post(
|
||||
params=params, headers=headers, data=io, verify=False)
|
||||
def upload_io_to_github(release, filename, filepath):
|
||||
print 'Uploading %s to Github' % \
|
||||
(filename)
|
||||
script_path = os.path.join(SOURCE_ROOT, 'script', 'upload-to-github.js')
|
||||
execute(['node', script_path, filepath, filename, str(release['id'])])
|
||||
|
||||
|
||||
def upload_sha256_checksum(version, file_path):
|
||||
|
||||
@@ -149,6 +149,34 @@ describe('app module', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.makeSingleInstance', function () {
|
||||
it('prevents the second launch of app', function (done) {
|
||||
this.timeout(120000)
|
||||
const appPath = path.join(__dirname, 'fixtures', 'api', 'singleton')
|
||||
// First launch should exit with 0.
|
||||
let secondLaunched = false
|
||||
const first = ChildProcess.spawn(remote.process.execPath, [appPath])
|
||||
let launchOnce = true
|
||||
first.stdout.on('data', (data) => {
|
||||
if (data.toString().trim() === 'launched' && launchOnce) {
|
||||
launchOnce = false
|
||||
// Second launch should exit with 1.
|
||||
const second = ChildProcess.spawn(remote.process.execPath, [appPath])
|
||||
second.once('exit', (code) => {
|
||||
assert.ok(!secondLaunched)
|
||||
assert.equal(code, 1)
|
||||
secondLaunched = true
|
||||
})
|
||||
}
|
||||
})
|
||||
first.once('exit', (code) => {
|
||||
assert.ok(secondLaunched)
|
||||
assert.equal(code, 0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.relaunch', function () {
|
||||
let server = null
|
||||
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'
|
||||
@@ -208,9 +236,10 @@ describe('app module', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.importCertificate', function () {
|
||||
xdescribe('app.importCertificate', function () {
|
||||
if (process.platform !== 'linux') return
|
||||
|
||||
this.timeout(120000)
|
||||
var w = null
|
||||
|
||||
afterEach(function () {
|
||||
@@ -405,7 +434,7 @@ describe('app module', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('select-client-certificate event', function () {
|
||||
xdescribe('select-client-certificate event', function () {
|
||||
let w = null
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
15
spec/fixtures/api/singleton/main.js
vendored
Normal file
15
spec/fixtures/api/singleton/main.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
const {app} = require('electron')
|
||||
|
||||
console.log('launched')
|
||||
|
||||
process.on('uncaughtException', () => {
|
||||
app.exit(2)
|
||||
})
|
||||
|
||||
const shouldExit = app.makeSingleInstance(() => {
|
||||
process.nextTick(() => app.exit(0))
|
||||
})
|
||||
|
||||
if (shouldExit) {
|
||||
app.exit(1)
|
||||
}
|
||||
5
spec/fixtures/api/singleton/package.json
vendored
Normal file
5
spec/fixtures/api/singleton/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "electron-app-singleton",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
2
vendor/libchromiumcontent
vendored
2
vendor/libchromiumcontent
vendored
Submodule vendor/libchromiumcontent updated: 92e2d6a965...be42f2234b
2
vendor/node
vendored
2
vendor/node
vendored
Submodule vendor/node updated: dfa72e2c73...a992f2ff41
Reference in New Issue
Block a user