Merge branch 'master' into master
This commit is contained in:
commit
9b3960fe90
125 changed files with 5299 additions and 4365 deletions
|
@ -19,6 +19,10 @@ jobs:
|
||||||
case ${MESSAGE} in
|
case ${MESSAGE} in
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
||||||
esac
|
esac
|
||||||
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
|
echo 'release build triggered from api'
|
||||||
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
fi
|
||||||
- run:
|
- run:
|
||||||
name: Bootstrap
|
name: Bootstrap
|
||||||
command: |
|
command: |
|
||||||
|
@ -52,9 +56,12 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Upload distribution
|
name: Upload distribution
|
||||||
command: |
|
command: |
|
||||||
if [ "$ELECTRON_RELEASE" == "1" ]; then
|
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
|
||||||
echo 'Uploading Electron release distribution'
|
echo 'Uploading Electron release distribution to github releases'
|
||||||
script/upload.py
|
script/upload.py
|
||||||
|
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
|
||||||
|
echo 'Uploading Electron release distribution to s3'
|
||||||
|
script/upload.py --upload_to_s3
|
||||||
else
|
else
|
||||||
echo 'Skipping upload distribution because build is not for release'
|
echo 'Skipping upload distribution because build is not for release'
|
||||||
fi
|
fi
|
||||||
|
@ -63,7 +70,7 @@ jobs:
|
||||||
- image: electronbuilds/electron:0.0.3
|
- image: electronbuilds/electron:0.0.3
|
||||||
environment:
|
environment:
|
||||||
TARGET_ARCH: arm64
|
TARGET_ARCH: arm64
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
|
@ -76,6 +83,10 @@ jobs:
|
||||||
case ${MESSAGE} in
|
case ${MESSAGE} in
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
||||||
esac
|
esac
|
||||||
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
|
echo 'release build triggered from api'
|
||||||
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
fi
|
||||||
- run:
|
- run:
|
||||||
name: Bootstrap
|
name: Bootstrap
|
||||||
command: |
|
command: |
|
||||||
|
@ -109,9 +120,12 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Upload distribution
|
name: Upload distribution
|
||||||
command: |
|
command: |
|
||||||
if [ "$ELECTRON_RELEASE" == "1" ]; then
|
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
|
||||||
echo 'Uploading Electron release distribution'
|
echo 'Uploading Electron release distribution to github releases'
|
||||||
script/upload.py
|
script/upload.py
|
||||||
|
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
|
||||||
|
echo 'Uploading Electron release distribution to s3'
|
||||||
|
script/upload.py --upload_to_s3
|
||||||
else
|
else
|
||||||
echo 'Skipping upload distribution because build is not for release'
|
echo 'Skipping upload distribution because build is not for release'
|
||||||
fi
|
fi
|
||||||
|
@ -133,6 +147,10 @@ jobs:
|
||||||
case ${MESSAGE} in
|
case ${MESSAGE} in
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
||||||
esac
|
esac
|
||||||
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
|
echo 'release build triggered from api'
|
||||||
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
fi
|
||||||
- run:
|
- run:
|
||||||
name: Bootstrap
|
name: Bootstrap
|
||||||
command: |
|
command: |
|
||||||
|
@ -166,9 +184,12 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Upload distribution
|
name: Upload distribution
|
||||||
command: |
|
command: |
|
||||||
if [ "$ELECTRON_RELEASE" == "1" ]; then
|
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
|
||||||
echo 'Uploading Electron release distribution'
|
echo 'Uploading Electron release distribution to github releases'
|
||||||
script/upload.py
|
script/upload.py
|
||||||
|
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
|
||||||
|
echo 'Uploading Electron release distribution to s3'
|
||||||
|
script/upload.py --upload_to_s3
|
||||||
else
|
else
|
||||||
echo 'Skipping upload distribution because build is not for release'
|
echo 'Skipping upload distribution because build is not for release'
|
||||||
fi
|
fi
|
||||||
|
@ -191,6 +212,10 @@ jobs:
|
||||||
case ${MESSAGE} in
|
case ${MESSAGE} in
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
||||||
esac
|
esac
|
||||||
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
|
echo 'release build triggered from api'
|
||||||
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
fi
|
||||||
- run:
|
- run:
|
||||||
name: Bootstrap
|
name: Bootstrap
|
||||||
command: |
|
command: |
|
||||||
|
@ -224,9 +249,12 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Upload distribution
|
name: Upload distribution
|
||||||
command: |
|
command: |
|
||||||
if [ "$ELECTRON_RELEASE" == "1" ]; then
|
if [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" != "1" ]; then
|
||||||
echo 'Uploading Electron release distribution'
|
echo 'Uploading Electron release distribution to github releases'
|
||||||
script/upload.py
|
script/upload.py
|
||||||
|
elif [ "$ELECTRON_RELEASE" == "1" ] && [ "$TRIGGERED_BY_API" == "1" ]; then
|
||||||
|
echo 'Uploading Electron release distribution to s3'
|
||||||
|
script/upload.py --upload_to_s3
|
||||||
else
|
else
|
||||||
echo 'Skipping upload distribution because build is not for release'
|
echo 'Skipping upload distribution because build is not for release'
|
||||||
fi
|
fi
|
||||||
|
|
44
Jenkinsfile
vendored
Normal file
44
Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
pipeline {
|
||||||
|
agent none
|
||||||
|
stages {
|
||||||
|
stage('Build') {
|
||||||
|
parallel {
|
||||||
|
stage('electron-osx-x64') {
|
||||||
|
agent {
|
||||||
|
label 'osx'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'script/bootstrap.py --target_arch=x64 --dev'
|
||||||
|
sh 'npm run lint'
|
||||||
|
sh 'script/build.py -c D'
|
||||||
|
sh 'script/test.py --ci --rebuild_native_modules'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('electron-mas-x64') {
|
||||||
|
agent {
|
||||||
|
label 'osx'
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
MAS_BUILD = '1'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'script/bootstrap.py --target_arch=x64 --dev'
|
||||||
|
sh 'npm run lint'
|
||||||
|
sh 'script/build.py -c D'
|
||||||
|
sh 'script/test.py --ci --rebuild_native_modules'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -105,7 +105,6 @@ forums
|
||||||
- [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack
|
- [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack
|
||||||
- [`electron-ru`](https://telegram.me/electron_ru) *(Russian)*
|
- [`electron-ru`](https://telegram.me/electron_ru) *(Russian)*
|
||||||
- [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*
|
- [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*
|
||||||
- [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)*
|
|
||||||
- [`electron-jp`](https://electron-jp.slack.com) *(Japanese)*
|
- [`electron-jp`](https://electron-jp.slack.com) *(Japanese)*
|
||||||
- [`electron-tr`](http://electron-tr.herokuapp.com) *(Turkish)*
|
- [`electron-tr`](http://electron-tr.herokuapp.com) *(Turkish)*
|
||||||
- [`electron-id`](https://electron-id.slack.com) *(Indonesia)*
|
- [`electron-id`](https://electron-id.slack.com) *(Indonesia)*
|
||||||
|
|
|
@ -1073,8 +1073,17 @@ std::vector<mate::Dictionary> App::GetAppMetrics(v8::Isolate* isolate) {
|
||||||
cpu_dict.Set("percentCPUUsage",
|
cpu_dict.Set("percentCPUUsage",
|
||||||
process_metric.second->metrics->GetPlatformIndependentCPUUsage()
|
process_metric.second->metrics->GetPlatformIndependentCPUUsage()
|
||||||
/ processor_count);
|
/ processor_count);
|
||||||
|
|
||||||
|
#if !defined(OS_WIN)
|
||||||
cpu_dict.Set("idleWakeupsPerSecond",
|
cpu_dict.Set("idleWakeupsPerSecond",
|
||||||
process_metric.second->metrics->GetIdleWakeupsPerSecond());
|
process_metric.second->metrics->GetIdleWakeupsPerSecond());
|
||||||
|
#else
|
||||||
|
// Chrome's underlying process_metrics.cc will throw a non-fatal warning
|
||||||
|
// that this method isn't implemented on Windows, so set it to 0 instead
|
||||||
|
// of calling it
|
||||||
|
cpu_dict.Set("idleWakeupsPerSecond", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
pid_dict.Set("cpu", cpu_dict);
|
pid_dict.Set("cpu", cpu_dict);
|
||||||
pid_dict.Set("pid", process_metric.second->pid);
|
pid_dict.Set("pid", process_metric.second->pid);
|
||||||
pid_dict.Set("type",
|
pid_dict.Set("type",
|
||||||
|
|
|
@ -169,14 +169,22 @@ void Notification::NotificationDisplayed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Notification::NotificationDestroyed() {
|
void Notification::NotificationDestroyed() {
|
||||||
Emit("close");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Notification::NotificationClosed() {
|
void Notification::NotificationClosed() {
|
||||||
|
Emit("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notification::Close() {
|
||||||
|
if (notification_) {
|
||||||
|
notification_->Dismiss();
|
||||||
|
notification_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Showing notifications
|
// Showing notifications
|
||||||
void Notification::Show() {
|
void Notification::Show() {
|
||||||
|
Close();
|
||||||
if (presenter_) {
|
if (presenter_) {
|
||||||
notification_ = presenter_->CreateNotification(this);
|
notification_ = presenter_->CreateNotification(this);
|
||||||
if (notification_) {
|
if (notification_) {
|
||||||
|
@ -207,6 +215,7 @@ void Notification::BuildPrototype(v8::Isolate* isolate,
|
||||||
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
||||||
.MakeDestroyable()
|
.MakeDestroyable()
|
||||||
.SetMethod("show", &Notification::Show)
|
.SetMethod("show", &Notification::Show)
|
||||||
|
.SetMethod("close", &Notification::Close)
|
||||||
.SetProperty("title", &Notification::GetTitle, &Notification::SetTitle)
|
.SetProperty("title", &Notification::GetTitle, &Notification::SetTitle)
|
||||||
.SetProperty("subtitle", &Notification::GetSubtitle,
|
.SetProperty("subtitle", &Notification::GetSubtitle,
|
||||||
&Notification::SetSubtitle)
|
&Notification::SetSubtitle)
|
||||||
|
|
|
@ -45,6 +45,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||||
~Notification() override;
|
~Notification() override;
|
||||||
|
|
||||||
void Show();
|
void Show();
|
||||||
|
void Close();
|
||||||
|
|
||||||
// Prop Getters
|
// Prop Getters
|
||||||
base::string16 GetTitle() const;
|
base::string16 GetTitle() const;
|
||||||
|
|
|
@ -212,6 +212,7 @@ struct Converter<atom::VerifyRequestParams> {
|
||||||
dict.Set("hostname", val.hostname);
|
dict.Set("hostname", val.hostname);
|
||||||
dict.Set("certificate", val.certificate);
|
dict.Set("certificate", val.certificate);
|
||||||
dict.Set("verificationResult", val.default_result);
|
dict.Set("verificationResult", val.default_result);
|
||||||
|
dict.Set("errorCode", val.error_code);
|
||||||
return dict.GetHandle();
|
return dict.GetHandle();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,6 +67,7 @@ void SystemPreferences::BuildPrototype(
|
||||||
&SystemPreferences::UnsubscribeLocalNotification)
|
&SystemPreferences::UnsubscribeLocalNotification)
|
||||||
.SetMethod("getUserDefault", &SystemPreferences::GetUserDefault)
|
.SetMethod("getUserDefault", &SystemPreferences::GetUserDefault)
|
||||||
.SetMethod("setUserDefault", &SystemPreferences::SetUserDefault)
|
.SetMethod("setUserDefault", &SystemPreferences::SetUserDefault)
|
||||||
|
.SetMethod("removeUserDefault", &SystemPreferences::RemoveUserDefault)
|
||||||
.SetMethod("isSwipeTrackingFromScrollEventsEnabled",
|
.SetMethod("isSwipeTrackingFromScrollEventsEnabled",
|
||||||
&SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled)
|
&SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,6 +76,7 @@ class SystemPreferences : public mate::EventEmitter<SystemPreferences>
|
||||||
void SetUserDefault(const std::string& name,
|
void SetUserDefault(const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
mate::Arguments* args);
|
mate::Arguments* args);
|
||||||
|
void RemoveUserDefault(const std::string& name);
|
||||||
bool IsSwipeTrackingFromScrollEventsEnabled();
|
bool IsSwipeTrackingFromScrollEventsEnabled();
|
||||||
#endif
|
#endif
|
||||||
bool IsDarkMode();
|
bool IsDarkMode();
|
||||||
|
|
|
@ -229,6 +229,11 @@ void SystemPreferences::SetUserDefault(const std::string& name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SystemPreferences::RemoveUserDefault(const std::string& name) {
|
||||||
|
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
[defaults removeObjectForKey:base::SysUTF8ToNSString(name)];
|
||||||
|
}
|
||||||
|
|
||||||
bool SystemPreferences::IsDarkMode() {
|
bool SystemPreferences::IsDarkMode() {
|
||||||
NSString* mode = [[NSUserDefaults standardUserDefaults]
|
NSString* mode = [[NSUserDefaults standardUserDefaults]
|
||||||
stringForKey:@"AppleInterfaceStyle"];
|
stringForKey:@"AppleInterfaceStyle"];
|
||||||
|
|
|
@ -286,12 +286,13 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||||
request_id_(0),
|
request_id_(0),
|
||||||
background_throttling_(true),
|
background_throttling_(true),
|
||||||
enable_devtools_(true) {
|
enable_devtools_(true) {
|
||||||
|
const mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
|
||||||
if (type == REMOTE) {
|
if (type == REMOTE) {
|
||||||
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
||||||
Init(isolate);
|
Init(isolate);
|
||||||
AttachAsUserData(web_contents);
|
AttachAsUserData(web_contents);
|
||||||
|
InitZoomController(web_contents, options);
|
||||||
} else {
|
} else {
|
||||||
const mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
|
|
||||||
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
||||||
session_.Reset(isolate, session.ToV8());
|
session_.Reset(isolate, session.ToV8());
|
||||||
InitWithSessionAndOptions(isolate, web_contents, session, options);
|
InitWithSessionAndOptions(isolate, web_contents, session, options);
|
||||||
|
@ -392,6 +393,15 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options)
|
||||||
InitWithSessionAndOptions(isolate, web_contents, session, options);
|
InitWithSessionAndOptions(isolate, web_contents, session, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::InitZoomController(content::WebContents* web_contents,
|
||||||
|
const mate::Dictionary& options) {
|
||||||
|
WebContentsZoomController::CreateForWebContents(web_contents);
|
||||||
|
zoom_controller_ = WebContentsZoomController::FromWebContents(web_contents);
|
||||||
|
double zoom_factor;
|
||||||
|
if (options.Get(options::kZoomFactor, &zoom_factor))
|
||||||
|
zoom_controller_->SetDefaultZoomFactor(zoom_factor);
|
||||||
|
}
|
||||||
|
|
||||||
void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate,
|
void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate,
|
||||||
content::WebContents *web_contents,
|
content::WebContents *web_contents,
|
||||||
mate::Handle<api::Session> session,
|
mate::Handle<api::Session> session,
|
||||||
|
@ -409,11 +419,7 @@ void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate,
|
||||||
// Initialize security state client.
|
// Initialize security state client.
|
||||||
SecurityStateTabHelper::CreateForWebContents(web_contents);
|
SecurityStateTabHelper::CreateForWebContents(web_contents);
|
||||||
// Initialize zoom controller.
|
// Initialize zoom controller.
|
||||||
WebContentsZoomController::CreateForWebContents(web_contents);
|
InitZoomController(web_contents, options);
|
||||||
zoom_controller_ = WebContentsZoomController::FromWebContents(web_contents);
|
|
||||||
double zoom_factor;
|
|
||||||
if (options.Get(options::kZoomFactor, &zoom_factor))
|
|
||||||
zoom_controller_->SetDefaultZoomFactor(zoom_factor);
|
|
||||||
|
|
||||||
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
||||||
|
|
||||||
|
|
|
@ -385,6 +385,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
// get the zoom level.
|
// get the zoom level.
|
||||||
void OnGetZoomLevel(IPC::Message* reply_msg);
|
void OnGetZoomLevel(IPC::Message* reply_msg);
|
||||||
|
|
||||||
|
void InitZoomController(content::WebContents* web_contents,
|
||||||
|
const mate::Dictionary& options);
|
||||||
|
|
||||||
v8::Global<v8::Value> session_;
|
v8::Global<v8::Value> session_;
|
||||||
v8::Global<v8::Value> devtools_web_contents_;
|
v8::Global<v8::Value> devtools_web_contents_;
|
||||||
v8::Global<v8::Value> debugger_;
|
v8::Global<v8::Value> debugger_;
|
||||||
|
|
|
@ -79,8 +79,12 @@ class EventEmitter : public Wrappable<T> {
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
v8::Locker locker(isolate());
|
v8::Locker locker(isolate());
|
||||||
v8::HandleScope handle_scope(isolate());
|
v8::HandleScope handle_scope(isolate());
|
||||||
|
v8::Local<v8::Object> wrapper = GetWrapper();
|
||||||
|
if (wrapper.IsEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
v8::Local<v8::Object> event = internal::CreateJSEvent(
|
v8::Local<v8::Object> event = internal::CreateJSEvent(
|
||||||
isolate(), GetWrapper(), sender, message);
|
isolate(), wrapper, sender, message);
|
||||||
return EmitWithEvent(name, event, args...);
|
return EmitWithEvent(name, event, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,10 @@ class TrackableObject : public TrackableObjectBase,
|
||||||
public:
|
public:
|
||||||
// Mark the JS object as destroyed.
|
// Mark the JS object as destroyed.
|
||||||
void MarkDestroyed() {
|
void MarkDestroyed() {
|
||||||
Wrappable<T>::GetWrapper()->SetAlignedPointerInInternalField(0, nullptr);
|
v8::Local<v8::Object> wrapper = Wrappable<T>::GetWrapper();
|
||||||
|
if (!wrapper.IsEmpty()) {
|
||||||
|
wrapper->SetAlignedPointerInInternalField(0, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDestroyed() {
|
bool IsDestroyed() {
|
||||||
|
|
|
@ -117,6 +117,14 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
||||||
bool center;
|
bool center;
|
||||||
if (options.Get(options::kX, &x) && options.Get(options::kY, &y)) {
|
if (options.Get(options::kX, &x) && options.Get(options::kY, &y)) {
|
||||||
SetPosition(gfx::Point(x, y));
|
SetPosition(gfx::Point(x, y));
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
// FIXME(felixrieseberg): Dirty, dirty workaround for
|
||||||
|
// https://github.com/electron/electron/issues/10862
|
||||||
|
// Somehow, we need to call `SetBounds` twice to get
|
||||||
|
// usable results. The root cause is still unknown.
|
||||||
|
SetPosition(gfx::Point(x, y));
|
||||||
|
#endif
|
||||||
} else if (options.Get(options::kCenter, ¢er) && center) {
|
} else if (options.Get(options::kCenter, ¢er) && center) {
|
||||||
Center();
|
Center();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "atom/browser/native_window_mac.h"
|
#include "atom/browser/native_window_mac.h"
|
||||||
|
|
||||||
|
#include <AvailabilityMacros.h>
|
||||||
#include <Quartz/Quartz.h>
|
#include <Quartz/Quartz.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -465,7 +466,7 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#if !defined(MAC_OS_X_VERSION_10_12)
|
#if !defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NSWindowTabbingModeDisallowed = 2
|
NSWindowTabbingModeDisallowed = 2
|
||||||
|
@ -482,7 +483,7 @@ enum {
|
||||||
- (IBAction)toggleTabBar:(id)sender;
|
- (IBAction)toggleTabBar:(id)sender;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif // MAC_OS_X_VERSION_10_12
|
#endif
|
||||||
|
|
||||||
@interface AtomNSWindow : EventDispatchingWindow<QLPreviewPanelDataSource, QLPreviewPanelDelegate, NSTouchBarDelegate> {
|
@interface AtomNSWindow : EventDispatchingWindow<QLPreviewPanelDataSource, QLPreviewPanelDelegate, NSTouchBarDelegate> {
|
||||||
@private
|
@private
|
||||||
|
|
|
@ -92,6 +92,7 @@ class CertVerifierRequest : public AtomCertVerifier::Request {
|
||||||
std::unique_ptr<VerifyRequestParams> request(new VerifyRequestParams());
|
std::unique_ptr<VerifyRequestParams> request(new VerifyRequestParams());
|
||||||
request->hostname = params_.hostname();
|
request->hostname = params_.hostname();
|
||||||
request->default_result = net::ErrorToString(error);
|
request->default_result = net::ErrorToString(error);
|
||||||
|
request->error_code = error;
|
||||||
request->certificate = params_.certificate();
|
request->certificate = params_.certificate();
|
||||||
BrowserThread::PostTask(
|
BrowserThread::PostTask(
|
||||||
BrowserThread::UI, FROM_HERE,
|
BrowserThread::UI, FROM_HERE,
|
||||||
|
|
|
@ -19,6 +19,7 @@ class CertVerifierRequest;
|
||||||
struct VerifyRequestParams {
|
struct VerifyRequestParams {
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
std::string default_result;
|
std::string default_result;
|
||||||
|
int error_code;
|
||||||
scoped_refptr<net::X509Certificate> certificate;
|
scoped_refptr<net::X509Certificate> certificate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,9 @@ void URLRequestFetchJob::OnURLFetchComplete(const net::URLFetcher* source) {
|
||||||
HeadersCompleted();
|
HeadersCompleted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ReadRawDataComplete(0);
|
if (request_->status().is_io_pending()) {
|
||||||
|
ReadRawDataComplete(0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NotifyStartError(fetcher_->GetStatus());
|
NotifyStartError(fetcher_->GetStatus());
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>electron.icns</string>
|
<string>electron.icns</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.8.1</string>
|
<string>1.8.2</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.8.1</string>
|
<string>1.8.2</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.developer-tools</string>
|
<string>public.app-category.developer-tools</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|
|
@ -56,8 +56,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,8,1,0
|
FILEVERSION 1,8,2,2
|
||||||
PRODUCTVERSION 1,8,1,0
|
PRODUCTVERSION 1,8,2,2
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -74,12 +74,12 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "GitHub, Inc."
|
VALUE "CompanyName", "GitHub, Inc."
|
||||||
VALUE "FileDescription", "Electron"
|
VALUE "FileDescription", "Electron"
|
||||||
VALUE "FileVersion", "1.8.1"
|
VALUE "FileVersion", "1.8.2"
|
||||||
VALUE "InternalName", "electron.exe"
|
VALUE "InternalName", "electron.exe"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||||
VALUE "OriginalFilename", "electron.exe"
|
VALUE "OriginalFilename", "electron.exe"
|
||||||
VALUE "ProductName", "Electron"
|
VALUE "ProductName", "Electron"
|
||||||
VALUE "ProductVersion", "1.8.1"
|
VALUE "ProductVersion", "1.8.2"
|
||||||
VALUE "SquirrelAwareVersion", "1"
|
VALUE "SquirrelAwareVersion", "1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "native_mate/constructor.h"
|
#include "native_mate/constructor.h"
|
||||||
#include "native_mate/persistent_dictionary.h"
|
#include "native_mate/persistent_dictionary.h"
|
||||||
|
|
||||||
@interface AtomTouchBar : NSObject<NSScrubberDelegate, NSScrubberDataSource> {
|
@interface AtomTouchBar : NSObject<NSScrubberDelegate, NSScrubberDataSource, NSScrubberFlowLayoutDelegate> {
|
||||||
@protected
|
@protected
|
||||||
std::vector<mate::PersistentDictionary> ordered_settings_;
|
std::vector<mate::PersistentDictionary> ordered_settings_;
|
||||||
std::map<std::string, mate::PersistentDictionary> settings_;
|
std::map<std::string, mate::PersistentDictionary> settings_;
|
||||||
|
|
|
@ -667,4 +667,40 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
|
||||||
return itemView;
|
return itemView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSSize)scrubber:(NSScrubber *)scrubber layout:(NSScrubberFlowLayout *)layout sizeForItemAtIndex:(NSInteger)itemIndex
|
||||||
|
{
|
||||||
|
NSInteger width = 50;
|
||||||
|
NSInteger height = 30;
|
||||||
|
NSInteger margin = 15;
|
||||||
|
NSSize defaultSize = NSMakeSize(width, height);
|
||||||
|
|
||||||
|
std::string s_id([[scrubber identifier] UTF8String]);
|
||||||
|
if (![self hasItemWithID:s_id]) return defaultSize;
|
||||||
|
|
||||||
|
mate::PersistentDictionary settings = settings_[s_id];
|
||||||
|
std::vector<mate::PersistentDictionary> items;
|
||||||
|
if (!settings.Get("items", &items)) return defaultSize;
|
||||||
|
|
||||||
|
if (itemIndex >= static_cast<NSInteger>(items.size())) return defaultSize;
|
||||||
|
|
||||||
|
mate::PersistentDictionary item = items[itemIndex];
|
||||||
|
std::string title;
|
||||||
|
|
||||||
|
if (item.Get("label", &title)) {
|
||||||
|
NSSize size = NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX);
|
||||||
|
NSRect textRect = [base::SysUTF8ToNSString(title) boundingRectWithSize:size
|
||||||
|
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||||
|
attributes:@{ NSFontAttributeName: [NSFont systemFontOfSize:0]}];
|
||||||
|
|
||||||
|
width = textRect.size.width + margin;
|
||||||
|
} else {
|
||||||
|
gfx::Image image;
|
||||||
|
if (item.Get("icon", &image)) {
|
||||||
|
width = image.AsNSImage().size.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NSMakeSize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
@class NSTouchBar, NSTouchBarItem;
|
@class NSTouchBar, NSTouchBarItem;
|
||||||
@class NSScrubber, NSScrubberItemView, NSScrubberArrangedView, NSScrubberTextItemView, NSScrubberImageItemView, NSScrubberSelectionStyle;
|
@class NSScrubber, NSScrubberItemView, NSScrubberArrangedView, NSScrubberTextItemView, NSScrubberImageItemView, NSScrubberSelectionStyle;
|
||||||
@protocol NSTouchBarDelegate, NSScrubberDelegate, NSScrubberDataSource;
|
@protocol NSTouchBarDelegate, NSScrubberDelegate, NSScrubberDataSource, NSScrubberFlowLayoutDelegate, NSScrubberFlowLayout;
|
||||||
|
|
||||||
typedef float NSTouchBarItemPriority;
|
typedef float NSTouchBarItemPriority;
|
||||||
static const NSTouchBarItemPriority NSTouchBarItemPriorityHigh = 1000;
|
static const NSTouchBarItemPriority NSTouchBarItemPriorityHigh = 1000;
|
||||||
|
@ -149,6 +149,9 @@ static const NSTouchBarItemIdentifier NSTouchBarItemIdentifierOtherItemsProxy =
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface NSScrubberFlowLayout: NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
@interface NSScrubberSelectionStyle : NSObject<NSCoding>
|
@interface NSScrubberSelectionStyle : NSObject<NSCoding>
|
||||||
|
|
||||||
@property(class, strong, readonly) NSScrubberSelectionStyle* outlineOverlayStyle;
|
@property(class, strong, readonly) NSScrubberSelectionStyle* outlineOverlayStyle;
|
||||||
|
@ -229,6 +232,12 @@ static const NSTouchBarItemIdentifier NSTouchBarItemIdentifierOtherItemsProxy =
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@protocol NSScrubberFlowLayoutDelegate<NSObject>
|
||||||
|
|
||||||
|
- (NSSize)scrubber:(NSScrubber *)scrubber layout:(NSScrubberFlowLayout *)layout sizeForItemAtIndex:(NSInteger)itemIndex;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
#pragma clang assume_nonnull end
|
#pragma clang assume_nonnull end
|
||||||
|
|
||||||
#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1
|
#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1
|
||||||
|
|
|
@ -31,14 +31,26 @@ struct Converter<CrashReporter::UploadReportResult> {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// TODO(2.0) Remove
|
||||||
void SetExtraParameter(const std::string& key, mate::Arguments* args) {
|
void SetExtraParameter(const std::string& key, mate::Arguments* args) {
|
||||||
std::string value;
|
std::string value;
|
||||||
if (args->GetNext(&value))
|
if (args->GetNext(&value))
|
||||||
CrashReporter::GetInstance()->SetExtraParameter(key, value);
|
CrashReporter::GetInstance()->AddExtraParameter(key, value);
|
||||||
else
|
else
|
||||||
CrashReporter::GetInstance()->RemoveExtraParameter(key);
|
CrashReporter::GetInstance()->RemoveExtraParameter(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddExtraParameter(const std::string& key, const std::string& value) {
|
||||||
|
CrashReporter::GetInstance()->AddExtraParameter(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveExtraParameter(const std::string& key) {
|
||||||
|
CrashReporter::GetInstance()->RemoveExtraParameter(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> GetParameters() {
|
||||||
|
return CrashReporter::GetInstance()->GetParameters();
|
||||||
|
}
|
||||||
|
|
||||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
v8::Local<v8::Context> context, void* priv) {
|
v8::Local<v8::Context> context, void* priv) {
|
||||||
|
@ -46,6 +58,9 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
auto reporter = base::Unretained(CrashReporter::GetInstance());
|
auto reporter = base::Unretained(CrashReporter::GetInstance());
|
||||||
dict.SetMethod("start", base::Bind(&CrashReporter::Start, reporter));
|
dict.SetMethod("start", base::Bind(&CrashReporter::Start, reporter));
|
||||||
dict.SetMethod("setExtraParameter", &SetExtraParameter);
|
dict.SetMethod("setExtraParameter", &SetExtraParameter);
|
||||||
|
dict.SetMethod("addExtraParameter", &AddExtraParameter);
|
||||||
|
dict.SetMethod("removeExtraParameter", &RemoveExtraParameter);
|
||||||
|
dict.SetMethod("getParameters", &GetParameters);
|
||||||
dict.SetMethod("getUploadedReports",
|
dict.SetMethod("getUploadedReports",
|
||||||
base::Bind(&CrashReporter::GetUploadedReports, reporter));
|
base::Bind(&CrashReporter::GetUploadedReports, reporter));
|
||||||
dict.SetMethod("setUploadToServer",
|
dict.SetMethod("setUploadToServer",
|
||||||
|
|
|
@ -541,6 +541,13 @@ mate::Handle<NativeImage> NativeImage::CreateFromDataURL(
|
||||||
return CreateEmpty(isolate);
|
return CreateEmpty(isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(OS_MACOSX)
|
||||||
|
mate::Handle<NativeImage> NativeImage::CreateFromNamedImage(
|
||||||
|
mate::Arguments* args, const std::string& name) {
|
||||||
|
return CreateEmpty(args->isolate());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void NativeImage::BuildPrototype(
|
void NativeImage::BuildPrototype(
|
||||||
v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> prototype) {
|
v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> prototype) {
|
||||||
|
@ -609,6 +616,8 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
dict.SetMethod("createFromBuffer", &atom::api::NativeImage::CreateFromBuffer);
|
dict.SetMethod("createFromBuffer", &atom::api::NativeImage::CreateFromBuffer);
|
||||||
dict.SetMethod("createFromDataURL",
|
dict.SetMethod("createFromDataURL",
|
||||||
&atom::api::NativeImage::CreateFromDataURL);
|
&atom::api::NativeImage::CreateFromDataURL);
|
||||||
|
dict.SetMethod("createFromNamedImage",
|
||||||
|
&atom::api::NativeImage::CreateFromNamedImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -53,6 +53,8 @@ class NativeImage : public mate::Wrappable<NativeImage> {
|
||||||
mate::Arguments* args, v8::Local<v8::Value> buffer);
|
mate::Arguments* args, v8::Local<v8::Value> buffer);
|
||||||
static mate::Handle<NativeImage> CreateFromDataURL(
|
static mate::Handle<NativeImage> CreateFromDataURL(
|
||||||
v8::Isolate* isolate, const GURL& url);
|
v8::Isolate* isolate, const GURL& url);
|
||||||
|
static mate::Handle<NativeImage> CreateFromNamedImage(
|
||||||
|
mate::Arguments* args, const std::string& name);
|
||||||
|
|
||||||
static void BuildPrototype(v8::Isolate* isolate,
|
static void BuildPrototype(v8::Isolate* isolate,
|
||||||
v8::Local<v8::FunctionTemplate> prototype);
|
v8::Local<v8::FunctionTemplate> prototype);
|
||||||
|
|
|
@ -6,10 +6,56 @@
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
#include "base/strings/sys_string_conversions.h"
|
||||||
|
#include "ui/gfx/color_utils.h"
|
||||||
|
#include "ui/gfx/image/image.h"
|
||||||
|
#include "ui/gfx/image/image_skia.h"
|
||||||
|
#include "ui/gfx/image/image_skia_operations.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
NSData* bufferFromNSImage(NSImage* image) {
|
||||||
|
CGImageRef ref = [image CGImageForProposedRect:nil context:nil hints:nil];
|
||||||
|
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:ref];
|
||||||
|
[rep setSize:[image size]];
|
||||||
|
return [rep representationUsingType:NSPNGFileType properties:[[NSDictionary alloc] init]];
|
||||||
|
}
|
||||||
|
|
||||||
|
double safeShift(double in, double def) {
|
||||||
|
if (in >= 0 || in <= 1 || in == def) return in;
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
mate::Handle<NativeImage> NativeImage::CreateFromNamedImage(
|
||||||
|
mate::Arguments* args, const std::string& name) {
|
||||||
|
@autoreleasepool {
|
||||||
|
std::vector<double> hsl_shift;
|
||||||
|
NSImage* image = [NSImage imageNamed:base::SysUTF8ToNSString(name)];
|
||||||
|
if (!image.valid) {
|
||||||
|
return CreateEmpty(args->isolate());
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData* png_data = bufferFromNSImage(image);
|
||||||
|
|
||||||
|
if (args->GetNext(&hsl_shift) && hsl_shift.size() == 3) {
|
||||||
|
gfx::Image gfx_image = gfx::Image::CreateFrom1xPNGBytes(
|
||||||
|
reinterpret_cast<const unsigned char*>((char *) [png_data bytes]), [png_data length]);
|
||||||
|
color_utils::HSL shift = {
|
||||||
|
safeShift(hsl_shift[0], -1),
|
||||||
|
safeShift(hsl_shift[1], 0.5),
|
||||||
|
safeShift(hsl_shift[2], 0.5)
|
||||||
|
};
|
||||||
|
png_data = bufferFromNSImage(gfx::Image(
|
||||||
|
gfx::ImageSkiaOperations::CreateHSLShiftedImage(
|
||||||
|
gfx_image.AsImageSkia(), shift)).CopyNSImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateFromPNG(args->isolate(), (char *) [png_data bytes], [png_data length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NativeImage::SetTemplateImage(bool setAsTemplate) {
|
void NativeImage::SetTemplateImage(bool setAsTemplate) {
|
||||||
[image_.AsNSImage() setTemplate:setAsTemplate];
|
[image_.AsNSImage() setTemplate:setAsTemplate];
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#define ATOM_MAJOR_VERSION 1
|
#define ATOM_MAJOR_VERSION 1
|
||||||
#define ATOM_MINOR_VERSION 8
|
#define ATOM_MINOR_VERSION 8
|
||||||
#define ATOM_PATCH_VERSION 1
|
#define ATOM_PATCH_VERSION 2
|
||||||
|
|
||||||
#define ATOM_VERSION_IS_RELEASE 1
|
#define ATOM_VERSION_IS_RELEASE 1
|
||||||
|
|
||||||
|
|
|
@ -86,13 +86,17 @@ void CrashReporter::InitBreakpad(const std::string& product_name,
|
||||||
void CrashReporter::SetUploadParameters() {
|
void CrashReporter::SetUploadParameters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReporter::SetExtraParameter(const std::string& key,
|
void CrashReporter::AddExtraParameter(const std::string& key,
|
||||||
const std::string& value) {
|
const std::string& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReporter::RemoveExtraParameter(const std::string& key) {
|
void CrashReporter::RemoveExtraParameter(const std::string& key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> CrashReporter::GetParameters() const {
|
||||||
|
return upload_parameters_;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(OS_MACOSX) && defined(MAS_BUILD)
|
#if defined(OS_MACOSX) && defined(MAS_BUILD)
|
||||||
// static
|
// static
|
||||||
CrashReporter* CrashReporter::GetInstance() {
|
CrashReporter* CrashReporter::GetInstance() {
|
||||||
|
|
|
@ -37,9 +37,10 @@ class CrashReporter {
|
||||||
|
|
||||||
virtual void SetUploadToServer(bool upload_to_server);
|
virtual void SetUploadToServer(bool upload_to_server);
|
||||||
virtual bool GetUploadToServer();
|
virtual bool GetUploadToServer();
|
||||||
virtual void SetExtraParameter(const std::string& key,
|
virtual void AddExtraParameter(const std::string& key,
|
||||||
const std::string& value);
|
const std::string& value);
|
||||||
virtual void RemoveExtraParameter(const std::string& key);
|
virtual void RemoveExtraParameter(const std::string& key);
|
||||||
|
virtual std::map<std::string, std::string> GetParameters() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CrashReporter();
|
CrashReporter();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
||||||
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -34,9 +35,10 @@ class CrashReporterMac : public CrashReporter {
|
||||||
void SetUploadParameters() override;
|
void SetUploadParameters() override;
|
||||||
void SetUploadToServer(bool upload_to_server) override;
|
void SetUploadToServer(bool upload_to_server) override;
|
||||||
bool GetUploadToServer() override;
|
bool GetUploadToServer() override;
|
||||||
void SetExtraParameter(const std::string& key,
|
void AddExtraParameter(const std::string& key,
|
||||||
const std::string& value) override;
|
const std::string& value) override;
|
||||||
void RemoveExtraParameter(const std::string& key) override;
|
void RemoveExtraParameter(const std::string& key) override;
|
||||||
|
std::map<std::string, std::string> GetParameters() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct base::DefaultSingletonTraits<CrashReporterMac>;
|
friend struct base::DefaultSingletonTraits<CrashReporterMac>;
|
||||||
|
|
|
@ -105,12 +105,13 @@ void CrashReporterMac::SetCrashKeyValue(const base::StringPiece& key,
|
||||||
simple_string_dictionary_->SetKeyValue(key.data(), value.data());
|
simple_string_dictionary_->SetKeyValue(key.data(), value.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReporterMac::SetExtraParameter(const std::string& key,
|
void CrashReporterMac::AddExtraParameter(const std::string& key,
|
||||||
const std::string& value) {
|
const std::string& value) {
|
||||||
if (simple_string_dictionary_)
|
if (simple_string_dictionary_) {
|
||||||
SetCrashKeyValue(key, value);
|
SetCrashKeyValue(key, value);
|
||||||
else
|
} else {
|
||||||
upload_parameters_[key] = value;
|
upload_parameters_[key] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReporterMac::RemoveExtraParameter(const std::string& key) {
|
void CrashReporterMac::RemoveExtraParameter(const std::string& key) {
|
||||||
|
@ -120,6 +121,20 @@ void CrashReporterMac::RemoveExtraParameter(const std::string& key) {
|
||||||
upload_parameters_.erase(key);
|
upload_parameters_.erase(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> CrashReporterMac::GetParameters() const {
|
||||||
|
if (simple_string_dictionary_) {
|
||||||
|
std::map<std::string, std::string> ret;
|
||||||
|
crashpad::SimpleStringDictionary::Iterator iter(*simple_string_dictionary_);
|
||||||
|
for(;;) {
|
||||||
|
const auto entry = iter.Next();
|
||||||
|
if (!entry) break;
|
||||||
|
ret[entry->key] = entry->value;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return upload_parameters_;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<CrashReporter::UploadReportResult>
|
std::vector<CrashReporter::UploadReportResult>
|
||||||
CrashReporterMac::GetUploadedReports(const base::FilePath& crashes_dir) {
|
CrashReporterMac::GetUploadedReports(const base::FilePath& crashes_dir) {
|
||||||
std::vector<CrashReporter::UploadReportResult> uploaded_reports;
|
std::vector<CrashReporter::UploadReportResult> uploaded_reports;
|
||||||
|
|
|
@ -75,11 +75,11 @@ bool ShowItemInFolder(const base::FilePath& full_path) {
|
||||||
if (!base::DirectoryExists(dir))
|
if (!base::DirectoryExists(dir))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return XDGOpen(dir.value(), true);
|
return XDGOpen(dir.value(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenItem(const base::FilePath& full_path) {
|
bool OpenItem(const base::FilePath& full_path) {
|
||||||
return XDGOpen(full_path.value(), true);
|
return XDGOpen(full_path.value(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenExternal(const GURL& url, bool activate) {
|
bool OpenExternal(const GURL& url, bool activate) {
|
||||||
|
|
|
@ -166,16 +166,12 @@ BrowserMainParts::~BrowserMainParts() {
|
||||||
|
|
||||||
#if defined(OS_WIN) || defined(OS_LINUX)
|
#if defined(OS_WIN) || defined(OS_LINUX)
|
||||||
void OverrideAppLogsPath() {
|
void OverrideAppLogsPath() {
|
||||||
#if defined(OS_WIN)
|
base::FilePath path;
|
||||||
std::wstring app_name = base::UTF8ToWide(GetApplicationName());
|
if (PathService::Get(brightray::DIR_APP_DATA, &path)) {
|
||||||
std::wstring log_path = L"%HOMEDRIVE%%HOMEPATH%\\AppData\\Roaming\\";
|
path = path.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName()));
|
||||||
std::wstring app_log_path = log_path + app_name + L"\\logs";
|
path = path.Append(base::FilePath::FromUTF8Unsafe("logs"));
|
||||||
#else
|
PathService::Override(DIR_APP_LOGS, path);
|
||||||
std::string app_name = GetApplicationName();
|
}
|
||||||
std::string home_path = std::string(getenv("HOME"));
|
|
||||||
std::string app_log_path = home_path + "/.config/" + app_name + "/logs";
|
|
||||||
#endif
|
|
||||||
PathService::Override(DIR_APP_LOGS, base::FilePath(app_log_path));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
namespace brightray {
|
namespace brightray {
|
||||||
|
|
||||||
|
int g_identifier_ = 1;
|
||||||
|
|
||||||
CocoaNotification::CocoaNotification(NotificationDelegate* delegate,
|
CocoaNotification::CocoaNotification(NotificationDelegate* delegate,
|
||||||
NotificationPresenter* presenter)
|
NotificationPresenter* presenter)
|
||||||
: Notification(delegate, presenter) {
|
: Notification(delegate, presenter) {
|
||||||
|
@ -29,6 +31,8 @@ void CocoaNotification::Show(const NotificationOptions& options) {
|
||||||
[notification_ setTitle:base::SysUTF16ToNSString(options.title)];
|
[notification_ setTitle:base::SysUTF16ToNSString(options.title)];
|
||||||
[notification_ setSubtitle:base::SysUTF16ToNSString(options.subtitle)];
|
[notification_ setSubtitle:base::SysUTF16ToNSString(options.subtitle)];
|
||||||
[notification_ setInformativeText:base::SysUTF16ToNSString(options.msg)];
|
[notification_ setInformativeText:base::SysUTF16ToNSString(options.msg)];
|
||||||
|
[notification_ setIdentifier:[NSString stringWithFormat:@"%s%d", "ElectronNotification", g_identifier_]];
|
||||||
|
g_identifier_++;
|
||||||
|
|
||||||
if ([notification_ respondsToSelector:@selector(setContentImage:)] &&
|
if ([notification_ respondsToSelector:@selector(setContentImage:)] &&
|
||||||
!options.icon.drawsNothing()) {
|
!options.icon.drawsNothing()) {
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
notification->NotificationReplied([notif.response.string UTF8String]);
|
notification->NotificationReplied([notif.response.string UTF8String]);
|
||||||
} else if (notif.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
|
} else if (notif.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
|
||||||
notification->NotificationButtonClicked();
|
notification->NotificationButtonClicked();
|
||||||
} else {
|
} else if (notif.activationType == NSUserNotificationActivationTypeContentsClicked) {
|
||||||
notification->NotificationClicked();
|
notification->NotificationClicked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ CocoaNotification* NotificationPresenterMac::GetNotification(
|
||||||
NSUserNotification* ns_notification) {
|
NSUserNotification* ns_notification) {
|
||||||
for (Notification* notification : notifications()) {
|
for (Notification* notification : notifications()) {
|
||||||
auto native_notification = static_cast<CocoaNotification*>(notification);
|
auto native_notification = static_cast<CocoaNotification*>(notification);
|
||||||
if ([native_notification->notification() isEqual:ns_notification])
|
if ([native_notification->notification().identifier
|
||||||
|
isEqual:ns_notification.identifier])
|
||||||
return native_notification;
|
return native_notification;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -27,8 +27,7 @@ for (let i = 0; i < argv.length; i++) {
|
||||||
} else if (argv[i] === '--default' || argv[i] === '-d') {
|
} else if (argv[i] === '--default' || argv[i] === '-d') {
|
||||||
option.default = true
|
option.default = true
|
||||||
break
|
break
|
||||||
} else if (argv[i] === '--interactive' || argv[i] === '-i' ||
|
} else if (argv[i] === '--interactive' || argv[i] === '-i' || argv[i] === '-repl') {
|
||||||
argv[i] === '-repl' || argv[i] === '-r') {
|
|
||||||
option.interactive = true
|
option.interactive = true
|
||||||
} else if (argv[i] === '--test-type=webdriver') {
|
} else if (argv[i] === '--test-type=webdriver') {
|
||||||
option.webdriver = true
|
option.webdriver = true
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
반드시 사용하는 Electron 버전과 문서 버전을 일치시켜야 합니다. 버전 숫자는 문서
|
반드시 사용하는 Electron 버전과 문서 버전을 일치시켜야 합니다. 버전 숫자는 문서
|
||||||
페이지 URL에 포함되어 있습니다. 만약 그렇지 않다면, 아마 개발 브랜치의 문서일
|
페이지 URL에 포함되어 있습니다. 만약 그렇지 않다면, 아마 개발 브랜치의 문서일
|
||||||
것이며 당신의 Electron 버전과 호환되지 않는 API 변경을 포함할 것 입니다.
|
것이며 당신의 Electron 버전과 호환되지 않는 API 변경을 포함할 것 입니다.
|
||||||
이전 버전 문서는 깃허브에서 [태그로 열어]
|
이전 버전 문서는 깃허브에서 [태그로 열어](https://github.com/electron/electron/tree/v1.4.0) 볼 수 있습니다.
|
||||||
(https://github.com/electron/electron/tree/v1.4.0) 볼 수 있습니다.
|
|
||||||
"branches/tags 변경" 드롭다운을 열고 해당 버전의 태그를 선택하세요.
|
"branches/tags 변경" 드롭다운을 열고 해당 버전의 태그를 선택하세요.
|
||||||
|
|
||||||
**역자주:** 한국어 번역 문서는 `atom.io`에 반영되어 있지 않습니다. 한국어 번역
|
**역자주:** 한국어 번역 문서는 `atom.io`에 반영되어 있지 않습니다. 한국어 번역
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
## 이슈 제출
|
## 이슈 제출
|
||||||
|
|
||||||
* [여기](https://github.com/electron/electron/issues/new)에서 새로운 이슈를 만들 수
|
* [여기](https://github.com/electron/electron/issues/new)에서 새로운 이슈를 만들 수
|
||||||
있습니다. 하지만 이슈를 작성하기 전에 아래의 항목들을 숙지하고 가능한한 이슈 보고에
|
있습니다. 하지만 이슈를 작성하기 전에 아래의 항목들을 숙지하고 가능한 한 이슈 보고에
|
||||||
대해 최대한 많은 정보와 자세한 설명을 포함해야 합니다. 가능하다면 다음 항목을 포함해야
|
대해 최대한 많은 정보와 자세한 설명을 포함해야 합니다. 가능하다면 다음 항목을 포함해야
|
||||||
합니다:
|
합니다:
|
||||||
* 사용하고 있는 Electron의 버전
|
* 사용하고 있는 Electron의 버전
|
||||||
|
@ -28,9 +28,9 @@
|
||||||
|
|
||||||
## Pull Request 하기
|
## Pull Request 하기
|
||||||
|
|
||||||
* 가능한한 스크린샷과 GIF 애니메이션 이미지를 pull request에 추가
|
* 가능한 한 스크린샷과 GIF 애니메이션 이미지를 pull request에 추가
|
||||||
* JavaScript, C++과 Python등
|
* JavaScript, C++과 Python등
|
||||||
[참조 문서에 정의된 코딩스타일](/docs-translations/ko-KR/development/coding-style.md)을
|
[참조 문서에 정의된 코딩 스타일](/docs-translations/ko-KR/development/coding-style.md)을
|
||||||
준수
|
준수
|
||||||
* [문서 스타일 가이드](/docs-translations/ko-KR/styleguide.md)에 따라 문서를
|
* [문서 스타일 가이드](/docs-translations/ko-KR/styleguide.md)에 따라 문서를
|
||||||
[Markdown](https://daringfireball.net/projects/markdown) 형식으로 작성.
|
[Markdown](https://daringfireball.net/projects/markdown) 형식으로 작성.
|
||||||
|
|
|
@ -42,7 +42,7 @@ npm install electron --save-dev
|
||||||
## 참조 문서
|
## 참조 문서
|
||||||
|
|
||||||
[Docs](https://github.com/electron/electron/tree/master/docs-translations/ko-KR/README.md)에
|
[Docs](https://github.com/electron/electron/tree/master/docs-translations/ko-KR/README.md)에
|
||||||
개발 지침과 API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝트에 기여하는법
|
개발 지침과 API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝트에 기여하는 법
|
||||||
또한 문서에 포함되어 있으니 참고하시기 바랍니다.
|
또한 문서에 포함되어 있으니 참고하시기 바랍니다.
|
||||||
|
|
||||||
## 참조 문서 (번역)
|
## 참조 문서 (번역)
|
||||||
|
|
|
@ -43,9 +43,9 @@ API 참고문서에는 이 규칙에 예외가 있습니다.
|
||||||
|
|
||||||
## 마크다운 규칙
|
## 마크다운 규칙
|
||||||
|
|
||||||
* 코드 블록에서 `cmd` 대신 `bash` 를 사용하세요. (문법 강조기 입니다)
|
* 코드 블록에서 `cmd` 대신 `bash` 를 사용해야합니다. (문법 강조기 때문입니다.)
|
||||||
* 한줄은 영문 80 자 길이로 맞춰야 합니다.
|
* 한줄은 영문 80 자 길이로 맞춰야 합니다.
|
||||||
* 2단계를 넘는 중첩 목록은 사용하지 마세요. (마크다운 렌더러 때문입니다)
|
* 2단계를 넘는 중첩 목록은 사용하면 안됩니다. (마크다운 렌더러 때문입니다.)
|
||||||
* 모든 `js` 와 `javascript` 는 [표준-마크다운](http://npm.im/standard-markdown)에
|
* 모든 `js` 와 `javascript` 는 [표준-마크다운](http://npm.im/standard-markdown)에
|
||||||
의해 분석됩니다.
|
의해 분석됩니다.
|
||||||
|
|
||||||
|
@ -158,8 +158,8 @@ API 참고문서에는 이 규칙에 예외가 있습니다.
|
||||||
|
|
||||||
제목은 `###` 또는 `####`-단계로 합니다. 모듈이냐 클래스냐에 따라 달라집니다.
|
제목은 `###` 또는 `####`-단계로 합니다. 모듈이냐 클래스냐에 따라 달라집니다.
|
||||||
|
|
||||||
모듈에서 `objectName` 은 모듕의 이름입니다. 클래스에서 그것은 클래스의
|
모듈의 경우, `objectName` 은 모듈의 이름입니다. 클래스의 경우, 클래스의
|
||||||
인스턴스의 이름이며, 모듈의 이름과 달라야합니다.
|
인스턴스 이름이어야 하며, 모듈의 이름과 달라야합니다.
|
||||||
|
|
||||||
예를 들어, `session` 모듈의 `Session` 클래스의 메소드는 `objectName` 으로
|
예를 들어, `session` 모듈의 `Session` 클래스의 메소드는 `objectName` 으로
|
||||||
`ses` 를 사용해야 합니다.
|
`ses` 를 사용해야 합니다.
|
||||||
|
|
|
@ -17,23 +17,23 @@ Bu proje katılımcı sözleşmesine bağlıdır. Katılarak,
|
||||||
bu kodun sürdürülebilir olduğunu üstlenmeniz beklenmekte.
|
bu kodun sürdürülebilir olduğunu üstlenmeniz beklenmekte.
|
||||||
Lütfen uygun olmayan davranışları electron@github.com'a rapor edin.
|
Lütfen uygun olmayan davranışları electron@github.com'a rapor edin.
|
||||||
|
|
||||||
## Downloads
|
## İndirme Bağlantıları
|
||||||
|
|
||||||
Electron prebuilt mimarisini yüklemek için,
|
Electron prebuilt mimarisini yüklemek için,
|
||||||
[`npm`](https://docs.npmjs.com/):
|
[`npm`](https://docs.npmjs.com/):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Development dependency olarak yükleyin
|
# Geliştirme bağımlılığı olarak yükleyin
|
||||||
npm install electron --save-dev
|
npm install electron --save-dev
|
||||||
|
|
||||||
# `electron` komutunu global olarak $PATH'a yükleyin
|
# `electron` komutunu global olarak $PATH'a yükleyin
|
||||||
npm install electron -g
|
npm install electron -g
|
||||||
```
|
```
|
||||||
|
|
||||||
Prebuilt mimarileri, debug sembolleri, ve fazlası için
|
Prebuilt mimarileri, debug sembolleri ve fazlası için
|
||||||
[releases page](https://github.com/electron/electron/releases) sayfasını ziyaret edin.
|
[releases](https://github.com/electron/electron/releases) sayfasını ziyaret edin.
|
||||||
|
|
||||||
### Mirrors
|
### Alternatif Bağlantılar
|
||||||
|
|
||||||
- [China](https://npm.taobao.org/mirrors/electron)
|
- [China](https://npm.taobao.org/mirrors/electron)
|
||||||
|
|
||||||
|
|
109
docs-translations/zh-CN/api/notification.md
Normal file
109
docs-translations/zh-CN/api/notification.md
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
# 通知
|
||||||
|
|
||||||
|
> 创建系统桌面通知
|
||||||
|
|
||||||
|
进程: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
|
## 在渲染器进程中使用
|
||||||
|
|
||||||
|
如果你想要从渲染器进程显示通知,则应使用 [HTML5 Notification API](../tutorial/notifications.md)
|
||||||
|
|
||||||
|
## 类: Notification
|
||||||
|
|
||||||
|
> 创建系统桌面通知
|
||||||
|
|
||||||
|
进程: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
|
`Notification` 是一个
|
||||||
|
[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter).
|
||||||
|
|
||||||
|
它通过由 `options ` 设置的原生属性创建一个新的 `Notification`.
|
||||||
|
|
||||||
|
### 静态方法
|
||||||
|
|
||||||
|
`Notification` 类具有以下静态方法:
|
||||||
|
|
||||||
|
#### `Notification.isSupported()`
|
||||||
|
|
||||||
|
返回 `Boolean` - 无论当前系统是否支持桌面通知
|
||||||
|
|
||||||
|
### `new Notification([options])` _实验性_
|
||||||
|
|
||||||
|
* `options` 对象
|
||||||
|
* `title` 字符串 - 通知的标题,显示在通知窗口的顶部.
|
||||||
|
* `subtitle` 字符串 - (可选) 通知的副标题,将显示在标题下方. _macOS_
|
||||||
|
* `body` 字符串 - 通知的正文,将显示在标题或副标题下方.
|
||||||
|
* `silent` 布尔 - (可选) 是否在显示通知时发出系统通知提示音.
|
||||||
|
* `icon` [NativeImage](native-image.md) - (可选) 通知所使用的图标
|
||||||
|
* `hasReply` 布尔 - (可选) 是否在通知中添加内联的回复选项. _macOS_
|
||||||
|
* `replyPlaceholder` 字符串 - (可选) 在内联输入字段中的提示占位符. _macOS_
|
||||||
|
* `sound` 字符串 - (可选) 显示通知时要播放的声音文件的名称. _macOS_
|
||||||
|
* `actions` [NotificationAction[]](structures/notification-action.md) - (可选) 添加到通知中的操作. 请阅读 `NotificationAction` 文档中的可用操作和限制. _macOS_
|
||||||
|
|
||||||
|
|
||||||
|
### 实例事件
|
||||||
|
|
||||||
|
使用 `new Notification` 创建的对象会发出以下事件:
|
||||||
|
|
||||||
|
**注意:** 某些事件仅在特定的操作系统上可用,请参照标签标示。
|
||||||
|
|
||||||
|
#### 事件: 'show'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
* `event` 事件
|
||||||
|
|
||||||
|
当向用户显示通知时发出. 注意这可以被多次触发, 因为通知可以通过 `show()` 方法多次显示.
|
||||||
|
|
||||||
|
#### 事件: 'click'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
* `event` 事件
|
||||||
|
|
||||||
|
当用户点击通知时发出.
|
||||||
|
|
||||||
|
#### 事件: 'close'
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
* `event` 事件
|
||||||
|
|
||||||
|
当用户手动关闭通知时发出.
|
||||||
|
|
||||||
|
在关闭所有通知的情况下,不能保证会发送此事件.
|
||||||
|
|
||||||
|
#### 事件: 'reply' _macOS_
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
* `event` 事件
|
||||||
|
* `reply` 字符串 - 用户输入到内联回复字段的字符串.
|
||||||
|
|
||||||
|
当用户点击 `hasReply: true` 的通知上的 “回复” 按钮时发出.
|
||||||
|
|
||||||
|
#### Event: 'action' _macOS_
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
* `event` 事件
|
||||||
|
* `index` Number - The index of the action that was activated
|
||||||
|
|
||||||
|
### 实例方法
|
||||||
|
|
||||||
|
使用 `new Notification` 创建的对象具有以下实例方法:
|
||||||
|
|
||||||
|
#### `notification.show()`
|
||||||
|
|
||||||
|
立即向用户显示通知. 请注意这不同于 HTML5 Notification 的实现, 简单地实例化一个 `new Notification` 不会立即向用户显示, 你需要在操作系统显示之前调用此方法.
|
||||||
|
|
||||||
|
### 播放声音
|
||||||
|
|
||||||
|
在 macOS 上, 你可以在显示通知时指定要播放的声音的名称. 除了自定义声音文件, 可以使用任意默认声音 (在 “系统偏好设置” > “声音” 下) . 同时确保声音文件被复制到应用程序包下 (例如`YourApp.app/Contents/Resources`), 或以下位置之一:
|
||||||
|
|
||||||
|
* `~/Library/Sounds`
|
||||||
|
* `/Library/Sounds`
|
||||||
|
* `/Network/Library/Sounds`
|
||||||
|
* `/System/Library/Sounds`
|
||||||
|
|
||||||
|
查看 [`NSSound`](https://developer.apple.com/documentation/appkit/nssound) 文档获取更多信息.
|
|
@ -0,0 +1,19 @@
|
||||||
|
# NotificationAction 对象
|
||||||
|
|
||||||
|
* `type` 字符串 - 动作的类型, 可以是 `button`.
|
||||||
|
* `text` 字符串 - (可选) 指定动作的标签.
|
||||||
|
|
||||||
|
## 平台 / 动作 支持
|
||||||
|
|
||||||
|
| 动作类型 | 平台支持 | `text`用法 | 默认 `text` | 限制 |
|
||||||
|
|-------------|------------------|-----------------|----------------|-------------|
|
||||||
|
| `button` | macOS | 用作按钮的标签 | "Show" | 最多只有一个按钮,如果仅提供多个,则仅使用最后一个按钮。 此操作也与 `hasReply` 不兼容,如果 `hasReply` 为 `true` ,将被忽略。 |
|
||||||
|
|
||||||
|
### macOS 上的按钮支持
|
||||||
|
|
||||||
|
为了让额外的通知按钮在 MacOS 上工作,您的应用程序必须符合以下条件。
|
||||||
|
|
||||||
|
* 应用程序已签名。
|
||||||
|
* 在 `info.plist` 中,将应用程序的 `NSUserNotificationAlertStyle` 设为 `alert` 。
|
||||||
|
|
||||||
|
如果这些要求中的任何一个都不符合,按钮就不会出现。
|
|
@ -173,7 +173,7 @@ required[, optional]
|
||||||
如果参数或方法对某些平台是唯一的,那么这些平台将使用数据类型后面的空格分隔的斜体列表来表示。 值可以是 `macOS`,`Windows` 或 `Linux`
|
如果参数或方法对某些平台是唯一的,那么这些平台将使用数据类型后面的空格分隔的斜体列表来表示。 值可以是 `macOS`,`Windows` 或 `Linux`
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
* `animate` Boolean (optional) _macOS_ _Windows_ - Animate the thing.
|
* `animate` Boolean (optional) _macOS_ _Windows_ - 进行动画处理的事情.
|
||||||
```
|
```
|
||||||
|
|
||||||
`Array` 类型的参数, 必须在指定数组下面的描述中描述可能包含的元素.
|
`Array` 类型的参数, 必须在指定数组下面的描述中描述可能包含的元素.
|
||||||
|
|
57
docs-translations/zh-CN/tutorial/notifications.md
Normal file
57
docs-translations/zh-CN/tutorial/notifications.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# 通知 (Windows, Linux, macOS)
|
||||||
|
|
||||||
|
所有三个操作系统都提供了应用程序向用户发送通知的手段。Electron允许开发者使用 [HTML5 Notification API](https://notifications.spec.whatwg.org/) 发送通知,并使用当前运行的操作系统的本地通知 API 来显示它。
|
||||||
|
|
||||||
|
**注意:** 由于这是一个 HTML5 API,它只能在渲染器进程中使用。 如果你想在主进程中显示通知,请查看 [Notification](../api/notification.md) 模块.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let myNotification = new Notification('标题', {
|
||||||
|
body: '通知正文内容'
|
||||||
|
})
|
||||||
|
|
||||||
|
myNotification.onclick = () => {
|
||||||
|
console.log('通知被点击')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
虽然操作系统的代码和用户体验相似,但依然存在微妙的差异。
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
* 在 Windows 10 上, 通知 "仅能运行".
|
||||||
|
* 在 Windows 8.1 和 Windows 8 上, 你的应用程序的快捷方式,[Application User Model ID][app-user-model-id],必须安装到 “开始” 屏幕。但是请注意,它不需要被固定到开始屏幕。
|
||||||
|
* 在 Windows 7 上, 通知通过视觉上类似于较新系统原生的一个自定义的实现来工作。
|
||||||
|
|
||||||
|
此外,在Windows 8中,通知正文的最大长度为250个字符,Windows团队建议将通知保留为200个字符。 然而,Windows 10中已经删除了这个限制,但是Windows团队要求开发人员合理使用。 尝试将大量文本发送到API(数千个字符)可能会导致不稳定。
|
||||||
|
|
||||||
|
### 高级通知
|
||||||
|
|
||||||
|
Windows 的更高版本允许高级通知,自定义模板,图像和其他灵活元素。 要发送这些通知(来自主进程或渲染器进程),请使用用户区模块 [electron-windows-notifications](https://github.com/felixrieseberg/electron-windows-notifications) 来用原生节点附件发送 `ToastNotification` 和 `TileNotification` 对象。
|
||||||
|
|
||||||
|
虽然包含按钮的通知只能使用 `electron-windows-notifications`,但处理回复则需要使用[`electron-windows-interactive-notifications`](https://github.com/felixrieseberg/electron-windows-interactive-notifications),这有助于注册所需的COM组件,并使用输入的用户数据调用Electron应用程序。
|
||||||
|
|
||||||
|
### 免打扰模式 / 演示模式
|
||||||
|
|
||||||
|
要检测是否允许发送通知,请使用用户区模块 [electron-notification-state](https://github.com/felixrieseberg/electron-notification-state)。
|
||||||
|
|
||||||
|
这样,您可以提前确定 Windows 是否会将通知忽略。
|
||||||
|
|
||||||
|
## macOS
|
||||||
|
|
||||||
|
macOS上的通知是最直接的,但你应该注意 [苹果关于通知的人机接口指南](https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/NotificationCenter.html).
|
||||||
|
|
||||||
|
请注意,通知的大小限制为256个字节,如果超过该限制,则会被截断。
|
||||||
|
|
||||||
|
### 高级通知
|
||||||
|
|
||||||
|
后来的 macOS 版本允许有一个输入字段的通知,允许用户快速回复通知。为了通过输入字段发送通知,请使用用户区模块 [node-mac-notifier](https://github.com/CharlieHess/node-mac-notifier)。
|
||||||
|
|
||||||
|
### 勿扰 / 会话状态
|
||||||
|
|
||||||
|
要检测是否允许发送通知,请使用用户区模块 [electron-notification-state](https://github.com/felixrieseberg/electron-notification-state).
|
||||||
|
|
||||||
|
这样可以提前检测是否显示通知。
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
通知使用 `libnotify` 发送,可以在 [Desktop Notifications Specification] [notification-spec] 之后的任何桌面环境中显示通知,包括 Cinnamon, Enlightenment, Unity, GNOME, KDE.
|
|
@ -543,7 +543,7 @@ bar, and on macOS you can visit it from dock menu.
|
||||||
|
|
||||||
Clears the recent documents list.
|
Clears the recent documents list.
|
||||||
|
|
||||||
### `app.setAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
|
### `app.setAsDefaultProtocolClient(protocol[, path, args])`
|
||||||
|
|
||||||
* `protocol` String - The name of your protocol, without `://`. If you want your
|
* `protocol` String - The name of your protocol, without `://`. If you want your
|
||||||
app to handle `electron://` links, call this method with `electron` as the
|
app to handle `electron://` links, call this method with `electron` as the
|
||||||
|
|
|
@ -200,7 +200,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
key is pressed. Default is `false`.
|
key is pressed. Default is `false`.
|
||||||
* `enableLargerThanScreen` Boolean (optional) - Enable the window to be resized larger
|
* `enableLargerThanScreen` Boolean (optional) - Enable the window to be resized larger
|
||||||
than screen. Default is `false`.
|
than screen. Default is `false`.
|
||||||
* `backgroundColor` String (optional) - Window's background color as Hexadecimal value,
|
* `backgroundColor` String (optional) - Window's background color as a hexadecimal value,
|
||||||
like `#66CD00` or `#FFF` or `#80FFFFFF` (alpha is supported). Default is
|
like `#66CD00` or `#FFF` or `#80FFFFFF` (alpha is supported). Default is
|
||||||
`#FFF` (white).
|
`#FFF` (white).
|
||||||
* `hasShadow` Boolean (optional) - Whether window should have a shadow. This is only
|
* `hasShadow` Boolean (optional) - Whether window should have a shadow. This is only
|
||||||
|
@ -322,7 +322,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
* `defaultEncoding` String (optional) - Defaults to `ISO-8859-1`.
|
* `defaultEncoding` String (optional) - Defaults to `ISO-8859-1`.
|
||||||
* `backgroundThrottling` Boolean (optional) - Whether to throttle animations and timers
|
* `backgroundThrottling` Boolean (optional) - Whether to throttle animations and timers
|
||||||
when the page becomes background. This also affects the
|
when the page becomes background. This also affects the
|
||||||
[Page Visibility API][#page-visibility]. Defaults to `true`.
|
[Page Visibility API](#page-visibility). Defaults to `true`.
|
||||||
* `offscreen` Boolean (optional) - Whether to enable offscreen rendering for the browser
|
* `offscreen` Boolean (optional) - Whether to enable offscreen rendering for the browser
|
||||||
window. Defaults to `false`. See the
|
window. Defaults to `false`. See the
|
||||||
[offscreen rendering tutorial](../tutorial/offscreen-rendering.md) for
|
[offscreen rendering tutorial](../tutorial/offscreen-rendering.md) for
|
||||||
|
@ -1443,6 +1443,10 @@ removed in future Electron releases.
|
||||||
|
|
||||||
* `browserView` [BrowserView](browser-view.md)
|
* `browserView` [BrowserView](browser-view.md)
|
||||||
|
|
||||||
|
#### `win.getBrowserView()` _Experimental_
|
||||||
|
|
||||||
|
Returns `BrowserView | null` - an attached BrowserView. Returns `null` if none is attached.
|
||||||
|
|
||||||
**Note:** The BrowserView API is currently experimental and may change or be
|
**Note:** The BrowserView API is currently experimental and may change or be
|
||||||
removed in future Electron releases.
|
removed in future Electron releases.
|
||||||
|
|
||||||
|
|
|
@ -116,18 +116,23 @@ called before `start` is called.
|
||||||
|
|
||||||
**Note:** This API can only be called from the main process.
|
**Note:** This API can only be called from the main process.
|
||||||
|
|
||||||
### `crashReporter.setExtraParameter(key, value)` _macOS_
|
### `crashReporter.addExtraParameter(key, value)` _macOS_
|
||||||
|
|
||||||
* `key` String - Parameter key, must be less than 64 characters long.
|
* `key` String - Parameter key, must be less than 64 characters long.
|
||||||
* `value` String - Parameter value, must be less than 64 characters long.
|
* `value` String - Parameter value, must be less than 64 characters long.
|
||||||
Specifying `null` or `undefined` will remove the key from the extra
|
|
||||||
parameters.
|
|
||||||
|
|
||||||
Set an extra parameter to be sent with the crash report. The values
|
Set an extra parameter to be sent with the crash report. The values
|
||||||
specified here will be sent in addition to any values set via the `extra` option
|
specified here will be sent in addition to any values set via the `extra` option when `start` was called. This API is only available on macOS, if you need to add/update extra parameters on Linux and Windows after your first call to `start` you can call `start` again with the updated `extra` options.
|
||||||
when `start` was called. This API is only available on macOS, if you need to
|
|
||||||
add/update extra parameters on Linux and Windows after your first call to
|
### `crashReporter.removeExtraParameter(key)` _macOS_
|
||||||
`start` you can call `start` again with the updated `extra` options.
|
|
||||||
|
* `key` String - Parameter key, must be less than 64 characters long.
|
||||||
|
|
||||||
|
Remove a extra parameter from the current set of parameters so that it will not be sent with the crash report.
|
||||||
|
|
||||||
|
### `crashReporter.getParameters()`
|
||||||
|
|
||||||
|
See all of the current parameters being passed to the crash reporter.
|
||||||
|
|
||||||
## Crash Report Payload
|
## Crash Report Payload
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,14 @@ and replies by setting `event.returnValue`.
|
||||||
**Note:** Sending a synchronous message will block the whole renderer process,
|
**Note:** Sending a synchronous message will block the whole renderer process,
|
||||||
unless you know what you are doing you should never use it.
|
unless you know what you are doing you should never use it.
|
||||||
|
|
||||||
|
### `ipcRenderer.sendTo(windowId, channel, [, arg1][, arg2][, ...])`
|
||||||
|
|
||||||
|
* `windowId` Number
|
||||||
|
* `channel` String
|
||||||
|
* `...args` any[]
|
||||||
|
|
||||||
|
Sends a message to a window with `windowid` via `channel`
|
||||||
|
|
||||||
### `ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])`
|
### `ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])`
|
||||||
|
|
||||||
* `channel` String
|
* `channel` String
|
||||||
|
|
|
@ -157,6 +157,34 @@ Returns `NativeImage`
|
||||||
|
|
||||||
Creates a new `NativeImage` instance from `dataURL`.
|
Creates a new `NativeImage` instance from `dataURL`.
|
||||||
|
|
||||||
|
### `nativeImage.createFromNamedImage(imageName[, hslShift])` _macOS_
|
||||||
|
|
||||||
|
* `imageName` String
|
||||||
|
* `hslShift` Number[]
|
||||||
|
|
||||||
|
Returns `NativeImage`
|
||||||
|
|
||||||
|
Creates a new `NativeImage` instance from the NSImage that maps to the
|
||||||
|
given image name. See [`NSImageName`](https://developer.apple.com/documentation/appkit/nsimagename?language=objc)
|
||||||
|
for a list of possible values.
|
||||||
|
|
||||||
|
The `hslShift` is applied to the image with the following rules
|
||||||
|
* `hsl_shift[0]` (hue): The absolute hue value for the image - 0 and 1 map
|
||||||
|
to 0 and 360 on the hue color wheel (red).
|
||||||
|
* `hsl_shift[1]` (saturation): A saturation shift for the image, with the
|
||||||
|
following key values:
|
||||||
|
0 = remove all color.
|
||||||
|
0.5 = leave unchanged.
|
||||||
|
1 = fully saturate the image.
|
||||||
|
* `hsl_shift[2]` (lightness): A lightness shift for the image, with the
|
||||||
|
following key values:
|
||||||
|
0 = remove all lightness (make all pixels black).
|
||||||
|
0.5 = leave unchanged.
|
||||||
|
1 = full lightness (make all pixels white).
|
||||||
|
|
||||||
|
This means that `[-1, 0, 1]` will make the image completely white and
|
||||||
|
`[-1, 1, 0]` will make the image completely black.
|
||||||
|
|
||||||
## Class: NativeImage
|
## Class: NativeImage
|
||||||
|
|
||||||
> Natively wrap images such as tray, dock, and application icons.
|
> Natively wrap images such as tray, dock, and application icons.
|
||||||
|
|
|
@ -34,7 +34,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||||
* `subtitle` String - (optional) A subtitle for the notification, which will be displayed below the title. _macOS_
|
* `subtitle` String - (optional) A subtitle for the notification, which will be displayed below the title. _macOS_
|
||||||
* `body` String - The body text of the notification, which will be displayed below the title or subtitle
|
* `body` String - The body text of the notification, which will be displayed below the title or subtitle
|
||||||
* `silent` Boolean - (optional) Whether or not to emit an OS notification noise when showing the notification
|
* `silent` Boolean - (optional) Whether or not to emit an OS notification noise when showing the notification
|
||||||
* `icon` [NativeImage](native-image.md) - (optional) An icon to use in the notification
|
* `icon` (String | [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_
|
* `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_
|
* `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_
|
* `sound` String - (optional) The name of the sound file to play when the notification is shown. _macOS_
|
||||||
|
@ -74,7 +74,7 @@ Returns:
|
||||||
|
|
||||||
Emitted when the notification is closed by manual intervention from the user.
|
Emitted when the notification is closed by manual intervention from the user.
|
||||||
|
|
||||||
This event is not guarunteed to be emitted in all cases where the notification
|
This event is not guaranteed to be emitted in all cases where the notification
|
||||||
is closed.
|
is closed.
|
||||||
|
|
||||||
#### Event: 'reply' _macOS_
|
#### Event: 'reply' _macOS_
|
||||||
|
@ -104,6 +104,13 @@ HTML5 Notification implementation, simply instantiating a `new Notification` doe
|
||||||
not immediately show it to the user, you need to call this method before the OS
|
not immediately show it to the user, you need to call this method before the OS
|
||||||
will display it.
|
will display it.
|
||||||
|
|
||||||
|
If the notification has been shown before, this method will dismiss the previously
|
||||||
|
shown notification and create a new one with identical properties.
|
||||||
|
|
||||||
|
#### `notification.close()`
|
||||||
|
|
||||||
|
Dismisses the notification.
|
||||||
|
|
||||||
### Playing Sounds
|
### Playing Sounds
|
||||||
|
|
||||||
On macOS, you can specify the name of the sound you'd like to play when the
|
On macOS, you can specify the name of the sound you'd like to play when the
|
||||||
|
|
|
@ -253,7 +253,8 @@ the original network configuration.
|
||||||
* `request` Object
|
* `request` Object
|
||||||
* `hostname` String
|
* `hostname` String
|
||||||
* `certificate` [Certificate](structures/certificate.md)
|
* `certificate` [Certificate](structures/certificate.md)
|
||||||
* `error` String - Verification result from chromium.
|
* `verificationResult` String - Verification result from chromium.
|
||||||
|
* `errorCode` Integer - Error code.
|
||||||
* `callback` Function
|
* `callback` Function
|
||||||
* `verificationResult` Integer - Value can be one of certificate error codes
|
* `verificationResult` Integer - Value can be one of certificate error codes
|
||||||
from [here](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
|
from [here](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
|
||||||
|
|
|
@ -36,7 +36,7 @@ Open the given file in the desktop's default manner.
|
||||||
|
|
||||||
### `shell.openExternal(url[, options, callback])`
|
### `shell.openExternal(url[, options, callback])`
|
||||||
|
|
||||||
* `url` String
|
* `url` String - max 2081 characters on windows, or the function returns false
|
||||||
* `options` Object (optional) _macOS_
|
* `options` Object (optional) _macOS_
|
||||||
* `activate` Boolean - `true` to bring the opened application to the
|
* `activate` Boolean - `true` to bring the opened application to the
|
||||||
foreground. The default is `true`.
|
foreground. The default is `true`.
|
||||||
|
|
|
@ -112,9 +112,9 @@ Same as `unsubscribeNotification`, but removes the subscriber from `NSNotificati
|
||||||
* `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`,
|
* `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`,
|
||||||
`url`, `array`, `dictionary`
|
`url`, `array`, `dictionary`
|
||||||
|
|
||||||
Returns `any` - The value of `key` in system preferences.
|
Returns `any` - The value of `key` in `NSUserDefaults`.
|
||||||
|
|
||||||
This API uses `NSUserDefaults` on macOS. Some popular `key` and `type`s are:
|
Some popular `key` and `type`s are:
|
||||||
|
|
||||||
* `AppleInterfaceStyle`: `string`
|
* `AppleInterfaceStyle`: `string`
|
||||||
* `AppleAquaColorVariant`: `integer`
|
* `AppleAquaColorVariant`: `integer`
|
||||||
|
@ -130,15 +130,22 @@ This API uses `NSUserDefaults` on macOS. Some popular `key` and `type`s are:
|
||||||
* `type` String - See [`getUserDefault`][#systempreferencesgetuserdefaultkey-type-macos]
|
* `type` String - See [`getUserDefault`][#systempreferencesgetuserdefaultkey-type-macos]
|
||||||
* `value` String
|
* `value` String
|
||||||
|
|
||||||
Set the value of `key` in system preferences.
|
Set the value of `key` in `NSUserDefaults`.
|
||||||
|
|
||||||
Note that `type` should match actual type of `value`. An exception is thrown
|
Note that `type` should match actual type of `value`. An exception is thrown
|
||||||
if they don't.
|
if they don't.
|
||||||
|
|
||||||
This API uses `NSUserDefaults` on macOS. Some popular `key` and `type`s are:
|
Some popular `key` and `type`s are:
|
||||||
|
|
||||||
* `ApplePressAndHoldEnabled`: `boolean`
|
* `ApplePressAndHoldEnabled`: `boolean`
|
||||||
|
|
||||||
|
### `systemPreferences.removeUserDefault(key)` _macOS_
|
||||||
|
|
||||||
|
* `key` String
|
||||||
|
|
||||||
|
Removes the `key` in `NSUserDefaults`. This can be used to restore the default
|
||||||
|
or global value of a `key` previously set with `setUserDefault`.
|
||||||
|
|
||||||
### `systemPreferences.isAeroGlassEnabled()` _Windows_
|
### `systemPreferences.isAeroGlassEnabled()` _Windows_
|
||||||
|
|
||||||
Returns `Boolean` - `true` if [DWM composition][dwm-composition] (Aero Glass) is
|
Returns `Boolean` - `true` if [DWM composition][dwm-composition] (Aero Glass) is
|
||||||
|
|
|
@ -245,8 +245,8 @@ win.on('hide', () => {
|
||||||
|
|
||||||
* `options` Object
|
* `options` Object
|
||||||
* `icon` ([NativeImage](native-image.md) | String) - (optional)
|
* `icon` ([NativeImage](native-image.md) | String) - (optional)
|
||||||
* `title` String - (optional)
|
* `title` String
|
||||||
* `content` String - (optional)
|
* `content` String
|
||||||
|
|
||||||
Displays a tray balloon.
|
Displays a tray balloon.
|
||||||
|
|
||||||
|
|
|
@ -581,6 +581,16 @@ that can't be set via `<webview>` attributes.
|
||||||
**Note:** The specified `preload` script option will be appear as `preloadURL`
|
**Note:** The specified `preload` script option will be appear as `preloadURL`
|
||||||
(not `preload`) in the `webPreferences` object emitted with this event.
|
(not `preload`) in the `webPreferences` object emitted with this event.
|
||||||
|
|
||||||
|
#### Event: 'did-attach-webview'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
* `event` Event
|
||||||
|
* `webContents` WebContents - The guest web contents that is used by the
|
||||||
|
`<webview>`.
|
||||||
|
|
||||||
|
Emitted when a `<webview>` has been attached to this web contents.
|
||||||
|
|
||||||
### Instance Methods
|
### Instance Methods
|
||||||
|
|
||||||
#### `contents.loadURL(url[, options])`
|
#### `contents.loadURL(url[, options])`
|
||||||
|
|
|
@ -6,7 +6,7 @@ Follow the guidelines below for building Electron on Windows.
|
||||||
|
|
||||||
* Windows 7 / Server 2008 R2 or higher
|
* Windows 7 / Server 2008 R2 or higher
|
||||||
* Visual Studio 2015 Update 3 - [download VS 2015 Community Edition for
|
* Visual Studio 2015 Update 3 - [download VS 2015 Community Edition for
|
||||||
free](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
|
free](https://www.visualstudio.com/vs/older-downloads/)
|
||||||
* [Python 2.7](http://www.python.org/download/releases/2.7/)
|
* [Python 2.7](http://www.python.org/download/releases/2.7/)
|
||||||
* [Node.js](http://nodejs.org/download/)
|
* [Node.js](http://nodejs.org/download/)
|
||||||
* [Git](http://git-scm.com)
|
* [Git](http://git-scm.com)
|
||||||
|
|
|
@ -2,47 +2,51 @@
|
||||||
|
|
||||||
This document describes the process for releasing a new version of Electron.
|
This document describes the process for releasing a new version of Electron.
|
||||||
|
|
||||||
## Find out what version change is needed
|
## Determine which branch to release from
|
||||||
Is this a major, minor, patch, or beta version change? Read the [Version Change Rules](docs/tutorial/electron-versioning.md#version-change-rules) to find out.
|
|
||||||
|
|
||||||
## Create a temporary branch
|
|
||||||
|
|
||||||
- **If releasing beta,** create a new branch from `master`.
|
- **If releasing beta,** create a new branch from `master`.
|
||||||
- **If releasing a stable version,** create a new branch from the beta branch you're stablizing.
|
- **If releasing a stable version,** create a new branch from the beta branch you're stabilizing.
|
||||||
|
|
||||||
Name the new branch `release` or anything you like.
|
## Find out what version change is needed
|
||||||
|
Run `npm run prepare-release -- --notesOnly` to view auto generated release
|
||||||
|
notes. The notes generated should help you determine if this is a major, minor,
|
||||||
|
patch, or beta version change. Read the
|
||||||
|
[Version Change Rules](../tutorial/electron-versioning.md#version-change-rules) for more information.
|
||||||
|
|
||||||
|
## Run the prepare-release script
|
||||||
|
The prepare release script will do the following:
|
||||||
|
1. Check if a release is already in process and if so it will halt.
|
||||||
|
2. Create a release branch.
|
||||||
|
3. Bump the version number in several files. See [this bump commit] for an example.
|
||||||
|
4. Create a draft release on GitHub with auto-generated release notes
|
||||||
|
5. Push the release branch so that the release builds get built.
|
||||||
|
Once you have determined which type of version change is needed, run the
|
||||||
|
`prepare-release` script with arguments according to your need:
|
||||||
|
- `[major|minor|patch|beta]` to increment one of the version numbers, or
|
||||||
|
- `--stable` to indicate this is a stable version
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
### Major version change
|
||||||
```sh
|
```sh
|
||||||
git checkout master
|
npm run prepare-release -- major
|
||||||
git pull
|
|
||||||
git checkout -b release
|
|
||||||
```
|
```
|
||||||
|
### Minor version change
|
||||||
This branch is created as a precaution to prevent any merged PRs from sneaking into a release between the time the temporary release branch is created and the CI builds are complete.
|
|
||||||
|
|
||||||
## Check for extant drafts
|
|
||||||
|
|
||||||
The upload script [looks for an existing draft release](https://github.com/electron/electron/blob/7961a97d7ddbed657c6c867cc8426e02c236c077/script/upload.py#L173-L181). To prevent your new release
|
|
||||||
from clobbering an existing draft, check [the releases page] and
|
|
||||||
make sure there are no drafts.
|
|
||||||
|
|
||||||
## Bump the version
|
|
||||||
|
|
||||||
Run the `bump-version` script with arguments according to your need:
|
|
||||||
- `--bump=[major|minor|patch|beta]` to increment one of the version numbers, or
|
|
||||||
- `--stable` to indicate this is a stable version, or
|
|
||||||
- `--version={version}` to set version number directly.
|
|
||||||
|
|
||||||
**Note**: you can use both `--bump` and `--stable` simultaneously.
|
|
||||||
|
|
||||||
There is also a `dry-run` flag you can use to make sure the version number generated is correct before committing.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run bump-version -- --bump=patch --stable
|
npm run prepare-release -- minor
|
||||||
git push origin HEAD
|
```
|
||||||
|
### Patch version change
|
||||||
|
```sh
|
||||||
|
npm run prepare-release -- patch
|
||||||
|
```
|
||||||
|
### Beta version change
|
||||||
|
```sh
|
||||||
|
npm run prepare-release -- beta
|
||||||
|
```
|
||||||
|
### Promote beta to stable
|
||||||
|
```sh
|
||||||
|
npm run prepare-release -- --stable
|
||||||
```
|
```
|
||||||
|
|
||||||
This will bump the version number in several files. See [this bump commit] for an example.
|
|
||||||
|
|
||||||
## Wait for builds :hourglass_flowing_sand:
|
## Wait for builds :hourglass_flowing_sand:
|
||||||
|
|
||||||
|
@ -159,65 +163,46 @@ This release is published to [npm](https://www.npmjs.com/package/electron) under
|
||||||
1. Uncheck the `prerelease` checkbox if you're publishing a stable release; leave it checked for beta releases.
|
1. Uncheck the `prerelease` checkbox if you're publishing a stable release; leave it checked for beta releases.
|
||||||
1. Click 'Save draft'. **Do not click 'Publish release'!**
|
1. Click 'Save draft'. **Do not click 'Publish release'!**
|
||||||
1. Wait for all builds to pass before proceeding.
|
1. Wait for all builds to pass before proceeding.
|
||||||
|
1. You can run `npm run release --validateRelease` to verify that all of the
|
||||||
|
required files have been created for the release.
|
||||||
|
|
||||||
## Merge temporary branch
|
## Merge temporary branch
|
||||||
|
Once the release builds have finished, merge the `release` branch back into
|
||||||
|
the source release branch using the `merge-release` script.
|
||||||
|
If the branch cannot be successfully merged back this script will automatically
|
||||||
|
rebase the `release` branch and push the changes which will trigger the release
|
||||||
|
builds again, which means you will need to wait for the release builds to run
|
||||||
|
again before proceeding.
|
||||||
|
|
||||||
Merge the temporary branch back into master, without creating a merge commit:
|
### Merging back into master
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git checkout master
|
npm run merge-release -- master
|
||||||
git merge release --no-commit
|
|
||||||
git push origin master
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If this fails, rebase with master and rebuild:
|
### Merging back into old release branch
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git pull
|
npm run merge-release -- 1-7-x
|
||||||
git checkout release
|
|
||||||
git rebase master
|
|
||||||
git push origin HEAD
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run local debug build
|
|
||||||
|
|
||||||
Run local debug build to verify that you are actually building the version you want. Sometimes you thought you were doing a release for a new version, but you're actually not.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run build
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify the window is displaying the current updated version.
|
|
||||||
|
|
||||||
## Set environment variables
|
|
||||||
|
|
||||||
You'll need to set the following environment variables to publish a release. Ask another team member for these credentials.
|
|
||||||
|
|
||||||
- `ELECTRON_S3_BUCKET`
|
|
||||||
- `ELECTRON_S3_ACCESS_KEY`
|
|
||||||
- `ELECTRON_S3_SECRET_KEY`
|
|
||||||
- `ELECTRON_GITHUB_TOKEN` - A personal access token with "repo" scope.
|
|
||||||
|
|
||||||
You will only need to do this once.
|
|
||||||
|
|
||||||
## Publish the release
|
## Publish the release
|
||||||
|
|
||||||
This script will download the binaries and generate the node headers and the .lib linker used on Windows by node-gyp to build native modules.
|
Once the merge has finished successfully, run the `release` script
|
||||||
|
via `npm run release` to finish the release process. This script will do the
|
||||||
|
following:
|
||||||
|
1. Build the project to validate that the correct version number is being released.
|
||||||
|
2. Download the binaries and generate the node headers and the .lib linker used
|
||||||
|
on Windows by node-gyp to build native modules.
|
||||||
|
3. Create and upload the SHASUMS files stored on S3 for the node files.
|
||||||
|
4. Create and upload the SHASUMS256.txt file stored on the GitHub release.
|
||||||
|
5. Validate that all of the required files are present on GitHub and S3 and have
|
||||||
|
the correct checksums as specified in the SHASUMS files.
|
||||||
|
6. Publish the release on GitHub
|
||||||
|
7. Delete the `release` branch.
|
||||||
|
|
||||||
```sh
|
## Publish to npm
|
||||||
npm run release
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Many distributions of Python still ship with old HTTPS certificates. You may see a `InsecureRequestWarning`, but it can be disregarded.
|
Once the publish is successful, run `npm run publish-to-npm` to publish to
|
||||||
|
release to npm.
|
||||||
## Delete the temporary branch
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git checkout master
|
|
||||||
git branch -D release # delete local branch
|
|
||||||
git push origin :release # delete remote branch
|
|
||||||
```
|
|
||||||
|
|
||||||
[the releases page]: https://github.com/electron/electron/releases
|
[the releases page]: https://github.com/electron/electron/releases
|
||||||
[this bump commit]: https://github.com/electron/electron/commit/78ec1b8f89b3886b856377a1756a51617bc33f5a
|
[this bump commit]: https://github.com/electron/electron/commit/78ec1b8f89b3886b856377a1756a51617bc33f5a
|
||||||
|
|
|
@ -91,9 +91,11 @@ free software licenses, and is a widely-used alternative to commercial
|
||||||
proprietary products like InstallShield. [electron-builder] supports NSIS
|
proprietary products like InstallShield. [electron-builder] supports NSIS
|
||||||
as a build target.
|
as a build target.
|
||||||
|
|
||||||
## OSR
|
### OSR
|
||||||
|
|
||||||
Off-screen rendering.
|
OSR (Off-screen rendering) can be used for loading heavy page in
|
||||||
|
background and then displaying it after (it will be much faster).
|
||||||
|
It allows you to render page without showing it on screen.
|
||||||
|
|
||||||
### process
|
### process
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ By using [`app.setAccessibilitySupportEnabled(enabled)`](https://electron.atom.i
|
||||||
|
|
||||||
Electron application will enable accessibility automatically when it detects assistive technology (Windows) or VoiceOver (macOS). See Chrome's [accessibility documentation](https://www.chromium.org/developers/design-documents/accessibility#TOC-How-Chrome-detects-the-presence-of-Assistive-Technology) for more details.
|
Electron application will enable accessibility automatically when it detects assistive technology (Windows) or VoiceOver (macOS). See Chrome's [accessibility documentation](https://www.chromium.org/developers/design-documents/accessibility#TOC-How-Chrome-detects-the-presence-of-Assistive-Technology) for more details.
|
||||||
|
|
||||||
On macOS, thrid-party assistive technology can switch accessibility inside Electron applications by setting the attribute `AXManualAccessibility` programmatically:
|
On macOS, third-party assistive technology can switch accessibility inside Electron applications by setting the attribute `AXManualAccessibility` programmatically:
|
||||||
|
|
||||||
```objc
|
```objc
|
||||||
CFStringRef kAXManualAccessibility = CFSTR("AXManualAccessibility");
|
CFStringRef kAXManualAccessibility = CFSTR("AXManualAccessibility");
|
||||||
|
|
|
@ -22,7 +22,7 @@ electron --inspect=5858 your/app
|
||||||
|
|
||||||
### `--inspect-brk=[port]`
|
### `--inspect-brk=[port]`
|
||||||
|
|
||||||
Like `--inspector` but pauses execution on the first line of JavaScript.
|
Like `--inspect` but pauses execution on the first line of JavaScript.
|
||||||
|
|
||||||
## External Debuggers
|
## External Debuggers
|
||||||
|
|
||||||
|
|
|
@ -85,3 +85,6 @@ Notifications are sent using `libnotify` which can show notifications on any
|
||||||
desktop environment that follows [Desktop Notifications
|
desktop environment that follows [Desktop Notifications
|
||||||
Specification][notification-spec], including Cinnamon, Enlightenment, Unity,
|
Specification][notification-spec], including Cinnamon, Enlightenment, Unity,
|
||||||
GNOME, KDE.
|
GNOME, KDE.
|
||||||
|
|
||||||
|
[notification-spec]: https://developer.gnome.org/notification-spec/
|
||||||
|
[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
# Online/Offline Event Detection
|
# Online/Offline Event Detection
|
||||||
|
|
||||||
Online and offline event detection can be implemented in the renderer process
|
[Online and offline event](https://developer.mozilla.org/en-US/docs/Online_and_offline_events) detection can be implemented in the renderer process using the [`navigator.onLine`](http://html5index.org/Offline%20-%20NavigatorOnLine.html) attribute, part of standard HTML5 API.
|
||||||
using standard HTML5 APIs, as shown in the following example.
|
The `navigator.onLine` attribute returns `false` if any network requests are guaranteed to fail i.e. definitely offline (disconnected from the network). It returns `true` in all other cases.
|
||||||
|
Since all other conditions return `true`, one has to be mindful of getting false positives, as we cannot assume `true` value necessarily means that Electron can access the internet. Such as in cases where the computer is running a virtualization software that has virtual ethernet adapters that are always “connected.”
|
||||||
|
Therefore, if you really want to determine the internet access status of Electron,
|
||||||
|
you should develop additional means for checking.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
_main.js_
|
_main.js_
|
||||||
|
|
||||||
|
@ -78,13 +83,3 @@ _online-status.html_
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
**NOTE:** If Electron is not able to connect to a local area network (LAN) or
|
|
||||||
a router, it is considered offline; all other conditions return `true`.
|
|
||||||
So while you can assume that Electron is offline when `navigator.onLine`
|
|
||||||
returns a `false` value, you cannot assume that a `true` value necessarily
|
|
||||||
means that Electron can access the internet. You could be getting false
|
|
||||||
positives, such as in cases where the computer is running a virtualization
|
|
||||||
software that has virtual ethernet adapters that are always "connected."
|
|
||||||
Therefore, if you really want to determine the internet access status of Electron,
|
|
||||||
you should develop additional means for checking.
|
|
||||||
|
|
|
@ -187,6 +187,12 @@ $ ./node_modules/.bin/electron .
|
||||||
$ .\node_modules\.bin\electron .
|
$ .\node_modules\.bin\electron .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Node v8.2.0 and later
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npx electron .
|
||||||
|
```
|
||||||
|
|
||||||
### Manually Downloaded Electron Binary
|
### Manually Downloaded Electron Binary
|
||||||
|
|
||||||
If you downloaded Electron manually, you can also use the included
|
If you downloaded Electron manually, you can also use the included
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
'product_name%': 'Electron',
|
'product_name%': 'Electron',
|
||||||
'company_name%': 'GitHub, Inc',
|
'company_name%': 'GitHub, Inc',
|
||||||
'company_abbr%': 'github',
|
'company_abbr%': 'github',
|
||||||
'version%': '1.8.1',
|
'version%': '1.8.2-beta.2',
|
||||||
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
||||||
},
|
},
|
||||||
'includes': [
|
'includes': [
|
||||||
|
|
|
@ -10,7 +10,9 @@ const electron = require('electron')
|
||||||
const {deprecate, Menu} = electron
|
const {deprecate, Menu} = electron
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
|
|
||||||
|
// App is an EventEmitter.
|
||||||
Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(app)
|
||||||
|
|
||||||
Object.assign(app, {
|
Object.assign(app, {
|
||||||
setApplicationMenu (menu) {
|
setApplicationMenu (menu) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const EventEmitter = require('events').EventEmitter
|
const EventEmitter = require('events').EventEmitter
|
||||||
const {autoUpdater, AutoUpdater} = process.atomBinding('auto_updater')
|
const {autoUpdater, AutoUpdater} = process.atomBinding('auto_updater')
|
||||||
|
|
||||||
|
// AutoUpdater is an EventEmitter.
|
||||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(autoUpdater)
|
||||||
|
|
||||||
module.exports = autoUpdater
|
module.exports = autoUpdater
|
||||||
|
|
|
@ -5,139 +5,39 @@ const EventEmitter = require('events').EventEmitter
|
||||||
const v8Util = process.atomBinding('v8_util')
|
const v8Util = process.atomBinding('v8_util')
|
||||||
const bindings = process.atomBinding('menu')
|
const bindings = process.atomBinding('menu')
|
||||||
|
|
||||||
// Automatically generated radio menu item's group id.
|
const {Menu} = bindings
|
||||||
var nextGroupId = 0
|
let applicationMenu = null
|
||||||
|
let groupIdIndex = 0
|
||||||
// Search between separators to find a radio menu item and return its group id,
|
|
||||||
// otherwise generate a group id.
|
|
||||||
var generateGroupId = function (items, pos) {
|
|
||||||
var i, item, j, k, ref1, ref2, ref3
|
|
||||||
if (pos > 0) {
|
|
||||||
for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
|
|
||||||
item = items[i]
|
|
||||||
if (item.type === 'radio') {
|
|
||||||
return item.groupId
|
|
||||||
}
|
|
||||||
if (item.type === 'separator') {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (pos < items.length) {
|
|
||||||
for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
|
|
||||||
item = items[i]
|
|
||||||
if (item.type === 'radio') {
|
|
||||||
return item.groupId
|
|
||||||
}
|
|
||||||
if (item.type === 'separator') {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ++nextGroupId
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index of item according to |id|.
|
|
||||||
var indexOfItemById = function (items, id) {
|
|
||||||
var i, item, j, len
|
|
||||||
for (i = j = 0, len = items.length; j < len; i = ++j) {
|
|
||||||
item = items[i]
|
|
||||||
if (item.id === id) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index of where to insert the item according to |position|.
|
|
||||||
var indexToInsertByPosition = function (items, position) {
|
|
||||||
var insertIndex
|
|
||||||
if (!position) {
|
|
||||||
return items.length
|
|
||||||
}
|
|
||||||
const [query, id] = position.split('=')
|
|
||||||
insertIndex = indexOfItemById(items, id)
|
|
||||||
if (insertIndex === -1 && query !== 'endof') {
|
|
||||||
console.warn("Item with id '" + id + "' is not found")
|
|
||||||
return items.length
|
|
||||||
}
|
|
||||||
switch (query) {
|
|
||||||
case 'after':
|
|
||||||
insertIndex++
|
|
||||||
break
|
|
||||||
case 'endof':
|
|
||||||
|
|
||||||
// If the |id| doesn't exist, then create a new group with the |id|.
|
|
||||||
if (insertIndex === -1) {
|
|
||||||
items.push({
|
|
||||||
id: id,
|
|
||||||
type: 'separator'
|
|
||||||
})
|
|
||||||
insertIndex = items.length - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the end of the group.
|
|
||||||
insertIndex++
|
|
||||||
while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
|
|
||||||
insertIndex++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return insertIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
const Menu = bindings.Menu
|
|
||||||
|
|
||||||
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
|
||||||
|
|
||||||
|
/* Instance Methods */
|
||||||
|
|
||||||
Menu.prototype._init = function () {
|
Menu.prototype._init = function () {
|
||||||
this.commandsMap = {}
|
this.commandsMap = {}
|
||||||
this.groupsMap = {}
|
this.groupsMap = {}
|
||||||
this.items = []
|
this.items = []
|
||||||
this.delegate = {
|
this.delegate = {
|
||||||
isCommandIdChecked: (commandId) => {
|
isCommandIdChecked: id => this.commandsMap[id] ? this.commandsMap[id].checked : undefined,
|
||||||
var command = this.commandsMap[commandId]
|
isCommandIdEnabled: id => this.commandsMap[id] ? this.commandsMap[id].enabled : undefined,
|
||||||
return command != null ? command.checked : undefined
|
isCommandIdVisible: id => this.commandsMap[id] ? this.commandsMap[id].visible : undefined,
|
||||||
},
|
getAcceleratorForCommandId: (id, useDefaultAccelerator) => {
|
||||||
isCommandIdEnabled: (commandId) => {
|
const command = this.commandsMap[id]
|
||||||
var command = this.commandsMap[commandId]
|
if (!command) return
|
||||||
return command != null ? command.enabled : undefined
|
if (command.accelerator) return command.accelerator
|
||||||
},
|
|
||||||
isCommandIdVisible: (commandId) => {
|
|
||||||
var command = this.commandsMap[commandId]
|
|
||||||
return command != null ? command.visible : undefined
|
|
||||||
},
|
|
||||||
getAcceleratorForCommandId: (commandId, useDefaultAccelerator) => {
|
|
||||||
const command = this.commandsMap[commandId]
|
|
||||||
if (command == null) return
|
|
||||||
if (command.accelerator != null) return command.accelerator
|
|
||||||
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
|
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
|
||||||
},
|
},
|
||||||
getIconForCommandId: (commandId) => {
|
getIconForCommandId: id => this.commandsMap[id] ? this.commandsMap[id].icon : undefined,
|
||||||
var command = this.commandsMap[commandId]
|
executeCommand: (event, id) => {
|
||||||
return command != null ? command.icon : undefined
|
const command = this.commandsMap[id]
|
||||||
},
|
if (!command) return
|
||||||
executeCommand: (event, commandId) => {
|
|
||||||
const command = this.commandsMap[commandId]
|
|
||||||
if (command == null) return
|
|
||||||
command.click(event, BrowserWindow.getFocusedWindow(), webContents.getFocusedWebContents())
|
command.click(event, BrowserWindow.getFocusedWindow(), webContents.getFocusedWebContents())
|
||||||
},
|
},
|
||||||
menuWillShow: () => {
|
menuWillShow: () => {
|
||||||
// Make sure radio groups have at least one menu item seleted.
|
// Ensure radio groups have at least one menu item seleted
|
||||||
var checked, group, id, j, len, radioItem, ref1
|
for (const id in this.groupsMap) {
|
||||||
ref1 = this.groupsMap
|
const found = this.groupsMap[id].find(item => item.checked) || null
|
||||||
for (id in ref1) {
|
if (!found) v8Util.setHiddenValue(this.groupsMap[id][0], 'checked', true)
|
||||||
group = ref1[id]
|
|
||||||
checked = false
|
|
||||||
for (j = 0, len = group.length; j < len; j++) {
|
|
||||||
radioItem = group[j]
|
|
||||||
if (!radioItem.checked) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
checked = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (!checked) {
|
|
||||||
v8Util.setHiddenValue(group[0], 'checked', true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,55 +45,47 @@ Menu.prototype._init = function () {
|
||||||
|
|
||||||
Menu.prototype.popup = function (window, x, y, positioningItem) {
|
Menu.prototype.popup = function (window, x, y, positioningItem) {
|
||||||
let asyncPopup
|
let asyncPopup
|
||||||
|
let [newX, newY, newPosition, newWindow] = [x, y, positioningItem, window]
|
||||||
|
|
||||||
// menu.popup(x, y, positioningItem)
|
// menu.popup(x, y, positioningItem)
|
||||||
if (window != null && (typeof window !== 'object' || window.constructor !== BrowserWindow)) {
|
if (!window) {
|
||||||
// Shift.
|
// shift over values
|
||||||
positioningItem = y
|
if (typeof window !== 'object' || window.constructor !== BrowserWindow) {
|
||||||
y = x
|
[newPosition, newY, newX, newWindow] = [y, x, window, null]
|
||||||
x = window
|
}
|
||||||
window = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// menu.popup(window, {})
|
// menu.popup(window, {})
|
||||||
if (x != null && typeof x === 'object') {
|
if (x && typeof x === 'object') {
|
||||||
const options = x
|
const opts = x
|
||||||
x = options.x
|
newX = opts.x
|
||||||
y = options.y
|
newY = opts.y
|
||||||
positioningItem = options.positioningItem
|
newPosition = opts.positioningItem
|
||||||
asyncPopup = options.async
|
asyncPopup = opts.async
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to showing in focused window.
|
// set defaults
|
||||||
if (window == null) window = BrowserWindow.getFocusedWindow()
|
if (typeof x !== 'number') newX = -1
|
||||||
|
if (typeof y !== 'number') newY = -1
|
||||||
// Default to showing under mouse location.
|
if (typeof positioningItem !== 'number') newPosition = -1
|
||||||
if (typeof x !== 'number') x = -1
|
if (!window) newWindow = BrowserWindow.getFocusedWindow()
|
||||||
if (typeof y !== 'number') y = -1
|
|
||||||
|
|
||||||
// Default to not highlighting any item.
|
|
||||||
if (typeof positioningItem !== 'number') positioningItem = -1
|
|
||||||
|
|
||||||
// Default to synchronous for backwards compatibility.
|
|
||||||
if (typeof asyncPopup !== 'boolean') asyncPopup = false
|
if (typeof asyncPopup !== 'boolean') asyncPopup = false
|
||||||
|
|
||||||
this.popupAt(window, x, y, positioningItem, asyncPopup)
|
this.popupAt(newWindow, newX, newY, newPosition, asyncPopup)
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.prototype.closePopup = function (window) {
|
Menu.prototype.closePopup = function (window) {
|
||||||
if (window == null || window.constructor !== BrowserWindow) {
|
if (!window || window.constructor !== BrowserWindow) {
|
||||||
window = BrowserWindow.getFocusedWindow()
|
window = BrowserWindow.getFocusedWindow()
|
||||||
}
|
}
|
||||||
if (window != null) {
|
if (window) this.closePopupAt(window.id)
|
||||||
this.closePopupAt(window.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.prototype.getMenuItemById = function (id) {
|
Menu.prototype.getMenuItemById = function (id) {
|
||||||
const items = this.items
|
const items = this.items
|
||||||
|
|
||||||
let found = items.find(item => item.id === id) || null
|
let found = items.find(item => item.id === id) || null
|
||||||
for (let i = 0, length = items.length; !found && i < length; i++) {
|
for (let i = 0; !found && i < items.length; i++) {
|
||||||
if (items[i].submenu) {
|
if (items[i].submenu) {
|
||||||
found = items[i].submenu.getMenuItemById(id)
|
found = items[i].submenu.getMenuItemById(id)
|
||||||
}
|
}
|
||||||
|
@ -206,61 +98,17 @@ Menu.prototype.append = function (item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.prototype.insert = function (pos, item) {
|
Menu.prototype.insert = function (pos, item) {
|
||||||
var base, name
|
if ((item ? item.constructor : void 0) !== MenuItem) {
|
||||||
if ((item != null ? item.constructor : void 0) !== MenuItem) {
|
|
||||||
throw new TypeError('Invalid item')
|
throw new TypeError('Invalid item')
|
||||||
}
|
}
|
||||||
switch (item.type) {
|
|
||||||
case 'normal':
|
|
||||||
this.insertItem(pos, item.commandId, item.label)
|
|
||||||
break
|
|
||||||
case 'checkbox':
|
|
||||||
this.insertCheckItem(pos, item.commandId, item.label)
|
|
||||||
break
|
|
||||||
case 'separator':
|
|
||||||
this.insertSeparator(pos)
|
|
||||||
break
|
|
||||||
case 'submenu':
|
|
||||||
this.insertSubMenu(pos, item.commandId, item.label, item.submenu)
|
|
||||||
break
|
|
||||||
case 'radio':
|
|
||||||
// Grouping radio menu items.
|
|
||||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
|
|
||||||
if ((base = this.groupsMap)[name = item.groupId] == null) {
|
|
||||||
base[name] = []
|
|
||||||
}
|
|
||||||
this.groupsMap[item.groupId].push(item)
|
|
||||||
|
|
||||||
// Setting a radio menu item should flip other items in the group.
|
// insert item depending on its type
|
||||||
v8Util.setHiddenValue(item, 'checked', item.checked)
|
insertItemByType.call(this, item, pos)
|
||||||
Object.defineProperty(item, 'checked', {
|
|
||||||
enumerable: true,
|
// set item properties
|
||||||
get: function () {
|
if (item.sublabel) this.setSublabel(pos, item.sublabel)
|
||||||
return v8Util.getHiddenValue(item, 'checked')
|
if (item.icon) this.setIcon(pos, item.icon)
|
||||||
},
|
if (item.role) this.setRole(pos, item.role)
|
||||||
set: () => {
|
|
||||||
var j, len, otherItem, ref1
|
|
||||||
ref1 = this.groupsMap[item.groupId]
|
|
||||||
for (j = 0, len = ref1.length; j < len; j++) {
|
|
||||||
otherItem = ref1[j]
|
|
||||||
if (otherItem !== item) {
|
|
||||||
v8Util.setHiddenValue(otherItem, 'checked', false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v8Util.setHiddenValue(item, 'checked', true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
|
|
||||||
}
|
|
||||||
if (item.sublabel != null) {
|
|
||||||
this.setSublabel(pos, item.sublabel)
|
|
||||||
}
|
|
||||||
if (item.icon != null) {
|
|
||||||
this.setIcon(pos, item.icon)
|
|
||||||
}
|
|
||||||
if (item.role != null) {
|
|
||||||
this.setRole(pos, item.role)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make menu accessable to items.
|
// Make menu accessable to items.
|
||||||
item.overrideReadOnlyProperty('menu', this)
|
item.overrideReadOnlyProperty('menu', this)
|
||||||
|
@ -270,73 +118,150 @@ Menu.prototype.insert = function (pos, item) {
|
||||||
this.commandsMap[item.commandId] = item
|
this.commandsMap[item.commandId] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force menuWillShow to be called
|
|
||||||
Menu.prototype._callMenuWillShow = function () {
|
Menu.prototype._callMenuWillShow = function () {
|
||||||
if (this.delegate != null) {
|
if (this.delegate) this.delegate.menuWillShow()
|
||||||
this.delegate.menuWillShow()
|
this.items.forEach(item => {
|
||||||
}
|
if (item.submenu) item.submenu._callMenuWillShow()
|
||||||
this.items.forEach(function (item) {
|
|
||||||
if (item.submenu != null) {
|
|
||||||
item.submenu._callMenuWillShow()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var applicationMenu = null
|
/* Static Methods */
|
||||||
|
|
||||||
Menu.setApplicationMenu = function (menu) {
|
Menu.getApplicationMenu = () => applicationMenu
|
||||||
if (!(menu === null || menu.constructor === Menu)) {
|
|
||||||
throw new TypeError('Invalid menu')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep a reference.
|
|
||||||
applicationMenu = menu
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
if (menu === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
menu._callMenuWillShow()
|
|
||||||
bindings.setApplicationMenu(menu)
|
|
||||||
} else {
|
|
||||||
BrowserWindow.getAllWindows().forEach(function (window) {
|
|
||||||
window.setMenu(menu)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu.getApplicationMenu = function () {
|
|
||||||
return applicationMenu
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
||||||
|
|
||||||
|
// set application menu with a preexisting menu
|
||||||
|
Menu.setApplicationMenu = function (menu) {
|
||||||
|
if (!(menu || menu.constructor === Menu)) {
|
||||||
|
throw new TypeError('Invalid menu')
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationMenu = menu
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
if (!menu) return
|
||||||
|
menu._callMenuWillShow()
|
||||||
|
bindings.setApplicationMenu(menu)
|
||||||
|
} else {
|
||||||
|
const windows = BrowserWindow.getAllWindows()
|
||||||
|
return windows.map(w => w.setMenu(menu))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Menu.buildFromTemplate = function (template) {
|
Menu.buildFromTemplate = function (template) {
|
||||||
var insertIndex, item, j, k, len, len1, menu, menuItem, positionedTemplate
|
if (!(template instanceof Array)) {
|
||||||
if (!Array.isArray(template)) {
|
|
||||||
throw new TypeError('Invalid template for Menu')
|
throw new TypeError('Invalid template for Menu')
|
||||||
}
|
}
|
||||||
positionedTemplate = []
|
|
||||||
insertIndex = 0
|
const menu = new Menu()
|
||||||
for (j = 0, len = template.length; j < len; j++) {
|
const positioned = []
|
||||||
item = template[j]
|
let idx = 0
|
||||||
if (item.position) {
|
|
||||||
insertIndex = indexToInsertByPosition(positionedTemplate, item.position)
|
// sort template by position
|
||||||
} else {
|
template.forEach(item => {
|
||||||
// If no |position| is specified, insert after last item.
|
idx = (item.position) ? indexToInsertByPosition(positioned, item.position) : idx += 1
|
||||||
insertIndex++
|
positioned.splice(idx, 0, item)
|
||||||
}
|
})
|
||||||
positionedTemplate.splice(insertIndex, 0, item)
|
|
||||||
}
|
// add each item from positioned menu to application menu
|
||||||
menu = new Menu()
|
positioned.forEach((item) => {
|
||||||
for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
|
|
||||||
item = positionedTemplate[k]
|
|
||||||
if (typeof item !== 'object') {
|
if (typeof item !== 'object') {
|
||||||
throw new TypeError('Invalid template for MenuItem')
|
throw new TypeError('Invalid template for MenuItem')
|
||||||
}
|
}
|
||||||
menuItem = new MenuItem(item)
|
menu.append(new MenuItem(item))
|
||||||
menu.append(menuItem)
|
})
|
||||||
}
|
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper Functions */
|
||||||
|
|
||||||
|
// Search between separators to find a radio menu item and return its group id
|
||||||
|
function generateGroupId (items, pos) {
|
||||||
|
if (pos > 0) {
|
||||||
|
for (let idx = pos - 1; idx >= 0; idx--) {
|
||||||
|
if (items[idx].type === 'radio') return items[idx].groupId
|
||||||
|
if (items[idx].type === 'separator') break
|
||||||
|
}
|
||||||
|
} else if (pos < items.length) {
|
||||||
|
for (let idx = pos; idx <= items.length - 1; idx++) {
|
||||||
|
if (items[idx].type === 'radio') return items[idx].groupId
|
||||||
|
if (items[idx].type === 'separator') break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groupIdIndex += 1
|
||||||
|
return groupIdIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexOfItemById (items, id) {
|
||||||
|
const foundItem = items.find(item => item.id === id) || -1
|
||||||
|
return items.indexOf(foundItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexToInsertByPosition (items, position) {
|
||||||
|
if (!position) return items.length
|
||||||
|
|
||||||
|
const [query, id] = position.split('=') // parse query and id from position
|
||||||
|
const idx = indexOfItemById(items, id) // calculate initial index of item
|
||||||
|
|
||||||
|
// warn if query doesn't exist
|
||||||
|
if (idx === -1 && query !== 'endof') {
|
||||||
|
console.warn(`Item with id ${id} is not found`)
|
||||||
|
return items.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute new index based on query
|
||||||
|
const queries = {
|
||||||
|
after: (index) => {
|
||||||
|
index += 1
|
||||||
|
return index
|
||||||
|
},
|
||||||
|
endof: (index) => {
|
||||||
|
if (index === -1) {
|
||||||
|
items.push({id, type: 'separator'})
|
||||||
|
index = items.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
while (index < items.length && items[index].type !== 'separator') index += 1
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return new index if needed, or original indexOfItemById
|
||||||
|
return (query in queries) ? queries[query](idx) : idx
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertItemByType (item, pos) {
|
||||||
|
const types = {
|
||||||
|
normal: () => this.insertItem(pos, item.commandId, item.label),
|
||||||
|
checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
|
||||||
|
separator: () => this.insertSeparator(pos),
|
||||||
|
submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
|
||||||
|
radio: () => {
|
||||||
|
// Grouping radio menu items
|
||||||
|
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
|
||||||
|
if (this.groupsMap[item.groupId] == null) {
|
||||||
|
this.groupsMap[item.groupId] = []
|
||||||
|
}
|
||||||
|
this.groupsMap[item.groupId].push(item)
|
||||||
|
|
||||||
|
// Setting a radio menu item should flip other items in the group.
|
||||||
|
v8Util.setHiddenValue(item, 'checked', item.checked)
|
||||||
|
Object.defineProperty(item, 'checked', {
|
||||||
|
enumerable: true,
|
||||||
|
get: () => v8Util.getHiddenValue(item, 'checked'),
|
||||||
|
set: () => {
|
||||||
|
this.groupsMap[item.groupId].forEach(other => {
|
||||||
|
if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
|
||||||
|
})
|
||||||
|
v8Util.setHiddenValue(item, 'checked', true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types[item.type]()
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Menu
|
module.exports = Menu
|
||||||
|
|
|
@ -9,7 +9,10 @@ const {Session} = process.atomBinding('session')
|
||||||
const {net, Net} = process.atomBinding('net')
|
const {net, Net} = process.atomBinding('net')
|
||||||
const {URLRequest} = net
|
const {URLRequest} = net
|
||||||
|
|
||||||
|
// Net is an EventEmitter.
|
||||||
Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(Net.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(net)
|
||||||
|
|
||||||
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
|
||||||
|
|
||||||
const kSupportedProtocols = new Set(['http:', 'https:'])
|
const kSupportedProtocols = new Set(['http:', 'https:'])
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
const {powerMonitor, PowerMonitor} = process.atomBinding('power_monitor')
|
const {powerMonitor, PowerMonitor} = process.atomBinding('power_monitor')
|
||||||
|
|
||||||
|
// PowerMonitor is an EventEmitter.
|
||||||
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(powerMonitor)
|
||||||
|
|
||||||
module.exports = powerMonitor
|
module.exports = powerMonitor
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
const {screen, Screen} = process.atomBinding('screen')
|
const {screen, Screen} = process.atomBinding('screen')
|
||||||
|
|
||||||
|
// Screen is an EventEmitter.
|
||||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(screen)
|
||||||
|
|
||||||
module.exports = screen
|
module.exports = screen
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
const {systemPreferences, SystemPreferences} = process.atomBinding('system_preferences')
|
const {systemPreferences, SystemPreferences} = process.atomBinding('system_preferences')
|
||||||
|
|
||||||
|
// SystemPreferences is an EventEmitter.
|
||||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(systemPreferences)
|
||||||
|
|
||||||
module.exports = systemPreferences
|
module.exports = systemPreferences
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const ipcMain = require('electron').ipcMain
|
const {ipcMain} = require('electron')
|
||||||
const desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer
|
const {desktopCapturer} = process.atomBinding('desktop_capturer')
|
||||||
|
|
||||||
var deepEqual = function (opt1, opt2) {
|
const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b)
|
||||||
return JSON.stringify(opt1) === JSON.stringify(opt2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A queue for holding all requests from renderer process.
|
// A queue for holding all requests from renderer process.
|
||||||
var requestsQueue = []
|
let requestsQueue = []
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, captureWindow, captureScreen, thumbnailSize, id) {
|
const electronSources = 'ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES'
|
||||||
var request
|
const capturerResult = (id) => `ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`
|
||||||
request = {
|
|
||||||
id: id,
|
ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, id) => {
|
||||||
|
const request = {
|
||||||
|
id,
|
||||||
options: {
|
options: {
|
||||||
captureWindow: captureWindow,
|
captureWindow,
|
||||||
captureScreen: captureScreen,
|
captureScreen,
|
||||||
thumbnailSize: thumbnailSize
|
thumbnailSize
|
||||||
},
|
},
|
||||||
webContents: event.sender
|
webContents: event.sender
|
||||||
}
|
}
|
||||||
|
@ -28,45 +28,38 @@ ipcMain.on('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, cap
|
||||||
|
|
||||||
// If the WebContents is destroyed before receiving result, just remove the
|
// If the WebContents is destroyed before receiving result, just remove the
|
||||||
// reference from requestsQueue to make the module not send the result to it.
|
// reference from requestsQueue to make the module not send the result to it.
|
||||||
event.sender.once('destroyed', function () {
|
event.sender.once('destroyed', () => {
|
||||||
request.webContents = null
|
request.webContents = null
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
desktopCapturer.emit = function (event, name, sources) {
|
desktopCapturer.emit = (event, name, sources) => {
|
||||||
// Receiving sources result from main process, now send them back to renderer.
|
// Receiving sources result from main process, now send them back to renderer.
|
||||||
var handledRequest, i, len, ref, ref1, request, result, source, unhandledRequestsQueue
|
const handledRequest = requestsQueue.shift()
|
||||||
handledRequest = requestsQueue.shift(0)
|
const handledWebContents = handledRequest.webContents
|
||||||
result = (function () {
|
const unhandledRequestsQueue = []
|
||||||
var i, len, results
|
|
||||||
results = []
|
const result = sources.map(source => {
|
||||||
for (i = 0, len = sources.length; i < len; i++) {
|
return {
|
||||||
source = sources[i]
|
id: source.id,
|
||||||
results.push({
|
name: source.name,
|
||||||
id: source.id,
|
thumbnail: source.thumbnail.toDataURL()
|
||||||
name: source.name,
|
|
||||||
thumbnail: source.thumbnail.toDataURL()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return results
|
})
|
||||||
})()
|
|
||||||
if ((ref = handledRequest.webContents) != null) {
|
if (handledWebContents) {
|
||||||
ref.send('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + handledRequest.id, result)
|
handledWebContents.send(capturerResult(handledRequest.id), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the queue to see whether there is other same request. If has, handle
|
// Check the queue to see whether there is another identical request & handle
|
||||||
// it for reducing redunplicated `desktopCaptuer.startHandling` calls.
|
requestsQueue.forEach(request => {
|
||||||
unhandledRequestsQueue = []
|
const webContents = request.webContents
|
||||||
for (i = 0, len = requestsQueue.length; i < len; i++) {
|
|
||||||
request = requestsQueue[i]
|
|
||||||
if (deepEqual(handledRequest.options, request.options)) {
|
if (deepEqual(handledRequest.options, request.options)) {
|
||||||
if ((ref1 = request.webContents) != null) {
|
if (webContents) webContents.send(capturerResult(request.id), result)
|
||||||
ref1.send('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + request.id, result)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
unhandledRequestsQueue.push(request)
|
unhandledRequestsQueue.push(request)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
requestsQueue = unhandledRequestsQueue
|
requestsQueue = unhandledRequestsQueue
|
||||||
|
|
||||||
// If the requestsQueue is not empty, start a new request handling.
|
// If the requestsQueue is not empty, start a new request handling.
|
||||||
|
|
|
@ -76,7 +76,7 @@ const createGuest = function (embedder, params) {
|
||||||
watchEmbedder(embedder)
|
watchEmbedder(embedder)
|
||||||
|
|
||||||
// Init guest web view after attached.
|
// Init guest web view after attached.
|
||||||
guest.on('did-attach', function () {
|
guest.on('did-attach', function (event) {
|
||||||
params = this.attachParams
|
params = this.attachParams
|
||||||
delete this.attachParams
|
delete this.attachParams
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ const createGuest = function (embedder, params) {
|
||||||
this.loadURL(params.src, opts)
|
this.loadURL(params.src, opts)
|
||||||
}
|
}
|
||||||
guest.allowPopups = params.allowpopups
|
guest.allowPopups = params.allowpopups
|
||||||
|
embedder.emit('did-attach-webview', event, guest)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sendToEmbedder = (channel, ...args) => {
|
const sendToEmbedder = (channel, ...args) => {
|
||||||
|
|
|
@ -9,36 +9,26 @@ const binding = process.atomBinding('crash_reporter')
|
||||||
|
|
||||||
class CrashReporter {
|
class CrashReporter {
|
||||||
start (options) {
|
start (options) {
|
||||||
if (options == null) {
|
if (options == null) options = {}
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
this.productName = options.productName != null ? options.productName : app.getName()
|
this.productName = options.productName != null ? options.productName : app.getName()
|
||||||
let {companyName, extra, ignoreSystemCrashHandler, submitURL, uploadToServer} = options
|
|
||||||
|
|
||||||
if (uploadToServer == null) {
|
let {
|
||||||
// TODO: Remove deprecated autoSubmit property in 2.0
|
companyName,
|
||||||
uploadToServer = options.autoSubmit
|
extra,
|
||||||
}
|
ignoreSystemCrashHandler,
|
||||||
|
submitURL,
|
||||||
|
uploadToServer
|
||||||
|
} = options
|
||||||
|
|
||||||
if (uploadToServer == null) {
|
if (uploadToServer == null) uploadToServer = options.autoSubmit
|
||||||
uploadToServer = true
|
if (uploadToServer == null) uploadToServer = true
|
||||||
}
|
if (ignoreSystemCrashHandler == null) ignoreSystemCrashHandler = false
|
||||||
|
if (extra == null) extra = {}
|
||||||
|
|
||||||
|
if (extra._productName == null) extra._productName = this.getProductName()
|
||||||
|
if (extra._companyName == null) extra._companyName = companyName
|
||||||
|
if (extra._version == null) extra._version = app.getVersion()
|
||||||
|
|
||||||
if (ignoreSystemCrashHandler == null) {
|
|
||||||
ignoreSystemCrashHandler = false
|
|
||||||
}
|
|
||||||
if (extra == null) {
|
|
||||||
extra = {}
|
|
||||||
}
|
|
||||||
if (extra._productName == null) {
|
|
||||||
extra._productName = this.getProductName()
|
|
||||||
}
|
|
||||||
if (extra._companyName == null) {
|
|
||||||
extra._companyName = companyName
|
|
||||||
}
|
|
||||||
if (extra._version == null) {
|
|
||||||
extra._version = app.getVersion()
|
|
||||||
}
|
|
||||||
if (companyName == null) {
|
if (companyName == null) {
|
||||||
throw new Error('companyName is a required option to crashReporter.start')
|
throw new Error('companyName is a required option to crashReporter.start')
|
||||||
}
|
}
|
||||||
|
@ -47,15 +37,16 @@ class CrashReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
|
const env = {
|
||||||
|
ELECTRON_INTERNAL_CRASH_SERVICE: 1
|
||||||
|
}
|
||||||
const args = [
|
const args = [
|
||||||
'--reporter-url=' + submitURL,
|
'--reporter-url=' + submitURL,
|
||||||
'--application-name=' + this.getProductName(),
|
'--application-name=' + this.getProductName(),
|
||||||
'--crashes-directory=' + this.getCrashesDirectory(),
|
'--crashes-directory=' + this.getCrashesDirectory(),
|
||||||
'--v=1'
|
'--v=1'
|
||||||
]
|
]
|
||||||
const env = {
|
|
||||||
ELECTRON_INTERNAL_CRASH_SERVICE: 1
|
|
||||||
}
|
|
||||||
this._crashServiceProcess = spawn(process.execPath, args, {
|
this._crashServiceProcess = spawn(process.execPath, args, {
|
||||||
env: env,
|
env: env,
|
||||||
detached: true
|
detached: true
|
||||||
|
@ -67,11 +58,7 @@ class CrashReporter {
|
||||||
|
|
||||||
getLastCrashReport () {
|
getLastCrashReport () {
|
||||||
const reports = this.getUploadedReports()
|
const reports = this.getUploadedReports()
|
||||||
if (reports.length > 0) {
|
return (reports.length > 0) ? reports[0] : null
|
||||||
return reports[0]
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getUploadedReports () {
|
getUploadedReports () {
|
||||||
|
@ -79,7 +66,7 @@ class CrashReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
getCrashesDirectory () {
|
getCrashesDirectory () {
|
||||||
const crashesDir = this.getProductName() + ' Crashes'
|
const crashesDir = `${this.getProductName()} Crashes`
|
||||||
return path.join(this.getTempDirectory(), crashesDir)
|
return path.join(this.getTempDirectory(), crashesDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +82,6 @@ class CrashReporter {
|
||||||
try {
|
try {
|
||||||
this.tempDirectory = app.getPath('temp')
|
this.tempDirectory = app.getPath('temp')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// app.getPath may throw so fallback to OS temp directory
|
|
||||||
this.tempDirectory = os.tmpdir()
|
this.tempDirectory = os.tmpdir()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,9 +104,30 @@ class CrashReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(2.0) Remove
|
||||||
setExtraParameter (key, value) {
|
setExtraParameter (key, value) {
|
||||||
|
// TODO(alexeykuzmin): Warning disabled since it caused
|
||||||
|
// a couple of Crash Reported tests to time out on Mac. Add it back.
|
||||||
|
// https://github.com/electron/electron/issues/11012
|
||||||
|
|
||||||
|
// if (!process.noDeprecations) {
|
||||||
|
// deprecate.warn('crashReporter.setExtraParameter',
|
||||||
|
// 'crashReporter.addExtraParameter or crashReporter.removeExtraParameter')
|
||||||
|
// }
|
||||||
binding.setExtraParameter(key, value)
|
binding.setExtraParameter(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addExtraParameter (key, value) {
|
||||||
|
binding.addExtraParameter(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeExtraParameter (key) {
|
||||||
|
binding.removeExtraParameter(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
getParameters (key, value) {
|
||||||
|
return binding.getParameters()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new CrashReporter()
|
module.exports = new CrashReporter()
|
||||||
|
|
|
@ -1,46 +1,43 @@
|
||||||
const ipcRenderer = require('electron').ipcRenderer
|
const {ipcRenderer, nativeImage} = require('electron')
|
||||||
const nativeImage = require('electron').nativeImage
|
|
||||||
|
|
||||||
var nextId = 0
|
const includes = [].includes
|
||||||
var includes = [].includes
|
let currentId = 0
|
||||||
|
|
||||||
var getNextId = function () {
|
const incrementId = () => {
|
||||||
return ++nextId
|
currentId += 1
|
||||||
|
return currentId
|
||||||
}
|
}
|
||||||
|
|
||||||
// |options.type| can not be empty and has to include 'window' or 'screen'.
|
// |options.types| can't be empty and must be an array
|
||||||
var isValid = function (options) {
|
function isValid (options) {
|
||||||
return ((options != null ? options.types : void 0) != null) && Array.isArray(options.types)
|
const types = options ? options.types : undefined
|
||||||
|
return Array.isArray(types)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getSources = function (options, callback) {
|
exports.getSources = function (options, callback) {
|
||||||
var captureScreen, captureWindow, id
|
if (!isValid(options)) return callback(new Error('Invalid options'))
|
||||||
if (!isValid(options)) {
|
const captureWindow = includes.call(options.types, 'window')
|
||||||
return callback(new Error('Invalid options'))
|
const captureScreen = includes.call(options.types, 'screen')
|
||||||
}
|
|
||||||
captureWindow = includes.call(options.types, 'window')
|
|
||||||
captureScreen = includes.call(options.types, 'screen')
|
|
||||||
if (options.thumbnailSize == null) {
|
if (options.thumbnailSize == null) {
|
||||||
options.thumbnailSize = {
|
options.thumbnailSize = {
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 150
|
height: 150
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
id = getNextId()
|
|
||||||
|
const id = incrementId()
|
||||||
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id)
|
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id)
|
||||||
return ipcRenderer.once('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + id, function (event, sources) {
|
return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => {
|
||||||
var source
|
callback(null, (() => {
|
||||||
callback(null, (function () {
|
const results = []
|
||||||
var i, len, results
|
sources.forEach(source => {
|
||||||
results = []
|
|
||||||
for (i = 0, len = sources.length; i < len; i++) {
|
|
||||||
source = sources[i]
|
|
||||||
results.push({
|
results.push({
|
||||||
id: source.id,
|
id: source.id,
|
||||||
name: source.name,
|
name: source.name,
|
||||||
thumbnail: nativeImage.createFromDataURL(source.thumbnail)
|
thumbnail: nativeImage.createFromDataURL(source.thumbnail)
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
return results
|
return results
|
||||||
})())
|
})())
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,16 +9,11 @@ const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron')
|
||||||
const resolvePromise = Promise.resolve.bind(Promise)
|
const resolvePromise = Promise.resolve.bind(Promise)
|
||||||
|
|
||||||
const callbacksRegistry = new CallbacksRegistry()
|
const callbacksRegistry = new CallbacksRegistry()
|
||||||
|
|
||||||
const remoteObjectCache = v8Util.createIDWeakMap()
|
const remoteObjectCache = v8Util.createIDWeakMap()
|
||||||
|
|
||||||
// Convert the arguments object into an array of meta data.
|
// Convert the arguments object into an array of meta data.
|
||||||
const wrapArgs = function (args, visited) {
|
function wrapArgs (args, visited = new Set()) {
|
||||||
if (visited == null) {
|
const valueToMeta = (value) => {
|
||||||
visited = new Set()
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueToMeta = function (value) {
|
|
||||||
// Check for circular reference.
|
// Check for circular reference.
|
||||||
if (visited.has(value)) {
|
if (visited.has(value)) {
|
||||||
return {
|
return {
|
||||||
|
@ -62,7 +57,7 @@ const wrapArgs = function (args, visited) {
|
||||||
|
|
||||||
let meta = {
|
let meta = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
name: value.constructor != null ? value.constructor.name : '',
|
name: value.constructor ? value.constructor.name : '',
|
||||||
members: []
|
members: []
|
||||||
}
|
}
|
||||||
visited.add(value)
|
visited.add(value)
|
||||||
|
@ -99,7 +94,7 @@ const wrapArgs = function (args, visited) {
|
||||||
// Populate object's members from descriptors.
|
// Populate object's members from descriptors.
|
||||||
// The |ref| will be kept referenced by |members|.
|
// The |ref| will be kept referenced by |members|.
|
||||||
// This matches |getObjectMemebers| in rpc-server.
|
// This matches |getObjectMemebers| in rpc-server.
|
||||||
const setObjectMembers = function (ref, object, metaId, members) {
|
function setObjectMembers (ref, object, metaId, members) {
|
||||||
if (!Array.isArray(members)) return
|
if (!Array.isArray(members)) return
|
||||||
|
|
||||||
for (let member of members) {
|
for (let member of members) {
|
||||||
|
@ -108,44 +103,41 @@ const setObjectMembers = function (ref, object, metaId, members) {
|
||||||
let descriptor = { enumerable: member.enumerable }
|
let descriptor = { enumerable: member.enumerable }
|
||||||
if (member.type === 'method') {
|
if (member.type === 'method') {
|
||||||
const remoteMemberFunction = function (...args) {
|
const remoteMemberFunction = function (...args) {
|
||||||
|
let command
|
||||||
if (this && this.constructor === remoteMemberFunction) {
|
if (this && this.constructor === remoteMemberFunction) {
|
||||||
// Constructor call.
|
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'
|
||||||
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
|
|
||||||
return metaToValue(ret)
|
|
||||||
} else {
|
} else {
|
||||||
// Call member function.
|
command = 'ELECTRON_BROWSER_MEMBER_CALL'
|
||||||
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
|
|
||||||
return metaToValue(ret)
|
|
||||||
}
|
}
|
||||||
|
const ret = ipcRenderer.sendSync(command, metaId, member.name, wrapArgs(args))
|
||||||
|
return metaToValue(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)
|
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)
|
||||||
|
|
||||||
descriptor.get = function () {
|
descriptor.get = () => {
|
||||||
descriptorFunction.ref = ref // The member should reference its object.
|
descriptorFunction.ref = ref // The member should reference its object.
|
||||||
return descriptorFunction
|
return descriptorFunction
|
||||||
}
|
}
|
||||||
// Enable monkey-patch the method
|
// Enable monkey-patch the method
|
||||||
descriptor.set = function (value) {
|
descriptor.set = (value) => {
|
||||||
descriptorFunction = value
|
descriptorFunction = value
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
descriptor.configurable = true
|
descriptor.configurable = true
|
||||||
} else if (member.type === 'get') {
|
} else if (member.type === 'get') {
|
||||||
descriptor.get = function () {
|
descriptor.get = () => {
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name))
|
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||||
|
const meta = ipcRenderer.sendSync(command, metaId, member.name)
|
||||||
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only set setter when it is writable.
|
|
||||||
if (member.writable) {
|
if (member.writable) {
|
||||||
descriptor.set = function (value) {
|
descriptor.set = (value) => {
|
||||||
const args = wrapArgs([value])
|
const args = wrapArgs([value])
|
||||||
const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, args)
|
const command = 'ELECTRON_BROWSER_MEMBER_SET'
|
||||||
// Meta will be non-null when a setter error occurred so parse it
|
const meta = ipcRenderer.sendSync(command, metaId, member.name, args)
|
||||||
// to a value so it gets re-thrown.
|
if (meta != null) metaToValue(meta)
|
||||||
if (meta != null) {
|
|
||||||
metaToValue(meta)
|
|
||||||
}
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +149,7 @@ const setObjectMembers = function (ref, object, metaId, members) {
|
||||||
|
|
||||||
// Populate object's prototype from descriptor.
|
// Populate object's prototype from descriptor.
|
||||||
// This matches |getObjectPrototype| in rpc-server.
|
// This matches |getObjectPrototype| in rpc-server.
|
||||||
const setObjectPrototype = function (ref, object, metaId, descriptor) {
|
function setObjectPrototype (ref, object, metaId, descriptor) {
|
||||||
if (descriptor === null) return
|
if (descriptor === null) return
|
||||||
let proto = {}
|
let proto = {}
|
||||||
setObjectMembers(ref, proto, metaId, descriptor.members)
|
setObjectMembers(ref, proto, metaId, descriptor.members)
|
||||||
|
@ -166,14 +158,15 @@ const setObjectPrototype = function (ref, object, metaId, descriptor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap function in Proxy for accessing remote properties
|
// Wrap function in Proxy for accessing remote properties
|
||||||
const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
|
function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
|
||||||
let loaded = false
|
let loaded = false
|
||||||
|
|
||||||
// Lazily load function properties
|
// Lazily load function properties
|
||||||
const loadRemoteProperties = () => {
|
const loadRemoteProperties = () => {
|
||||||
if (loaded) return
|
if (loaded) return
|
||||||
loaded = true
|
loaded = true
|
||||||
const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name)
|
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||||
|
const meta = ipcRenderer.sendSync(command, metaId, name)
|
||||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,13 +179,9 @@ const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
|
||||||
get: (target, property, receiver) => {
|
get: (target, property, receiver) => {
|
||||||
if (!target.hasOwnProperty(property)) loadRemoteProperties()
|
if (!target.hasOwnProperty(property)) loadRemoteProperties()
|
||||||
const value = target[property]
|
const value = target[property]
|
||||||
|
|
||||||
// Bind toString to target if it is a function to avoid
|
|
||||||
// Function.prototype.toString is not generic errors
|
|
||||||
if (property === 'toString' && typeof value === 'function') {
|
if (property === 'toString' && typeof value === 'function') {
|
||||||
return value.bind(target)
|
return value.bind(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
ownKeys: (target) => {
|
ownKeys: (target) => {
|
||||||
|
@ -201,7 +190,7 @@ const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
|
||||||
},
|
},
|
||||||
getOwnPropertyDescriptor: (target, property) => {
|
getOwnPropertyDescriptor: (target, property) => {
|
||||||
let descriptor = Object.getOwnPropertyDescriptor(target, property)
|
let descriptor = Object.getOwnPropertyDescriptor(target, property)
|
||||||
if (descriptor != null) return descriptor
|
if (descriptor) return descriptor
|
||||||
loadRemoteProperties()
|
loadRemoteProperties()
|
||||||
return Object.getOwnPropertyDescriptor(target, property)
|
return Object.getOwnPropertyDescriptor(target, property)
|
||||||
}
|
}
|
||||||
|
@ -209,155 +198,130 @@ const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert meta data from browser into real value.
|
// Convert meta data from browser into real value.
|
||||||
const metaToValue = function (meta) {
|
function metaToValue (meta) {
|
||||||
var el, i, len, ref1, results, ret
|
const types = {
|
||||||
switch (meta.type) {
|
value: () => meta.value,
|
||||||
case 'value':
|
array: () => meta.members.map((member) => metaToValue(member)),
|
||||||
return meta.value
|
buffer: () => Buffer.from(meta.value),
|
||||||
case 'array':
|
promise: () => resolvePromise({then: metaToValue(meta.then)}),
|
||||||
ref1 = meta.members
|
error: () => metaToPlainObject(meta),
|
||||||
results = []
|
date: () => new Date(meta.value),
|
||||||
for (i = 0, len = ref1.length; i < len; i++) {
|
exception: () => { throw new Error(`${meta.message}\n${meta.stack}`) }
|
||||||
el = ref1[i]
|
}
|
||||||
results.push(metaToValue(el))
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
case 'buffer':
|
|
||||||
return Buffer.from(meta.value)
|
|
||||||
case 'promise':
|
|
||||||
return resolvePromise({then: metaToValue(meta.then)})
|
|
||||||
case 'error':
|
|
||||||
return metaToPlainObject(meta)
|
|
||||||
case 'date':
|
|
||||||
return new Date(meta.value)
|
|
||||||
case 'exception':
|
|
||||||
throw new Error(meta.message + '\n' + meta.stack)
|
|
||||||
default:
|
|
||||||
if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id)
|
|
||||||
|
|
||||||
if (meta.type === 'function') {
|
if (meta.type in types) {
|
||||||
// A shadow class to represent the remote function object.
|
return types[meta.type]()
|
||||||
let remoteFunction = function (...args) {
|
} else {
|
||||||
if (this && this.constructor === remoteFunction) {
|
let ret
|
||||||
// Constructor call.
|
if (remoteObjectCache.has(meta.id)) {
|
||||||
let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args))
|
return remoteObjectCache.get(meta.id)
|
||||||
// Returning object in constructor will replace constructed object
|
}
|
||||||
// with the returned object.
|
|
||||||
// http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
|
// A shadow class to represent the remote function object.
|
||||||
return metaToValue(obj)
|
if (meta.type === 'function') {
|
||||||
} else {
|
let remoteFunction = function (...args) {
|
||||||
// Function call.
|
let command
|
||||||
let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args))
|
if (this && this.constructor === remoteFunction) {
|
||||||
return metaToValue(obj)
|
command = 'ELECTRON_BROWSER_CONSTRUCTOR'
|
||||||
}
|
} else {
|
||||||
|
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
|
||||||
}
|
}
|
||||||
ret = remoteFunction
|
const obj = ipcRenderer.sendSync(command, meta.id, wrapArgs(args))
|
||||||
} else {
|
return metaToValue(obj)
|
||||||
ret = {}
|
|
||||||
}
|
}
|
||||||
|
ret = remoteFunction
|
||||||
|
} else {
|
||||||
|
ret = {}
|
||||||
|
}
|
||||||
|
|
||||||
// Populate delegate members.
|
setObjectMembers(ret, ret, meta.id, meta.members)
|
||||||
setObjectMembers(ret, ret, meta.id, meta.members)
|
setObjectPrototype(ret, ret, meta.id, meta.proto)
|
||||||
// Populate delegate prototype.
|
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||||
setObjectPrototype(ret, ret, meta.id, meta.proto)
|
|
||||||
|
|
||||||
// Set constructor.name to object's name.
|
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
||||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
v8Util.setRemoteObjectFreer(ret, meta.id)
|
||||||
|
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
||||||
// Track delegate object's life time, and tell the browser to clean up
|
remoteObjectCache.set(meta.id, ret)
|
||||||
// when the object is GCed.
|
return ret
|
||||||
v8Util.setRemoteObjectFreer(ret, meta.id)
|
|
||||||
|
|
||||||
// Remember object's id.
|
|
||||||
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
|
||||||
remoteObjectCache.set(meta.id, ret)
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a plain object from the meta.
|
// Construct a plain object from the meta.
|
||||||
const metaToPlainObject = function (meta) {
|
function metaToPlainObject (meta) {
|
||||||
var i, len, obj, ref1
|
const obj = (() => meta.type === 'error' ? new Error() : {})()
|
||||||
obj = (function () {
|
for (let i = 0; i < meta.members.length; i++) {
|
||||||
switch (meta.type) {
|
let {name, value} = meta.members[i]
|
||||||
case 'error':
|
|
||||||
return new Error()
|
|
||||||
default:
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
ref1 = meta.members
|
|
||||||
for (i = 0, len = ref1.length; i < len; i++) {
|
|
||||||
let {name, value} = ref1[i]
|
|
||||||
obj[name] = value
|
obj[name] = value
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browser calls a callback in renderer.
|
// Browser calls a callback in renderer.
|
||||||
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', function (event, id, args) {
|
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => {
|
||||||
callbacksRegistry.apply(id, metaToValue(args))
|
callbacksRegistry.apply(id, metaToValue(args))
|
||||||
})
|
})
|
||||||
|
|
||||||
// A callback in browser is released.
|
// A callback in browser is released.
|
||||||
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) {
|
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, id) => {
|
||||||
callbacksRegistry.remove(id)
|
callbacksRegistry.remove(id)
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on('exit', () => {
|
process.on('exit', () => {
|
||||||
ipcRenderer.sendSync('ELECTRON_BROWSER_CONTEXT_RELEASE')
|
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||||
|
ipcRenderer.sendSync(command)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get remote module.
|
exports.require = (module) => {
|
||||||
exports.require = function (module) {
|
const command = 'ELECTRON_BROWSER_REQUIRE'
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module))
|
const meta = ipcRenderer.sendSync(command, module)
|
||||||
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alias to remote.require('electron').xxx.
|
// Alias to remote.require('electron').xxx.
|
||||||
exports.getBuiltin = function (module) {
|
exports.getBuiltin = (module) => {
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module))
|
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
|
||||||
|
const meta = ipcRenderer.sendSync(command, module)
|
||||||
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current BrowserWindow.
|
exports.getCurrentWindow = () => {
|
||||||
exports.getCurrentWindow = function () {
|
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW'))
|
const meta = ipcRenderer.sendSync(command)
|
||||||
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current WebContents object.
|
// Get current WebContents object.
|
||||||
exports.getCurrentWebContents = function () {
|
exports.getCurrentWebContents = () => {
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
|
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a global object in browser.
|
// Get a global object in browser.
|
||||||
exports.getGlobal = function (name) {
|
exports.getGlobal = (name) => {
|
||||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name))
|
const command = 'ELECTRON_BROWSER_GLOBAL'
|
||||||
|
const meta = ipcRenderer.sendSync(command, name)
|
||||||
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the process object in browser.
|
// Get the process object in browser.
|
||||||
exports.__defineGetter__('process', function () {
|
exports.__defineGetter__('process', () => exports.getGlobal('process'))
|
||||||
return exports.getGlobal('process')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create a funtion that will return the specifed value when called in browser.
|
// Create a function that will return the specified value when called in browser.
|
||||||
exports.createFunctionWithReturnValue = function (returnValue) {
|
exports.createFunctionWithReturnValue = (returnValue) => {
|
||||||
const func = function () {
|
const func = () => returnValue
|
||||||
return returnValue
|
|
||||||
}
|
|
||||||
v8Util.setHiddenValue(func, 'returnValue', true)
|
v8Util.setHiddenValue(func, 'returnValue', true)
|
||||||
return func
|
return func
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the guest WebContents from guestInstanceId.
|
// Get the guest WebContents from guestInstanceId.
|
||||||
exports.getGuestWebContents = function (guestInstanceId) {
|
exports.getGuestWebContents = (guestInstanceId) => {
|
||||||
const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId)
|
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
|
||||||
|
const meta = ipcRenderer.sendSync(command, guestInstanceId)
|
||||||
return metaToValue(meta)
|
return metaToValue(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addBuiltinProperty = (name) => {
|
const addBuiltinProperty = (name) => {
|
||||||
Object.defineProperty(exports, name, {
|
Object.defineProperty(exports, name, {
|
||||||
get: function () {
|
get: () => exports.getBuiltin(name)
|
||||||
return exports.getBuiltin(name)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ const {webFrame, WebFrame} = process.atomBinding('web_frame')
|
||||||
|
|
||||||
// WebFrame is an EventEmitter.
|
// WebFrame is an EventEmitter.
|
||||||
Object.setPrototypeOf(WebFrame.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(WebFrame.prototype, EventEmitter.prototype)
|
||||||
|
EventEmitter.call(webFrame)
|
||||||
|
|
||||||
// Lots of webview would subscribe to webFrame's events.
|
// Lots of webview would subscribe to webFrame's events.
|
||||||
webFrame.setMaxListeners(0)
|
webFrame.setMaxListeners(0)
|
||||||
|
|
|
@ -5,7 +5,7 @@ const {runInThisContext} = require('vm')
|
||||||
// https://developer.chrome.com/extensions/match_patterns
|
// https://developer.chrome.com/extensions/match_patterns
|
||||||
const matchesPattern = function (pattern) {
|
const matchesPattern = function (pattern) {
|
||||||
if (pattern === '<all_urls>') return true
|
if (pattern === '<all_urls>') return true
|
||||||
const regexp = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$')
|
const regexp = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
|
||||||
const url = `${location.protocol}//${location.host}${location.pathname}`
|
const url = `${location.protocol}//${location.host}${location.pathname}`
|
||||||
return url.match(regexp)
|
return url.match(regexp)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ const matchesPattern = function (pattern) {
|
||||||
const runContentScript = function (extensionId, url, code) {
|
const runContentScript = function (extensionId, url, code) {
|
||||||
const context = {}
|
const context = {}
|
||||||
require('./chrome-api').injectTo(extensionId, false, context)
|
require('./chrome-api').injectTo(extensionId, false, context)
|
||||||
const wrapper = `(function (chrome) {\n ${code}\n })`
|
const wrapper = `((chrome) => {\n ${code}\n })`
|
||||||
const compiledWrapper = runInThisContext(wrapper, {
|
const compiledWrapper = runInThisContext(wrapper, {
|
||||||
filename: url,
|
filename: url,
|
||||||
lineOffset: 1,
|
lineOffset: 1,
|
||||||
|
@ -23,6 +23,23 @@ const runContentScript = function (extensionId, url, code) {
|
||||||
return compiledWrapper.call(this, context.chrome)
|
return compiledWrapper.call(this, context.chrome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const runStylesheet = function (url, code) {
|
||||||
|
const wrapper = `((code) => {
|
||||||
|
function init() {
|
||||||
|
const styleElement = document.createElement('style');
|
||||||
|
styleElement.textContent = code;
|
||||||
|
document.head.append(styleElement);
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
})`
|
||||||
|
const compiledWrapper = runInThisContext(wrapper, {
|
||||||
|
filename: url,
|
||||||
|
lineOffset: 1,
|
||||||
|
displayErrors: true
|
||||||
|
})
|
||||||
|
return compiledWrapper.call(this, code)
|
||||||
|
}
|
||||||
|
|
||||||
// Run injected scripts.
|
// Run injected scripts.
|
||||||
// https://developer.chrome.com/extensions/content_scripts
|
// https://developer.chrome.com/extensions/content_scripts
|
||||||
const injectContentScript = function (extensionId, script) {
|
const injectContentScript = function (extensionId, script) {
|
||||||
|
@ -35,19 +52,22 @@ const injectContentScript = function (extensionId, script) {
|
||||||
process.once('document-start', fire)
|
process.once('document-start', fire)
|
||||||
} else if (script.runAt === 'document_end') {
|
} else if (script.runAt === 'document_end') {
|
||||||
process.once('document-end', fire)
|
process.once('document-end', fire)
|
||||||
} else if (script.runAt === 'document_idle') {
|
} else {
|
||||||
document.addEventListener('DOMContentLoaded', fire)
|
document.addEventListener('DOMContentLoaded', fire)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script.css) {
|
if (script.css) {
|
||||||
for (const {code} of script.css) {
|
for (const {url, code} of script.css) {
|
||||||
process.once('document-end', () => {
|
const fire = runStylesheet.bind(window, url, code)
|
||||||
var node = document.createElement('style')
|
if (script.runAt === 'document_start') {
|
||||||
node.innerHTML = code
|
process.once('document-start', fire)
|
||||||
window.document.body.appendChild(node)
|
} else if (script.runAt === 'document_end') {
|
||||||
})
|
process.once('document-end', fire)
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', fire)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ download({
|
||||||
arch: process.env.npm_config_arch,
|
arch: process.env.npm_config_arch,
|
||||||
strictSSL: process.env.npm_config_strict_ssl === 'true',
|
strictSSL: process.env.npm_config_strict_ssl === 'true',
|
||||||
force: process.env.force_no_cache === 'true',
|
force: process.env.force_no_cache === 'true',
|
||||||
quiet: ['info', 'verbose', 'silly', 'http'].indexOf(process.env.npm_config_loglevel) === -1
|
quiet: process.env.npm_config_loglevel === 'silent' || process.env.CI
|
||||||
}, extractFile)
|
}, extractFile)
|
||||||
|
|
||||||
// unzips and makes path.txt point at the correct executable
|
// unzips and makes path.txt point at the correct executable
|
||||||
|
|
16
package.json
16
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "electron",
|
"name": "electron",
|
||||||
"version": "1.8.1",
|
"version": "1.8.2-beta.2",
|
||||||
"repository": "https://github.com/electron/electron",
|
"repository": "https://github.com/electron/electron",
|
||||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -9,15 +9,18 @@
|
||||||
"check-for-leaks": "^1.0.2",
|
"check-for-leaks": "^1.0.2",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"dotenv-safe": "^4.0.4",
|
"dotenv-safe": "^4.0.4",
|
||||||
|
"dugite": "^1.45.0",
|
||||||
"electabul": "~0.0.4",
|
"electabul": "~0.0.4",
|
||||||
"electron-docs-linter": "^2.3.3",
|
"electron-docs-linter": "^2.3.3",
|
||||||
"electron-typescript-definitions": "^1.2.7",
|
"electron-typescript-definitions": "^1.2.10",
|
||||||
"github": "^9.2.0",
|
"github": "^9.2.0",
|
||||||
"heads": "^1.3.0",
|
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.14.3",
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"nugget": "^2.0.1",
|
||||||
"request": "^2.68.0",
|
"request": "^2.68.0",
|
||||||
"standard": "^8.4.0",
|
"standard": "^8.4.0",
|
||||||
"standard-markdown": "^4.0.0",
|
"standard-markdown": "^4.0.0",
|
||||||
|
"sumchecker": "^2.0.2",
|
||||||
"temp": "^0.8.3"
|
"temp": "^0.8.3"
|
||||||
},
|
},
|
||||||
"standard": {
|
"standard": {
|
||||||
|
@ -49,12 +52,15 @@
|
||||||
"lint-api-docs-js": "standard-markdown docs && standard-markdown docs-translations",
|
"lint-api-docs-js": "standard-markdown docs && standard-markdown docs-translations",
|
||||||
"create-api-json": "electron-docs-linter docs --outfile=out/electron-api.json --version=$npm_package_version",
|
"create-api-json": "electron-docs-linter docs --outfile=out/electron-api.json --version=$npm_package_version",
|
||||||
"create-typescript-definitions": "npm run create-api-json && electron-typescript-definitions --in=out/electron-api.json --out=out/electron.d.ts",
|
"create-typescript-definitions": "npm run create-api-json && electron-typescript-definitions --in=out/electron-api.json --out=out/electron.d.ts",
|
||||||
|
"merge-release": "node ./script/merge-release.js",
|
||||||
|
"mock-release": "node ./script/ci-release-build.js",
|
||||||
"preinstall": "node -e 'process.exit(0)'",
|
"preinstall": "node -e 'process.exit(0)'",
|
||||||
"publish-to-npm": "node ./script/publish-to-npm.js",
|
"publish-to-npm": "node ./script/publish-to-npm.js",
|
||||||
"prepack": "check-for-leaks",
|
"prepack": "check-for-leaks",
|
||||||
"prepush": "check-for-leaks",
|
"prepush": "check-for-leaks",
|
||||||
"prerelease": "node ./script/prerelease",
|
"prepare-release": "node ./script/prepare-release.js",
|
||||||
"release": "./script/upload.py -p",
|
"prerelease": "python ./script/bootstrap.py -v --dev && npm run build",
|
||||||
|
"release": "node ./script/release.js",
|
||||||
"repl": "python ./script/start.py --interactive",
|
"repl": "python ./script/start.py --interactive",
|
||||||
"start": "python ./script/start.py",
|
"start": "python ./script/start.py",
|
||||||
"test": "python ./script/test.py"
|
"test": "python ./script/test.py"
|
||||||
|
|
|
@ -90,6 +90,7 @@ def main():
|
||||||
update_package_json(version, suffix)
|
update_package_json(version, suffix)
|
||||||
tag_version(version, suffix)
|
tag_version(version, suffix)
|
||||||
|
|
||||||
|
print 'Bumped to version: {0}'.format(version + suffix)
|
||||||
|
|
||||||
def increase_version(versions, index):
|
def increase_version(versions, index):
|
||||||
for i in range(index + 1, 4):
|
for i in range(index + 1, 4):
|
||||||
|
|
56
script/ci-release-build.js
Normal file
56
script/ci-release-build.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
const args = require('minimist')(process.argv.slice(2))
|
||||||
|
const assert = require('assert')
|
||||||
|
const request = require('request')
|
||||||
|
|
||||||
|
const ciJobs = [
|
||||||
|
'electron-linux-arm64',
|
||||||
|
'electron-linux-ia32',
|
||||||
|
'electron-linux-x64',
|
||||||
|
'electron-linux-arm'
|
||||||
|
]
|
||||||
|
|
||||||
|
const CIcall = (buildUrl, targetBranch, job) => {
|
||||||
|
console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`)
|
||||||
|
|
||||||
|
request({
|
||||||
|
method: 'POST',
|
||||||
|
url: buildUrl,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
'build_parameters': {
|
||||||
|
'RUN_RELEASE_BUILD': 'true',
|
||||||
|
'CIRCLE_JOB': job
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, (err, res, body) => {
|
||||||
|
if (!err && res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
const build = JSON.parse(body)
|
||||||
|
console.log(`Check ${build.build_url} for status. (${job})`)
|
||||||
|
} else {
|
||||||
|
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), job)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args._.length < 1) {
|
||||||
|
console.log(`Trigger Circle CI to build release builds of electron.
|
||||||
|
Usage: ci-release-build.js [--job=CI_JOB_NAME] TARGET_BRANCH
|
||||||
|
`)
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(process.env.CIRCLE_TOKEN, 'CIRCLE_TOKEN not found in environment')
|
||||||
|
|
||||||
|
const targetBranch = args._[0]
|
||||||
|
const job = args['job']
|
||||||
|
const circleBuildUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/tree/${targetBranch}?circle-token=${process.env.CIRCLE_TOKEN}`
|
||||||
|
|
||||||
|
if (job) {
|
||||||
|
assert(ciJobs.includes(job), `Unknown CI job name: ${job}.`)
|
||||||
|
CIcall(circleBuildUrl, targetBranch, job)
|
||||||
|
} else {
|
||||||
|
ciJobs.forEach((job) => CIcall(circleBuildUrl, targetBranch, job))
|
||||||
|
}
|
10
script/cpplint.py
vendored
10
script/cpplint.py
vendored
|
@ -38,6 +38,10 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
if not os.path.isfile(cpplint_path()):
|
||||||
|
print("[INFO] Skipping cpplint, dependencies has not been bootstrapped")
|
||||||
|
return
|
||||||
|
|
||||||
os.chdir(SOURCE_ROOT)
|
os.chdir(SOURCE_ROOT)
|
||||||
atom_files = list_files('atom',
|
atom_files = list_files('atom',
|
||||||
['app', 'browser', 'common', 'renderer', 'utility'],
|
['app', 'browser', 'common', 'renderer', 'utility'],
|
||||||
|
@ -60,9 +64,13 @@ def list_files(parent, directories, filters):
|
||||||
|
|
||||||
|
|
||||||
def call_cpplint(files):
|
def call_cpplint(files):
|
||||||
cpplint = os.path.join(SOURCE_ROOT, 'vendor', 'depot_tools', 'cpplint.py')
|
cpplint = cpplint_path()
|
||||||
execute([sys.executable, cpplint] + files)
|
execute([sys.executable, cpplint] + files)
|
||||||
|
|
||||||
|
|
||||||
|
def cpplint_path():
|
||||||
|
return os.path.join(SOURCE_ROOT, 'vendor', 'depot_tools', 'cpplint.py')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|
116
script/merge-release.js
Executable file
116
script/merge-release.js
Executable file
|
@ -0,0 +1,116 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('colors')
|
||||||
|
const assert = require('assert')
|
||||||
|
const branchToRelease = process.argv[2]
|
||||||
|
const fail = '\u2717'.red
|
||||||
|
const { GitProcess, GitError } = require('dugite')
|
||||||
|
const pass = '\u2713'.green
|
||||||
|
const path = require('path')
|
||||||
|
const pkg = require('../package.json')
|
||||||
|
|
||||||
|
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
|
||||||
|
if (!branchToRelease) {
|
||||||
|
console.log(`Usage: merge-release branch`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const gitDir = path.resolve(__dirname, '..')
|
||||||
|
|
||||||
|
async function callGit (args, errorMessage, successMessage) {
|
||||||
|
let gitResult = await GitProcess.exec(args, gitDir)
|
||||||
|
if (gitResult.exitCode === 0) {
|
||||||
|
console.log(`${pass} ${successMessage}`)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
console.log(`${fail} ${errorMessage} ${gitResult.stderr}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkoutBranch (branchName) {
|
||||||
|
console.log(`Checking out ${branchName}.`)
|
||||||
|
let errorMessage = `Error checking out branch ${branchName}:`
|
||||||
|
let successMessage = `Successfully checked out branch ${branchName}.`
|
||||||
|
return await callGit(['checkout', branchName], errorMessage, successMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function commitMerge () {
|
||||||
|
console.log(`Committing the merge for v${pkg.version}`)
|
||||||
|
let errorMessage = `Error committing merge:`
|
||||||
|
let successMessage = `Successfully committed the merge for v${pkg.version}`
|
||||||
|
let gitArgs = ['commit', '-m', `v${pkg.version}`]
|
||||||
|
return await callGit(gitArgs, errorMessage, successMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mergeReleaseIntoBranch (branchName) {
|
||||||
|
console.log(`Merging release branch into ${branchName}.`)
|
||||||
|
let mergeArgs = ['merge', 'release', '--squash']
|
||||||
|
let mergeDetails = await GitProcess.exec(mergeArgs, gitDir)
|
||||||
|
if (mergeDetails.exitCode === 0) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
const error = GitProcess.parseError(mergeDetails.stderr)
|
||||||
|
if (error === GitError.MergeConflicts) {
|
||||||
|
console.log(`${fail} Could not merge release branch into ${branchName} ` +
|
||||||
|
`due to merge conflicts.`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
console.log(`${fail} Could not merge release branch into ${branchName} ` +
|
||||||
|
`due to an error: ${mergeDetails.stderr}.`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pushBranch (branchName) {
|
||||||
|
console.log(`Pushing branch ${branchName}.`)
|
||||||
|
let pushArgs = ['push', 'origin', branchName]
|
||||||
|
let errorMessage = `Could not push branch ${branchName} due to an error:`
|
||||||
|
let successMessage = `Successfully pushed branch ${branchName}.`
|
||||||
|
return await callGit(pushArgs, errorMessage, successMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pull () {
|
||||||
|
console.log(`Performing a git pull`)
|
||||||
|
let errorMessage = `Could not pull due to an error:`
|
||||||
|
let successMessage = `Successfully performed a git pull`
|
||||||
|
return await callGit(['pull'], errorMessage, successMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rebase (targetBranch) {
|
||||||
|
console.log(`Rebasing release branch from ${targetBranch}`)
|
||||||
|
let errorMessage = `Could not rebase due to an error:`
|
||||||
|
let successMessage = `Successfully rebased release branch from ` +
|
||||||
|
`${targetBranch}`
|
||||||
|
return await callGit(['rebase', targetBranch], errorMessage, successMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mergeRelease () {
|
||||||
|
await checkoutBranch(branchToRelease)
|
||||||
|
let mergeSuccess = await mergeReleaseIntoBranch(branchToRelease)
|
||||||
|
if (mergeSuccess) {
|
||||||
|
console.log(`${pass} Successfully merged release branch into ` +
|
||||||
|
`${branchToRelease}.`)
|
||||||
|
await commitMerge()
|
||||||
|
let pushSuccess = await pushBranch(branchToRelease)
|
||||||
|
if (pushSuccess) {
|
||||||
|
console.log(`${pass} Success!!! ${branchToRelease} now has the latest release!`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`Trying rebase of ${branchToRelease} into release branch.`)
|
||||||
|
await pull()
|
||||||
|
await checkoutBranch('release')
|
||||||
|
let rebaseResult = await rebase(branchToRelease)
|
||||||
|
if (rebaseResult) {
|
||||||
|
let pushResult = pushBranch('HEAD')
|
||||||
|
if (pushResult) {
|
||||||
|
console.log(`Rebase of ${branchToRelease} into release branch was ` +
|
||||||
|
`successful. Let release builds run and then try this step again.`)
|
||||||
|
}
|
||||||
|
// Exit as failure so release doesn't continue
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeRelease()
|
173
script/prepare-release.js
Executable file
173
script/prepare-release.js
Executable file
|
@ -0,0 +1,173 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('colors')
|
||||||
|
const args = require('minimist')(process.argv.slice(2))
|
||||||
|
const assert = require('assert')
|
||||||
|
const { execSync } = require('child_process')
|
||||||
|
const fail = '\u2717'.red
|
||||||
|
const { GitProcess, GitError } = require('dugite')
|
||||||
|
const GitHub = require('github')
|
||||||
|
const pass = '\u2713'.green
|
||||||
|
const path = require('path')
|
||||||
|
const pkg = require('../package.json')
|
||||||
|
const versionType = args._[0]
|
||||||
|
|
||||||
|
// TODO (future) automatically determine version based on conventional commits
|
||||||
|
// via conventional-recommended-bump
|
||||||
|
|
||||||
|
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
|
||||||
|
if (!versionType && !args.notesOnly) {
|
||||||
|
console.log(`Usage: prepare-release versionType [major | minor | patch | beta]` +
|
||||||
|
` (--stable) (--notesOnly)`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const github = new GitHub()
|
||||||
|
const gitDir = path.resolve(__dirname, '..')
|
||||||
|
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
||||||
|
|
||||||
|
async function createReleaseBranch () {
|
||||||
|
console.log(`Creating release branch.`)
|
||||||
|
let checkoutDetails = await GitProcess.exec([ 'checkout', '-b', 'release' ], gitDir)
|
||||||
|
if (checkoutDetails.exitCode === 0) {
|
||||||
|
console.log(`${pass} Successfully created the release branch.`)
|
||||||
|
} else {
|
||||||
|
const error = GitProcess.parseError(checkoutDetails.stderr)
|
||||||
|
if (error === GitError.BranchAlreadyExists) {
|
||||||
|
console.log(`${fail} Release branch already exists, aborting prepare ` +
|
||||||
|
`release process.`)
|
||||||
|
} else {
|
||||||
|
console.log(`${fail} Error creating release branch: ` +
|
||||||
|
`${checkoutDetails.stderr}`)
|
||||||
|
}
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewVersion () {
|
||||||
|
console.log(`Bumping for new "${versionType}" version.`)
|
||||||
|
let bumpScript = path.join(__dirname, 'bump-version.py')
|
||||||
|
let scriptArgs = [bumpScript, `--bump ${versionType}`]
|
||||||
|
if (args.stable) {
|
||||||
|
scriptArgs.push('--stable')
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let bumpVersion = execSync(scriptArgs.join(' '), {encoding: 'UTF-8'})
|
||||||
|
bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
|
||||||
|
let newVersion = `v${bumpVersion}`
|
||||||
|
console.log(`${pass} Successfully bumped version to ${newVersion}`)
|
||||||
|
return newVersion
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`${fail} Could not bump version, error was:`, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCurrentBranch (gitDir) {
|
||||||
|
console.log(`Determining current git branch`)
|
||||||
|
let gitArgs = ['rev-parse', '--abbrev-ref', 'HEAD']
|
||||||
|
let branchDetails = await GitProcess.exec(gitArgs, gitDir)
|
||||||
|
if (branchDetails.exitCode === 0) {
|
||||||
|
let currentBranch = branchDetails.stdout.trim()
|
||||||
|
console.log(`${pass} Successfully determined current git branch is ` +
|
||||||
|
`${currentBranch}`)
|
||||||
|
return currentBranch
|
||||||
|
} else {
|
||||||
|
let error = GitProcess.parseError(branchDetails.stderr)
|
||||||
|
console.log(`${fail} Could not get details for the current branch,
|
||||||
|
error was ${branchDetails.stderr}`, error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getReleaseNotes (currentBranch) {
|
||||||
|
console.log(`Generating release notes for ${currentBranch}.`)
|
||||||
|
let githubOpts = {
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron',
|
||||||
|
base: `v${pkg.version}`,
|
||||||
|
head: currentBranch
|
||||||
|
}
|
||||||
|
let releaseNotes = '(placeholder)\n'
|
||||||
|
console.log(`Checking for commits from ${pkg.version} to ${currentBranch}`)
|
||||||
|
let commitComparison = await github.repos.compareCommits(githubOpts)
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`{$fail} Error checking for commits from ${pkg.version} to ` +
|
||||||
|
`${currentBranch}`, err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
commitComparison.data.commits.forEach(commitEntry => {
|
||||||
|
let commitMessage = commitEntry.commit.message
|
||||||
|
if (commitMessage.toLowerCase().indexOf('merge') > -1) {
|
||||||
|
releaseNotes += `${commitMessage} \n`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log(`${pass} Done generating release notes for ${currentBranch}.`)
|
||||||
|
return releaseNotes
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createRelease (branchToTarget, isBeta) {
|
||||||
|
let releaseNotes = await getReleaseNotes(branchToTarget)
|
||||||
|
let newVersion = getNewVersion()
|
||||||
|
const githubOpts = {
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron'
|
||||||
|
}
|
||||||
|
console.log(`Checking for existing draft release.`)
|
||||||
|
let releases = await github.repos.getReleases(githubOpts)
|
||||||
|
.catch(err => {
|
||||||
|
console.log('$fail} Could not get releases. Error was', err)
|
||||||
|
})
|
||||||
|
let drafts = releases.data.filter(release => release.draft)
|
||||||
|
if (drafts.length > 0) {
|
||||||
|
console.log(`${fail} Aborting because draft release for
|
||||||
|
${drafts[0].tag_name} already exists.`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
console.log(`${pass} A draft release does not exist; creating one.`)
|
||||||
|
githubOpts.body = releaseNotes
|
||||||
|
githubOpts.draft = true
|
||||||
|
githubOpts.name = `electron ${newVersion}`
|
||||||
|
if (isBeta) {
|
||||||
|
githubOpts.body = `Note: This is a beta release. Please file new issues ` +
|
||||||
|
`for any bugs you find in it.\n \n This release is published to npm ` +
|
||||||
|
`under the beta tag and can be installed via npm install electron@beta, ` +
|
||||||
|
`or npm i electron@${newVersion.substr(1)}.`
|
||||||
|
githubOpts.name = `${githubOpts.name}`
|
||||||
|
githubOpts.prerelease = true
|
||||||
|
}
|
||||||
|
githubOpts.tag_name = newVersion
|
||||||
|
githubOpts.target_commitish = branchToTarget
|
||||||
|
await github.repos.createRelease(githubOpts)
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`${fail} Error creating new release: `, err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
console.log(`${pass} Draft release for ${newVersion} has been created.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pushRelease () {
|
||||||
|
let pushDetails = await GitProcess.exec(['push', 'origin', 'HEAD'], gitDir)
|
||||||
|
if (pushDetails.exitCode === 0) {
|
||||||
|
console.log(`${pass} Successfully pushed the release branch. Wait for ` +
|
||||||
|
`release builds to finish before running "npm run release".`)
|
||||||
|
} else {
|
||||||
|
console.log(`${fail} Error pushing the release branch: ` +
|
||||||
|
`${pushDetails.stderr}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prepareRelease (isBeta, notesOnly) {
|
||||||
|
let currentBranch = await getCurrentBranch(gitDir)
|
||||||
|
if (notesOnly) {
|
||||||
|
let releaseNotes = await getReleaseNotes(currentBranch)
|
||||||
|
console.log(`Draft release notes are: ${releaseNotes}`)
|
||||||
|
} else {
|
||||||
|
await createReleaseBranch()
|
||||||
|
await createRelease(currentBranch, isBeta)
|
||||||
|
await pushRelease()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareRelease(!args.stable, args.notesOnly)
|
|
@ -1,114 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
require('colors')
|
|
||||||
const assert = require('assert')
|
|
||||||
const GitHub = require('github')
|
|
||||||
const heads = require('heads')
|
|
||||||
const pkg = require('../package.json')
|
|
||||||
const pass = '\u2713'.green
|
|
||||||
const fail = '\u2717'.red
|
|
||||||
let failureCount = 0
|
|
||||||
|
|
||||||
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
|
|
||||||
|
|
||||||
const github = new GitHub()
|
|
||||||
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
|
||||||
github.repos.getReleases({owner: 'electron', repo: 'electron'})
|
|
||||||
.then(res => {
|
|
||||||
const releases = res.data
|
|
||||||
const drafts = releases
|
|
||||||
.filter(release => release.draft) // comment out for testing
|
|
||||||
// .filter(release => release.tag_name === 'v1.7.5') // uncomment for testing
|
|
||||||
|
|
||||||
check(drafts.length === 1, 'one draft exists', true)
|
|
||||||
const draft = drafts[0]
|
|
||||||
|
|
||||||
check(draft.tag_name === `v${pkg.version}`, `draft release version matches local package.json (v${pkg.version})`)
|
|
||||||
check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes')
|
|
||||||
|
|
||||||
const requiredAssets = assetsForVersion(draft.tag_name).sort()
|
|
||||||
const extantAssets = draft.assets.map(asset => asset.name).sort()
|
|
||||||
|
|
||||||
requiredAssets.forEach(asset => {
|
|
||||||
check(extantAssets.includes(asset), asset)
|
|
||||||
})
|
|
||||||
|
|
||||||
const s3Urls = s3UrlsForVersion(draft.tag_name)
|
|
||||||
heads(s3Urls)
|
|
||||||
.then(results => {
|
|
||||||
results.forEach((result, i) => {
|
|
||||||
check(result === 200, s3Urls[i])
|
|
||||||
})
|
|
||||||
|
|
||||||
process.exit(failureCount > 0 ? 1 : 0)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error making HEAD requests for S3 assets')
|
|
||||||
console.error(err)
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function check (condition, statement, exitIfFail = false) {
|
|
||||||
if (condition) {
|
|
||||||
console.log(`${pass} ${statement}`)
|
|
||||||
} else {
|
|
||||||
failureCount++
|
|
||||||
console.log(`${fail} ${statement}`)
|
|
||||||
if (exitIfFail) process.exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function assetsForVersion (version) {
|
|
||||||
const patterns = [
|
|
||||||
'electron-{{VERSION}}-darwin-x64-dsym.zip',
|
|
||||||
'electron-{{VERSION}}-darwin-x64-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-darwin-x64.zip',
|
|
||||||
'electron-{{VERSION}}-linux-arm-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-linux-arm.zip',
|
|
||||||
'electron-{{VERSION}}-linux-arm64-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-linux-arm64.zip',
|
|
||||||
'electron-{{VERSION}}-linux-armv7l-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-linux-armv7l.zip',
|
|
||||||
'electron-{{VERSION}}-linux-ia32-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-linux-ia32.zip',
|
|
||||||
'electron-{{VERSION}}-linux-x64-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-linux-x64.zip',
|
|
||||||
'electron-{{VERSION}}-mas-x64-dsym.zip',
|
|
||||||
'electron-{{VERSION}}-mas-x64-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-mas-x64.zip',
|
|
||||||
'electron-{{VERSION}}-win32-ia32-pdb.zip',
|
|
||||||
'electron-{{VERSION}}-win32-ia32-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-win32-ia32.zip',
|
|
||||||
'electron-{{VERSION}}-win32-x64-pdb.zip',
|
|
||||||
'electron-{{VERSION}}-win32-x64-symbols.zip',
|
|
||||||
'electron-{{VERSION}}-win32-x64.zip',
|
|
||||||
'electron-api.json',
|
|
||||||
'electron.d.ts',
|
|
||||||
'ffmpeg-{{VERSION}}-darwin-x64.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-linux-arm.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-linux-arm64.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-linux-armv7l.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-linux-ia32.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-linux-x64.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-mas-x64.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-win32-ia32.zip',
|
|
||||||
'ffmpeg-{{VERSION}}-win32-x64.zip'
|
|
||||||
]
|
|
||||||
return patterns.map(pattern => pattern.replace(/{{VERSION}}/g, version))
|
|
||||||
}
|
|
||||||
|
|
||||||
function s3UrlsForVersion (version) {
|
|
||||||
const bucket = 'https://gh-contractor-zcbenz.s3.amazonaws.com/'
|
|
||||||
const patterns = [
|
|
||||||
'atom-shell/dist/{{VERSION}}/iojs-{{VERSION}}-headers.tar.gz',
|
|
||||||
'atom-shell/dist/{{VERSION}}/iojs-{{VERSION}}.tar.gz',
|
|
||||||
'atom-shell/dist/{{VERSION}}/node-{{VERSION}}.tar.gz',
|
|
||||||
'atom-shell/dist/{{VERSION}}/node.lib',
|
|
||||||
'atom-shell/dist/{{VERSION}}/win-x64/iojs.lib',
|
|
||||||
'atom-shell/dist/{{VERSION}}/win-x86/iojs.lib',
|
|
||||||
'atom-shell/dist/{{VERSION}}/x64/node.lib',
|
|
||||||
'atom-shell/dist/index.json'
|
|
||||||
]
|
|
||||||
return patterns.map(pattern => bucket + pattern.replace(/{{VERSION}}/g, version))
|
|
||||||
}
|
|
|
@ -114,7 +114,7 @@ new Promise((resolve, reject) => {
|
||||||
cwd: tempDir
|
cwd: tempDir
|
||||||
})
|
})
|
||||||
const checkVersion = childProcess.execSync(`${path.join(tempDir, 'node_modules', '.bin', 'electron')} -v`)
|
const checkVersion = childProcess.execSync(`${path.join(tempDir, 'node_modules', '.bin', 'electron')} -v`)
|
||||||
assert.strictEqual(checkVersion.toString().trim(), `v${rootPackageJson.version}`)
|
assert.ok((`v${rootPackageJson.version}`.indexOf(checkVersion.toString().trim()) === 0), `Version is correct`)
|
||||||
resolve(tarballPath)
|
resolve(tarballPath)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
462
script/release.js
Executable file
462
script/release.js
Executable file
|
@ -0,0 +1,462 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('colors')
|
||||||
|
const args = require('minimist')(process.argv.slice(2))
|
||||||
|
const assert = require('assert')
|
||||||
|
const fs = require('fs')
|
||||||
|
const { execSync } = require('child_process')
|
||||||
|
const GitHub = require('github')
|
||||||
|
const { GitProcess } = require('dugite')
|
||||||
|
const nugget = require('nugget')
|
||||||
|
const pkg = require('../package.json')
|
||||||
|
const pkgVersion = `v${pkg.version}`
|
||||||
|
const pass = '\u2713'.green
|
||||||
|
const path = require('path')
|
||||||
|
const fail = '\u2717'.red
|
||||||
|
const sumchecker = require('sumchecker')
|
||||||
|
const temp = require('temp').track()
|
||||||
|
const { URL } = require('url')
|
||||||
|
let failureCount = 0
|
||||||
|
|
||||||
|
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
|
||||||
|
|
||||||
|
const github = new GitHub({
|
||||||
|
followRedirects: false
|
||||||
|
})
|
||||||
|
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
||||||
|
const gitDir = path.resolve(__dirname, '..')
|
||||||
|
|
||||||
|
async function getDraftRelease (version, skipValidation) {
|
||||||
|
let releaseInfo = await github.repos.getReleases({owner: 'electron', repo: 'electron'})
|
||||||
|
let drafts
|
||||||
|
let versionToCheck
|
||||||
|
if (version) {
|
||||||
|
drafts = releaseInfo.data
|
||||||
|
.filter(release => release.tag_name === version)
|
||||||
|
versionToCheck = version
|
||||||
|
} else {
|
||||||
|
drafts = releaseInfo.data
|
||||||
|
.filter(release => release.draft)
|
||||||
|
versionToCheck = pkgVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
const draft = drafts[0]
|
||||||
|
if (!skipValidation) {
|
||||||
|
failureCount = 0
|
||||||
|
check(drafts.length === 1, 'one draft exists', true)
|
||||||
|
check(draft.tag_name === versionToCheck, `draft release version matches local package.json (${versionToCheck})`)
|
||||||
|
if (versionToCheck.indexOf('beta')) {
|
||||||
|
check(draft.prerelease, 'draft is a prerelease')
|
||||||
|
}
|
||||||
|
check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes')
|
||||||
|
check((failureCount === 0), `Draft release looks good to go.`, true)
|
||||||
|
}
|
||||||
|
return draft
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateReleaseAssets (release) {
|
||||||
|
const requiredAssets = assetsForVersion(release.tag_name).sort()
|
||||||
|
const extantAssets = release.assets.map(asset => asset.name).sort()
|
||||||
|
const downloadUrls = release.assets.map(asset => asset.browser_download_url).sort()
|
||||||
|
|
||||||
|
failureCount = 0
|
||||||
|
requiredAssets.forEach(asset => {
|
||||||
|
check(extantAssets.includes(asset), asset)
|
||||||
|
})
|
||||||
|
check((failureCount === 0), `All required GitHub assets exist for release`, true)
|
||||||
|
|
||||||
|
if (release.draft) {
|
||||||
|
await verifyAssets(release)
|
||||||
|
} else {
|
||||||
|
await verifyShasums(downloadUrls)
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`${fail} error verifyingShasums`, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const s3Urls = s3UrlsForVersion(release.tag_name)
|
||||||
|
await verifyShasums(s3Urls, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function check (condition, statement, exitIfFail = false) {
|
||||||
|
if (condition) {
|
||||||
|
console.log(`${pass} ${statement}`)
|
||||||
|
} else {
|
||||||
|
failureCount++
|
||||||
|
console.log(`${fail} ${statement}`)
|
||||||
|
if (exitIfFail) process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assetsForVersion (version) {
|
||||||
|
const patterns = [
|
||||||
|
`electron-${version}-darwin-x64-dsym.zip`,
|
||||||
|
`electron-${version}-darwin-x64-symbols.zip`,
|
||||||
|
`electron-${version}-darwin-x64.zip`,
|
||||||
|
`electron-${version}-linux-arm-symbols.zip`,
|
||||||
|
`electron-${version}-linux-arm.zip`,
|
||||||
|
`electron-${version}-linux-arm64-symbols.zip`,
|
||||||
|
`electron-${version}-linux-arm64.zip`,
|
||||||
|
`electron-${version}-linux-armv7l-symbols.zip`,
|
||||||
|
`electron-${version}-linux-armv7l.zip`,
|
||||||
|
`electron-${version}-linux-ia32-symbols.zip`,
|
||||||
|
`electron-${version}-linux-ia32.zip`,
|
||||||
|
`electron-${version}-linux-x64-symbols.zip`,
|
||||||
|
`electron-${version}-linux-x64.zip`,
|
||||||
|
`electron-${version}-mas-x64-dsym.zip`,
|
||||||
|
`electron-${version}-mas-x64-symbols.zip`,
|
||||||
|
`electron-${version}-mas-x64.zip`,
|
||||||
|
`electron-${version}-win32-ia32-pdb.zip`,
|
||||||
|
`electron-${version}-win32-ia32-symbols.zip`,
|
||||||
|
`electron-${version}-win32-ia32.zip`,
|
||||||
|
`electron-${version}-win32-x64-pdb.zip`,
|
||||||
|
`electron-${version}-win32-x64-symbols.zip`,
|
||||||
|
`electron-${version}-win32-x64.zip`,
|
||||||
|
`electron-api.json`,
|
||||||
|
`electron.d.ts`,
|
||||||
|
`ffmpeg-${version}-darwin-x64.zip`,
|
||||||
|
`ffmpeg-${version}-linux-arm.zip`,
|
||||||
|
`ffmpeg-${version}-linux-arm64.zip`,
|
||||||
|
`ffmpeg-${version}-linux-armv7l.zip`,
|
||||||
|
`ffmpeg-${version}-linux-ia32.zip`,
|
||||||
|
`ffmpeg-${version}-linux-x64.zip`,
|
||||||
|
`ffmpeg-${version}-mas-x64.zip`,
|
||||||
|
`ffmpeg-${version}-win32-ia32.zip`,
|
||||||
|
`ffmpeg-${version}-win32-x64.zip`,
|
||||||
|
`SHASUMS256.txt`
|
||||||
|
]
|
||||||
|
return patterns
|
||||||
|
}
|
||||||
|
|
||||||
|
function s3UrlsForVersion (version) {
|
||||||
|
const bucket = `https://gh-contractor-zcbenz.s3.amazonaws.com/`
|
||||||
|
const patterns = [
|
||||||
|
`${bucket}atom-shell/dist/${version}/iojs-${version}-headers.tar.gz`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/iojs-${version}.tar.gz`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/node-${version}.tar.gz`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/node.lib`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/win-x64/iojs.lib`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/win-x86/iojs.lib`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/x64/node.lib`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/SHASUMS.txt`,
|
||||||
|
`${bucket}atom-shell/dist/${version}/SHASUMS256.txt`,
|
||||||
|
`${bucket}atom-shell/dist/index.json`
|
||||||
|
]
|
||||||
|
return patterns
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkVersion () {
|
||||||
|
console.log(`Verifying that app version matches package version ${pkgVersion}.`)
|
||||||
|
let startScript = path.join(__dirname, 'start.py')
|
||||||
|
let appVersion = runScript(startScript, ['--version']).trim()
|
||||||
|
check((pkgVersion.indexOf(appVersion) === 0), `App version ${appVersion} matches ` +
|
||||||
|
`package version ${pkgVersion}.`, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function runScript (scriptName, scriptArgs, cwd) {
|
||||||
|
let scriptCommand = `${scriptName} ${scriptArgs.join(' ')}`
|
||||||
|
let scriptOptions = {
|
||||||
|
encoding: 'UTF-8'
|
||||||
|
}
|
||||||
|
if (cwd) {
|
||||||
|
scriptOptions.cwd = cwd
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return execSync(scriptCommand, scriptOptions)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`${fail} Error running ${scriptName}`, err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadNodeShasums () {
|
||||||
|
console.log('Uploading Node SHASUMS file to S3.')
|
||||||
|
let scriptPath = path.join(__dirname, 'upload-node-checksums.py')
|
||||||
|
runScript(scriptPath, ['-v', pkgVersion])
|
||||||
|
console.log(`${pass} Done uploading Node SHASUMS file to S3.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadIndexJson () {
|
||||||
|
console.log('Uploading index.json to S3.')
|
||||||
|
let scriptPath = path.join(__dirname, 'upload-index-json.py')
|
||||||
|
runScript(scriptPath, [])
|
||||||
|
console.log(`${pass} Done uploading index.json to S3.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createReleaseShasums (release) {
|
||||||
|
let fileName = 'SHASUMS256.txt'
|
||||||
|
let existingAssets = release.assets.filter(asset => asset.name === fileName)
|
||||||
|
if (existingAssets.length > 0) {
|
||||||
|
console.log(`${fileName} already exists on GitHub; deleting before creating new file.`)
|
||||||
|
await github.repos.deleteAsset({
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron',
|
||||||
|
id: existingAssets[0].id
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(`${fail} Error deleting ${fileName} on GitHub:`, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.log(`Creating and uploading the release ${fileName}.`)
|
||||||
|
let scriptPath = path.join(__dirname, 'merge-electron-checksums.py')
|
||||||
|
let checksums = runScript(scriptPath, ['-v', pkgVersion])
|
||||||
|
console.log(`${pass} Generated release SHASUMS.`)
|
||||||
|
let filePath = await saveShaSumFile(checksums, fileName)
|
||||||
|
console.log(`${pass} Created ${fileName} file.`)
|
||||||
|
await uploadShasumFile(filePath, fileName, release)
|
||||||
|
console.log(`${pass} Successfully uploaded ${fileName} to GitHub.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadShasumFile (filePath, fileName, release) {
|
||||||
|
let githubOpts = {
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron',
|
||||||
|
id: release.id,
|
||||||
|
filePath,
|
||||||
|
name: fileName
|
||||||
|
}
|
||||||
|
return await github.repos.uploadAsset(githubOpts)
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`${fail} Error uploading ${filePath} to GitHub:`, err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveShaSumFile (checksums, fileName) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
temp.open(fileName, (err, info) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(`${fail} Could not create ${fileName} file`)
|
||||||
|
process.exit(1)
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(info.fd, checksums)
|
||||||
|
fs.close(info.fd, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(`${fail} Could close ${fileName} file`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
resolve(info.path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function publishRelease (release) {
|
||||||
|
let githubOpts = {
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron',
|
||||||
|
id: release.id,
|
||||||
|
tag_name: release.tag_name,
|
||||||
|
draft: false
|
||||||
|
}
|
||||||
|
return await github.repos.editRelease(githubOpts)
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`${fail} Error publishing release:`, err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeRelease (releaseToValidate) {
|
||||||
|
if (releaseToValidate) {
|
||||||
|
console.log(`Validating release ${args.validateRelease}`)
|
||||||
|
let release = await getDraftRelease(args.validateRelease)
|
||||||
|
await validateReleaseAssets(release)
|
||||||
|
} else {
|
||||||
|
checkVersion()
|
||||||
|
let draftRelease = await getDraftRelease()
|
||||||
|
uploadNodeShasums()
|
||||||
|
uploadIndexJson()
|
||||||
|
await createReleaseShasums(draftRelease)
|
||||||
|
// Fetch latest version of release before verifying
|
||||||
|
draftRelease = await getDraftRelease(pkgVersion, true)
|
||||||
|
await validateReleaseAssets(draftRelease)
|
||||||
|
await publishRelease(draftRelease)
|
||||||
|
await cleanupReleaseBranch()
|
||||||
|
console.log(`${pass} SUCCESS!!! Release has been published. Please run ` +
|
||||||
|
`"npm run publish-to-npm" to publish release to npm.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeTempDir () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
temp.mkdir('electron-publish', (err, dirPath) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve(dirPath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyAssets (release) {
|
||||||
|
let downloadDir = await makeTempDir()
|
||||||
|
let githubOpts = {
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/octet-stream'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`Downloading files from GitHub to verify shasums`)
|
||||||
|
let shaSumFile = 'SHASUMS256.txt'
|
||||||
|
let filesToCheck = await Promise.all(release.assets.map(async (asset) => {
|
||||||
|
githubOpts.id = asset.id
|
||||||
|
let assetDetails = await github.repos.getAsset(githubOpts)
|
||||||
|
await downloadFiles(assetDetails.meta.location, downloadDir, false, asset.name)
|
||||||
|
return asset.name
|
||||||
|
})).catch(err => {
|
||||||
|
console.log(`${fail} Error downloading files from GitHub`, err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
filesToCheck = filesToCheck.filter(fileName => fileName !== shaSumFile)
|
||||||
|
let checkerOpts
|
||||||
|
await validateChecksums({
|
||||||
|
algorithm: 'sha256',
|
||||||
|
filesToCheck,
|
||||||
|
fileDirectory: downloadDir,
|
||||||
|
shaSumFile,
|
||||||
|
checkerOpts,
|
||||||
|
fileSource: 'GitHub'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadFiles (urls, directory, quiet, targetName) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let nuggetOpts = {
|
||||||
|
dir: directory
|
||||||
|
}
|
||||||
|
if (quiet) {
|
||||||
|
nuggetOpts.quiet = quiet
|
||||||
|
}
|
||||||
|
if (targetName) {
|
||||||
|
nuggetOpts.target = targetName
|
||||||
|
}
|
||||||
|
nugget(urls, nuggetOpts, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyShasums (urls, isS3) {
|
||||||
|
let fileSource = isS3 ? 'S3' : 'GitHub'
|
||||||
|
console.log(`Downloading files from ${fileSource} to verify shasums`)
|
||||||
|
let downloadDir = await makeTempDir()
|
||||||
|
let filesToCheck = []
|
||||||
|
try {
|
||||||
|
if (!isS3) {
|
||||||
|
await downloadFiles(urls, downloadDir)
|
||||||
|
filesToCheck = urls.map(url => {
|
||||||
|
let currentUrl = new URL(url)
|
||||||
|
return path.basename(currentUrl.pathname)
|
||||||
|
}).filter(file => file.indexOf('SHASUMS') === -1)
|
||||||
|
} else {
|
||||||
|
const s3VersionPath = `/atom-shell/dist/${pkgVersion}/`
|
||||||
|
await Promise.all(urls.map(async (url) => {
|
||||||
|
let currentUrl = new URL(url)
|
||||||
|
let dirname = path.dirname(currentUrl.pathname)
|
||||||
|
let filename = path.basename(currentUrl.pathname)
|
||||||
|
let s3VersionPathIdx = dirname.indexOf(s3VersionPath)
|
||||||
|
if (s3VersionPathIdx === -1 || dirname === s3VersionPath) {
|
||||||
|
if (s3VersionPathIdx !== -1 && filename.indexof('SHASUMS') === -1) {
|
||||||
|
filesToCheck.push(filename)
|
||||||
|
}
|
||||||
|
await downloadFiles(url, downloadDir, true)
|
||||||
|
} else {
|
||||||
|
let subDirectory = dirname.substr(s3VersionPathIdx + s3VersionPath.length)
|
||||||
|
let fileDirectory = path.join(downloadDir, subDirectory)
|
||||||
|
try {
|
||||||
|
fs.statSync(fileDirectory)
|
||||||
|
} catch (err) {
|
||||||
|
fs.mkdirSync(fileDirectory)
|
||||||
|
}
|
||||||
|
filesToCheck.push(path.join(subDirectory, filename))
|
||||||
|
await downloadFiles(url, fileDirectory, true)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`${fail} Error downloading files from ${fileSource}`, err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
console.log(`${pass} Successfully downloaded the files from ${fileSource}.`)
|
||||||
|
let checkerOpts
|
||||||
|
if (isS3) {
|
||||||
|
checkerOpts = { defaultTextEncoding: 'binary' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await validateChecksums({
|
||||||
|
algorithm: 'sha256',
|
||||||
|
filesToCheck,
|
||||||
|
fileDirectory: downloadDir,
|
||||||
|
shaSumFile: 'SHASUMS256.txt',
|
||||||
|
checkerOpts,
|
||||||
|
fileSource
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isS3) {
|
||||||
|
await validateChecksums({
|
||||||
|
algorithm: 'sha1',
|
||||||
|
filesToCheck,
|
||||||
|
fileDirectory: downloadDir,
|
||||||
|
shaSumFile: 'SHASUMS.txt',
|
||||||
|
checkerOpts,
|
||||||
|
fileSource
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateChecksums (validationArgs) {
|
||||||
|
console.log(`Validating checksums for files from ${validationArgs.fileSource} ` +
|
||||||
|
`against ${validationArgs.shaSumFile}.`)
|
||||||
|
let shaSumFilePath = path.join(validationArgs.fileDirectory, validationArgs.shaSumFile)
|
||||||
|
let checker = new sumchecker.ChecksumValidator(validationArgs.algorithm,
|
||||||
|
shaSumFilePath, validationArgs.checkerOpts)
|
||||||
|
await checker.validate(validationArgs.fileDirectory, validationArgs.filesToCheck)
|
||||||
|
.catch(err => {
|
||||||
|
if (err instanceof sumchecker.ChecksumMismatchError) {
|
||||||
|
console.error(`${fail} The checksum of ${err.filename} from ` +
|
||||||
|
`${validationArgs.fileSource} did not match the shasum in ` +
|
||||||
|
`${validationArgs.shaSumFile}`)
|
||||||
|
} else if (err instanceof sumchecker.ChecksumParseError) {
|
||||||
|
console.error(`${fail} The checksum file ${validationArgs.shaSumFile} ` +
|
||||||
|
`from ${validationArgs.fileSource} could not be parsed.`, err)
|
||||||
|
} else if (err instanceof sumchecker.NoChecksumFoundError) {
|
||||||
|
console.error(`${fail} The file ${err.filename} from ` +
|
||||||
|
`${validationArgs.fileSource} was not in the shasum file ` +
|
||||||
|
`${validationArgs.shaSumFile}.`)
|
||||||
|
} else {
|
||||||
|
console.error(`${fail} Error matching files from ` +
|
||||||
|
`${validationArgs.fileSource} shasums in ${validationArgs.shaSumFile}.`, err)
|
||||||
|
}
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
console.log(`${pass} All files from ${validationArgs.fileSource} match ` +
|
||||||
|
`shasums defined in ${validationArgs.shaSumFile}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cleanupReleaseBranch () {
|
||||||
|
console.log(`Cleaning up release branch.`)
|
||||||
|
let errorMessage = `Could not delete local release branch.`
|
||||||
|
let successMessage = `Successfully deleted local release branch.`
|
||||||
|
await callGit(['branch', '-D', 'release'], errorMessage, successMessage)
|
||||||
|
errorMessage = `Could not delete remote release branch.`
|
||||||
|
successMessage = `Successfully deleted remote release branch.`
|
||||||
|
return await callGit(['push', 'origin', ':release'], errorMessage, successMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callGit (args, errorMessage, successMessage) {
|
||||||
|
let gitResult = await GitProcess.exec(args, gitDir)
|
||||||
|
if (gitResult.exitCode === 0) {
|
||||||
|
console.log(`${pass} ${successMessage}`)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
console.log(`${fail} ${errorMessage} ${gitResult.stderr}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeRelease(args.validateRelease)
|
|
@ -2,6 +2,10 @@ const GitHub = require('github')
|
||||||
const github = new GitHub()
|
const github = new GitHub()
|
||||||
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
||||||
|
|
||||||
|
if (process.argv.length < 5) {
|
||||||
|
console.log('Usage: upload-to-github filePath fileName releaseId')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
let filePath = process.argv[2]
|
let filePath = process.argv[2]
|
||||||
let fileName = process.argv[3]
|
let fileName = process.argv[3]
|
||||||
let releaseId = process.argv[4]
|
let releaseId = process.argv[4]
|
||||||
|
@ -13,9 +17,35 @@ let githubOpts = {
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
name: fileName
|
name: fileName
|
||||||
}
|
}
|
||||||
github.repos.uploadAsset(githubOpts).then(() => {
|
|
||||||
process.exit()
|
let retry = 0
|
||||||
}).catch((err) => {
|
|
||||||
console.log(`Error uploading ${fileName} to GitHub:`, err)
|
function uploadToGitHub () {
|
||||||
process.exitCode = 1
|
github.repos.uploadAsset(githubOpts).then(() => {
|
||||||
})
|
console.log(`Successfully uploaded ${fileName} to GitHub.`)
|
||||||
|
process.exit()
|
||||||
|
}).catch((err) => {
|
||||||
|
if (retry < 4) {
|
||||||
|
console.log(`Error uploading ${fileName} to GitHub, will retry. Error was:`, err)
|
||||||
|
retry++
|
||||||
|
github.repos.getRelease(githubOpts).then(release => {
|
||||||
|
let existingAssets = release.data.assets.filter(asset => asset.name === fileName)
|
||||||
|
if (existingAssets.length > 0) {
|
||||||
|
console.log(`${fileName} already exists; will delete before retrying upload.`)
|
||||||
|
github.repos.deleteAsset({
|
||||||
|
owner: 'electron',
|
||||||
|
repo: 'electron',
|
||||||
|
id: existingAssets[0].id
|
||||||
|
}).then(uploadToGitHub).catch(uploadToGitHub)
|
||||||
|
} else {
|
||||||
|
uploadToGitHub()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log(`Error retrying uploading ${fileName} to GitHub:`, err)
|
||||||
|
process.exitCode = 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadToGitHub()
|
||||||
|
|
114
script/upload.py
114
script/upload.py
|
@ -36,17 +36,16 @@ PDB_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'pdb')
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
if not args.publish_release:
|
if not dist_newer_than_head():
|
||||||
if not dist_newer_than_head():
|
run_python_script('create-dist.py')
|
||||||
run_python_script('create-dist.py')
|
|
||||||
|
|
||||||
build_version = get_electron_build_version()
|
build_version = get_electron_build_version()
|
||||||
if not ELECTRON_VERSION.startswith(build_version):
|
if not ELECTRON_VERSION.startswith(build_version):
|
||||||
error = 'Tag name ({0}) should match build version ({1})\n'.format(
|
error = 'Tag name ({0}) should match build version ({1})\n'.format(
|
||||||
ELECTRON_VERSION, build_version)
|
ELECTRON_VERSION, build_version)
|
||||||
sys.stderr.write(error)
|
sys.stderr.write(error)
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
github = GitHub(auth_token())
|
github = GitHub(auth_token())
|
||||||
releases = github.repos(ELECTRON_REPO).releases.get()
|
releases = github.repos(ELECTRON_REPO).releases.get()
|
||||||
|
@ -57,54 +56,44 @@ def main():
|
||||||
tag_exists = True
|
tag_exists = True
|
||||||
break
|
break
|
||||||
|
|
||||||
assert tag_exists == args.overwrite, \
|
if not args.upload_to_s3:
|
||||||
'You have to pass --overwrite to overwrite a published release'
|
assert tag_exists == args.overwrite, \
|
||||||
|
'You have to pass --overwrite to overwrite a published release'
|
||||||
if not args.overwrite:
|
if not args.overwrite:
|
||||||
release = create_or_get_release_draft(github, releases, args.version,
|
release = create_or_get_release_draft(github, releases, args.version,
|
||||||
tag_exists)
|
tag_exists)
|
||||||
|
|
||||||
if args.publish_release:
|
|
||||||
# Upload the Node SHASUMS*.txt.
|
|
||||||
run_python_script('upload-node-checksums.py', '-v', ELECTRON_VERSION)
|
|
||||||
|
|
||||||
# Upload the index.json.
|
|
||||||
run_python_script('upload-index-json.py')
|
|
||||||
|
|
||||||
# Create and upload the Electron SHASUMS*.txt
|
|
||||||
release_electron_checksums(release)
|
|
||||||
|
|
||||||
# Press the publish button.
|
|
||||||
publish_release(github, release['id'])
|
|
||||||
|
|
||||||
# TODO: run publish-to-npm script here
|
|
||||||
|
|
||||||
# Do not upload other files when passed "-p".
|
|
||||||
return
|
|
||||||
|
|
||||||
# Upload Electron with GitHub Releases API.
|
# Upload Electron with GitHub Releases API.
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, DIST_NAME))
|
upload_electron(github, release, os.path.join(DIST_DIR, DIST_NAME),
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, SYMBOLS_NAME))
|
args.upload_to_s3)
|
||||||
|
upload_electron(github, release, os.path.join(DIST_DIR, SYMBOLS_NAME),
|
||||||
|
args.upload_to_s3)
|
||||||
if PLATFORM == 'darwin':
|
if PLATFORM == 'darwin':
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR,
|
upload_electron(github, release, os.path.join(DIST_DIR,
|
||||||
'electron-api.json'))
|
'electron-api.json'), args.upload_to_s3)
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, 'electron.d.ts'))
|
upload_electron(github, release, os.path.join(DIST_DIR, 'electron.d.ts'),
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, DSYM_NAME))
|
args.upload_to_s3)
|
||||||
|
upload_electron(github, release, os.path.join(DIST_DIR, DSYM_NAME),
|
||||||
|
args.upload_to_s3)
|
||||||
elif PLATFORM == 'win32':
|
elif PLATFORM == 'win32':
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, PDB_NAME))
|
upload_electron(github, release, os.path.join(DIST_DIR, PDB_NAME),
|
||||||
|
args.upload_to_s3)
|
||||||
|
|
||||||
# Upload free version of ffmpeg.
|
# Upload free version of ffmpeg.
|
||||||
ffmpeg = get_zip_name('ffmpeg', ELECTRON_VERSION)
|
ffmpeg = get_zip_name('ffmpeg', ELECTRON_VERSION)
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, ffmpeg))
|
upload_electron(github, release, os.path.join(DIST_DIR, ffmpeg),
|
||||||
|
args.upload_to_s3)
|
||||||
|
|
||||||
# Upload chromedriver and mksnapshot for minor version update.
|
# Upload chromedriver and mksnapshot for minor version update.
|
||||||
if parse_version(args.version)[2] == '0':
|
if parse_version(args.version)[2] == '0':
|
||||||
chromedriver = get_zip_name('chromedriver', ELECTRON_VERSION)
|
chromedriver = get_zip_name('chromedriver', ELECTRON_VERSION)
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, chromedriver))
|
upload_electron(github, release, os.path.join(DIST_DIR, chromedriver),
|
||||||
|
args.upload_to_s3)
|
||||||
mksnapshot = get_zip_name('mksnapshot', ELECTRON_VERSION)
|
mksnapshot = get_zip_name('mksnapshot', ELECTRON_VERSION)
|
||||||
upload_electron(github, release, os.path.join(DIST_DIR, mksnapshot))
|
upload_electron(github, release, os.path.join(DIST_DIR, mksnapshot),
|
||||||
|
args.upload_to_s3)
|
||||||
|
|
||||||
if PLATFORM == 'win32' and not tag_exists:
|
if PLATFORM == 'win32' and not tag_exists and not args.upload_to_s3:
|
||||||
# Upload PDBs to Windows symbol server.
|
# Upload PDBs to Windows symbol server.
|
||||||
run_python_script('upload-windows-pdb.py')
|
run_python_script('upload-windows-pdb.py')
|
||||||
|
|
||||||
|
@ -123,6 +112,12 @@ def parse_args():
|
||||||
parser.add_argument('-p', '--publish-release',
|
parser.add_argument('-p', '--publish-release',
|
||||||
help='Publish the release',
|
help='Publish the release',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
|
parser.add_argument('-s', '--upload_to_s3',
|
||||||
|
help='Upload assets to s3 bucket',
|
||||||
|
dest='upload_to_s3',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
required=False)
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,17 +201,17 @@ def create_release_draft(github, tag):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def release_electron_checksums(release):
|
def upload_electron(github, release, file_path, upload_to_s3):
|
||||||
checksums = run_python_script('merge-electron-checksums.py',
|
|
||||||
'-v', ELECTRON_VERSION)
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
# if upload_to_s3 is set, skip github upload.
|
||||||
|
if upload_to_s3:
|
||||||
|
bucket, access_key, secret_key = s3_config()
|
||||||
|
key_prefix = 'electron-artifacts/{0}'.format(release['tag_name'])
|
||||||
|
s3put(bucket, access_key, secret_key, os.path.dirname(file_path),
|
||||||
|
key_prefix, [file_path])
|
||||||
|
upload_sha256_checksum(release['tag_name'], file_path, key_prefix)
|
||||||
|
return
|
||||||
|
|
||||||
def upload_electron(github, release, file_path):
|
|
||||||
# Delete the original file before uploading in CI.
|
# Delete the original file before uploading in CI.
|
||||||
filename = os.path.basename(file_path)
|
filename = os.path.basename(file_path)
|
||||||
if os.environ.has_key('CI'):
|
if os.environ.has_key('CI'):
|
||||||
|
@ -239,7 +234,7 @@ def upload_electron(github, release, file_path):
|
||||||
arm_filename = filename.replace('armv7l', 'arm')
|
arm_filename = filename.replace('armv7l', 'arm')
|
||||||
arm_file_path = os.path.join(os.path.dirname(file_path), arm_filename)
|
arm_file_path = os.path.join(os.path.dirname(file_path), arm_filename)
|
||||||
shutil.copy2(file_path, arm_file_path)
|
shutil.copy2(file_path, arm_file_path)
|
||||||
upload_electron(github, release, arm_file_path)
|
upload_electron(github, release, arm_file_path, upload_to_s3)
|
||||||
|
|
||||||
|
|
||||||
def upload_io_to_github(release, filename, filepath):
|
def upload_io_to_github(release, filename, filepath):
|
||||||
|
@ -249,9 +244,11 @@ def upload_io_to_github(release, filename, filepath):
|
||||||
execute(['node', script_path, filepath, filename, str(release['id'])])
|
execute(['node', script_path, filepath, filename, str(release['id'])])
|
||||||
|
|
||||||
|
|
||||||
def upload_sha256_checksum(version, file_path):
|
def upload_sha256_checksum(version, file_path, key_prefix=None):
|
||||||
bucket, access_key, secret_key = s3_config()
|
bucket, access_key, secret_key = s3_config()
|
||||||
checksum_path = '{}.sha256sum'.format(file_path)
|
checksum_path = '{}.sha256sum'.format(file_path)
|
||||||
|
if key_prefix is None:
|
||||||
|
key_prefix = 'atom-shell/tmp/{0}'.format(version)
|
||||||
sha256 = hashlib.sha256()
|
sha256 = hashlib.sha256()
|
||||||
with open(file_path, 'rb') as f:
|
with open(file_path, 'rb') as f:
|
||||||
sha256.update(f.read())
|
sha256.update(f.read())
|
||||||
|
@ -260,12 +257,7 @@ def upload_sha256_checksum(version, file_path):
|
||||||
with open(checksum_path, 'w') as checksum:
|
with open(checksum_path, 'w') as checksum:
|
||||||
checksum.write('{} *{}'.format(sha256.hexdigest(), filename))
|
checksum.write('{} *{}'.format(sha256.hexdigest(), filename))
|
||||||
s3put(bucket, access_key, secret_key, os.path.dirname(checksum_path),
|
s3put(bucket, access_key, secret_key, os.path.dirname(checksum_path),
|
||||||
'atom-shell/tmp/{0}'.format(version), [checksum_path])
|
key_prefix, [checksum_path])
|
||||||
|
|
||||||
|
|
||||||
def publish_release(github, release_id):
|
|
||||||
data = dict(draft=False)
|
|
||||||
github.repos(ELECTRON_REPO).releases(release_id).patch(data=data)
|
|
||||||
|
|
||||||
|
|
||||||
def auth_token():
|
def auth_token():
|
||||||
|
|
|
@ -11,17 +11,17 @@ const {app, BrowserWindow, ipcMain} = remote
|
||||||
|
|
||||||
const isCI = remote.getGlobal('isCi')
|
const isCI = remote.getGlobal('isCi')
|
||||||
|
|
||||||
describe('electron module', function () {
|
describe('electron module', () => {
|
||||||
it('does not expose internal modules to require', function () {
|
it('does not expose internal modules to require', () => {
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
require('clipboard')
|
require('clipboard')
|
||||||
}, /Cannot find module 'clipboard'/)
|
}, /Cannot find module 'clipboard'/)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('require("electron")', function () {
|
describe('require("electron")', () => {
|
||||||
let window = null
|
let window = null
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
window = new BrowserWindow({
|
window = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
|
@ -29,24 +29,22 @@ describe('electron module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
return closeWindow(window).then(function () { window = null })
|
return closeWindow(window).then(() => { window = null })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('always returns the internal electron module', function (done) {
|
it('always returns the internal electron module', (done) => {
|
||||||
ipcMain.once('answer', function () {
|
ipcMain.once('answer', () => done())
|
||||||
done()
|
window.loadURL(`file://${path.join(__dirname, 'fixtures', 'api', 'electron-module-app', 'index.html')}`)
|
||||||
})
|
|
||||||
window.loadURL('file://' + path.join(__dirname, 'fixtures', 'api', 'electron-module-app', 'index.html'))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app module', function () {
|
describe('app module', () => {
|
||||||
let server, secureUrl
|
let server, secureUrl
|
||||||
const certPath = path.join(__dirname, 'fixtures', 'certificates')
|
const certPath = path.join(__dirname, 'fixtures', 'certificates')
|
||||||
|
|
||||||
before(function () {
|
before(() => {
|
||||||
const options = {
|
const options = {
|
||||||
key: fs.readFileSync(path.join(certPath, 'server.key')),
|
key: fs.readFileSync(path.join(certPath, 'server.key')),
|
||||||
cert: fs.readFileSync(path.join(certPath, 'server.pem')),
|
cert: fs.readFileSync(path.join(certPath, 'server.pem')),
|
||||||
|
@ -58,7 +56,7 @@ describe('app module', function () {
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
}
|
}
|
||||||
|
|
||||||
server = https.createServer(options, function (req, res) {
|
server = https.createServer(options, (req, res) => {
|
||||||
if (req.client.authorized) {
|
if (req.client.authorized) {
|
||||||
res.writeHead(200)
|
res.writeHead(200)
|
||||||
res.end('<title>authorized</title>')
|
res.end('<title>authorized</title>')
|
||||||
|
@ -68,24 +66,24 @@ describe('app module', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
server.listen(0, '127.0.0.1', function () {
|
server.listen(0, '127.0.0.1', () => {
|
||||||
const port = server.address().port
|
const port = server.address().port
|
||||||
secureUrl = `https://127.0.0.1:${port}`
|
secureUrl = `https://127.0.0.1:${port}`
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
after(function () {
|
after(() => {
|
||||||
server.close()
|
server.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.getVersion()', function () {
|
describe('app.getVersion()', () => {
|
||||||
it('returns the version field of package.json', function () {
|
it('returns the version field of package.json', () => {
|
||||||
assert.equal(app.getVersion(), '0.1.0')
|
assert.equal(app.getVersion(), '0.1.0')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.setVersion(version)', function () {
|
describe('app.setVersion(version)', () => {
|
||||||
it('overrides the version', function () {
|
it('overrides the version', () => {
|
||||||
assert.equal(app.getVersion(), '0.1.0')
|
assert.equal(app.getVersion(), '0.1.0')
|
||||||
app.setVersion('test-version')
|
app.setVersion('test-version')
|
||||||
assert.equal(app.getVersion(), 'test-version')
|
assert.equal(app.getVersion(), 'test-version')
|
||||||
|
@ -93,14 +91,14 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.getName()', function () {
|
describe('app.getName()', () => {
|
||||||
it('returns the name field of package.json', function () {
|
it('returns the name field of package.json', () => {
|
||||||
assert.equal(app.getName(), 'Electron Test')
|
assert.equal(app.getName(), 'Electron Test')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.setName(name)', function () {
|
describe('app.setName(name)', () => {
|
||||||
it('overrides the name', function () {
|
it('overrides the name', () => {
|
||||||
assert.equal(app.getName(), 'Electron Test')
|
assert.equal(app.getName(), 'Electron Test')
|
||||||
app.setName('test-name')
|
app.setName('test-name')
|
||||||
assert.equal(app.getName(), 'test-name')
|
assert.equal(app.getName(), 'test-name')
|
||||||
|
@ -108,36 +106,35 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.getLocale()', function () {
|
describe('app.getLocale()', () => {
|
||||||
it('should not be empty', function () {
|
it('should not be empty', () => {
|
||||||
assert.notEqual(app.getLocale(), '')
|
assert.notEqual(app.getLocale(), '')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.isInApplicationsFolder()', function () {
|
describe('app.isInApplicationsFolder()', () => {
|
||||||
it('should be false during tests', function () {
|
it('should be false during tests', () => {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
assert.equal(app.isInApplicationsFolder(), false)
|
assert.equal(app.isInApplicationsFolder(), false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.exit(exitCode)', function () {
|
describe('app.exit(exitCode)', () => {
|
||||||
var appProcess = null
|
let appProcess = null
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
if (appProcess != null) appProcess.kill()
|
if (appProcess != null) appProcess.kill()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits a process exit event with the code', function (done) {
|
it('emits a process exit event with the code', (done) => {
|
||||||
var appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app')
|
const appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app')
|
||||||
var electronPath = remote.getGlobal('process').execPath
|
const electronPath = remote.getGlobal('process').execPath
|
||||||
var output = ''
|
let output = ''
|
||||||
appProcess = ChildProcess.spawn(electronPath, [appPath])
|
appProcess = ChildProcess.spawn(electronPath, [appPath])
|
||||||
appProcess.stdout.on('data', function (data) {
|
appProcess.stdout.on('data', (data) => {
|
||||||
output += data
|
output += data
|
||||||
})
|
})
|
||||||
appProcess.on('close', function (code) {
|
appProcess.on('close', (code) => {
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
assert.notEqual(output.indexOf('Exit event with code: 123'), -1)
|
assert.notEqual(output.indexOf('Exit event with code: 123'), -1)
|
||||||
}
|
}
|
||||||
|
@ -146,7 +143,7 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('closes all windows', function (done) {
|
it('closes all windows', (done) => {
|
||||||
var appPath = path.join(__dirname, 'fixtures', 'api', 'exit-closes-all-windows-app')
|
var appPath = path.join(__dirname, 'fixtures', 'api', 'exit-closes-all-windows-app')
|
||||||
var electronPath = remote.getGlobal('process').execPath
|
var electronPath = remote.getGlobal('process').execPath
|
||||||
appProcess = ChildProcess.spawn(electronPath, [appPath])
|
appProcess = ChildProcess.spawn(electronPath, [appPath])
|
||||||
|
@ -157,7 +154,7 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.makeSingleInstance', function () {
|
describe('app.makeSingleInstance', () => {
|
||||||
it('prevents the second launch of app', function (done) {
|
it('prevents the second launch of app', function (done) {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
const appPath = path.join(__dirname, 'fixtures', 'api', 'singleton')
|
const appPath = path.join(__dirname, 'fixtures', 'api', 'singleton')
|
||||||
|
@ -178,11 +175,11 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.relaunch', function () {
|
describe('app.relaunch', () => {
|
||||||
let server = null
|
let server = null
|
||||||
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'
|
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'
|
||||||
|
|
||||||
beforeEach(function (done) {
|
beforeEach((done) => {
|
||||||
fs.unlink(socketPath, () => {
|
fs.unlink(socketPath, () => {
|
||||||
server = net.createServer()
|
server = net.createServer()
|
||||||
server.listen(socketPath)
|
server.listen(socketPath)
|
||||||
|
@ -190,14 +187,12 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function (done) {
|
afterEach((done) => {
|
||||||
server.close(() => {
|
server.close(() => {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
done()
|
done()
|
||||||
} else {
|
} else {
|
||||||
fs.unlink(socketPath, () => {
|
fs.unlink(socketPath, () => done())
|
||||||
done()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -206,11 +201,9 @@ describe('app module', function () {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
|
|
||||||
let state = 'none'
|
let state = 'none'
|
||||||
server.once('error', (error) => {
|
server.once('error', (error) => done(error))
|
||||||
done(error)
|
|
||||||
})
|
|
||||||
server.on('connection', (client) => {
|
server.on('connection', (client) => {
|
||||||
client.once('data', function (data) {
|
client.once('data', (data) => {
|
||||||
if (String(data) === 'false' && state === 'none') {
|
if (String(data) === 'false' && state === 'none') {
|
||||||
state = 'first-launch'
|
state = 'first-launch'
|
||||||
} else if (String(data) === 'true' && state === 'first-launch') {
|
} else if (String(data) === 'true' && state === 'first-launch') {
|
||||||
|
@ -226,42 +219,36 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.setUserActivity(type, userInfo)', function () {
|
describe('app.setUserActivity(type, userInfo)', () => {
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
it('sets the current activity', function () {
|
it('sets the current activity', () => {
|
||||||
app.setUserActivity('com.electron.testActivity', {testData: '123'})
|
app.setUserActivity('com.electron.testActivity', {testData: '123'})
|
||||||
assert.equal(app.getCurrentActivityType(), 'com.electron.testActivity')
|
assert.equal(app.getCurrentActivityType(), 'com.electron.testActivity')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
xdescribe('app.importCertificate', function () {
|
xdescribe('app.importCertificate', () => {
|
||||||
if (process.platform !== 'linux') return
|
if (process.platform !== 'linux') return
|
||||||
|
|
||||||
var w = null
|
var w = null
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => closeWindow(w).then(() => { w = null }))
|
||||||
return closeWindow(w).then(function () { w = null })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can import certificate into platform cert store', function (done) {
|
it('can import certificate into platform cert store', (done) => {
|
||||||
let options = {
|
let options = {
|
||||||
certificate: path.join(certPath, 'client.p12'),
|
certificate: path.join(certPath, 'client.p12'),
|
||||||
password: 'electron'
|
password: 'electron'
|
||||||
}
|
}
|
||||||
|
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({ show: false })
|
||||||
show: false
|
|
||||||
})
|
|
||||||
|
|
||||||
w.webContents.on('did-finish-load', function () {
|
w.webContents.on('did-finish-load', () => {
|
||||||
assert.equal(w.webContents.getTitle(), 'authorized')
|
assert.equal(w.webContents.getTitle(), 'authorized')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcRenderer.once('select-client-certificate', function (event, webContentsId, list) {
|
ipcRenderer.once('select-client-certificate', (event, webContentsId, list) => {
|
||||||
assert.equal(webContentsId, w.webContents.id)
|
assert.equal(webContentsId, w.webContents.id)
|
||||||
assert.equal(list.length, 1)
|
assert.equal(list.length, 1)
|
||||||
assert.equal(list[0].issuerName, 'Intermediate CA')
|
assert.equal(list[0].issuerName, 'Intermediate CA')
|
||||||
|
@ -271,7 +258,7 @@ describe('app module', function () {
|
||||||
event.sender.send('client-certificate-response', list[0])
|
event.sender.send('client-certificate-response', list[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
app.importCertificate(options, function (result) {
|
app.importCertificate(options, (result) => {
|
||||||
assert(!result)
|
assert(!result)
|
||||||
ipcRenderer.sendSync('set-client-certificate-option', false)
|
ipcRenderer.sendSync('set-client-certificate-option', false)
|
||||||
w.loadURL(secureUrl)
|
w.loadURL(secureUrl)
|
||||||
|
@ -279,79 +266,69 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserWindow events', function () {
|
describe('BrowserWindow events', () => {
|
||||||
var w = null
|
let w = null
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => closeWindow(w).then(() => { w = null }))
|
||||||
return closeWindow(w).then(function () { w = null })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should emit browser-window-focus event when window is focused', function (done) {
|
it('should emit browser-window-focus event when window is focused', (done) => {
|
||||||
app.once('browser-window-focus', function (e, window) {
|
app.once('browser-window-focus', (e, window) => {
|
||||||
assert.equal(w.id, window.id)
|
assert.equal(w.id, window.id)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({ show: false })
|
||||||
show: false
|
|
||||||
})
|
|
||||||
w.emit('focus')
|
w.emit('focus')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should emit browser-window-blur event when window is blured', function (done) {
|
it('should emit browser-window-blur event when window is blured', (done) => {
|
||||||
app.once('browser-window-blur', function (e, window) {
|
app.once('browser-window-blur', (e, window) => {
|
||||||
assert.equal(w.id, window.id)
|
assert.equal(w.id, window.id)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({ show: false })
|
||||||
show: false
|
|
||||||
})
|
|
||||||
w.emit('blur')
|
w.emit('blur')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should emit browser-window-created event when window is created', function (done) {
|
it('should emit browser-window-created event when window is created', (done) => {
|
||||||
app.once('browser-window-created', function (e, window) {
|
app.once('browser-window-created', (e, window) => {
|
||||||
setImmediate(function () {
|
setImmediate(() => {
|
||||||
assert.equal(w.id, window.id)
|
assert.equal(w.id, window.id)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({ show: false })
|
||||||
show: false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should emit web-contents-created event when a webContents is created', function (done) {
|
it('should emit web-contents-created event when a webContents is created', (done) => {
|
||||||
app.once('web-contents-created', function (e, webContents) {
|
app.once('web-contents-created', (e, webContents) => {
|
||||||
setImmediate(function () {
|
setImmediate(() => {
|
||||||
assert.equal(w.webContents.id, webContents.id)
|
assert.equal(w.webContents.id, webContents.id)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({ show: false })
|
||||||
show: false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.setBadgeCount API', function () {
|
describe('app.setBadgeCount API', () => {
|
||||||
const shouldFail = process.platform === 'win32' ||
|
const shouldFail = process.platform === 'win32' ||
|
||||||
(process.platform === 'linux' && !app.isUnityRunning())
|
(process.platform === 'linux' && !app.isUnityRunning())
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
app.setBadgeCount(0)
|
app.setBadgeCount(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns false when failed', function () {
|
it('returns false when failed', () => {
|
||||||
assert.equal(app.setBadgeCount(42), !shouldFail)
|
assert.equal(app.setBadgeCount(42), !shouldFail)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set a badge count', function () {
|
it('should set a badge count', () => {
|
||||||
app.setBadgeCount(42)
|
app.setBadgeCount(42)
|
||||||
assert.equal(app.getBadgeCount(), shouldFail ? 0 : 42)
|
assert.equal(app.getBadgeCount(), shouldFail ? 0 : 42)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('app.get/setLoginItemSettings API', function () {
|
describe('app.get/setLoginItemSettings API', () => {
|
||||||
if (process.platform === 'linux') return
|
if (process.platform === 'linux') return
|
||||||
|
|
||||||
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe')
|
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe')
|
||||||
|
@ -360,17 +337,17 @@ describe('app module', function () {
|
||||||
'--process-start-args', `"--hidden"`
|
'--process-start-args', `"--hidden"`
|
||||||
]
|
]
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
app.setLoginItemSettings({openAtLogin: false})
|
app.setLoginItemSettings({openAtLogin: false})
|
||||||
app.setLoginItemSettings({openAtLogin: false, path: updateExe, args: processStartArgs})
|
app.setLoginItemSettings({openAtLogin: false, path: updateExe, args: processStartArgs})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
app.setLoginItemSettings({openAtLogin: false})
|
app.setLoginItemSettings({openAtLogin: false})
|
||||||
app.setLoginItemSettings({openAtLogin: false, path: updateExe, args: processStartArgs})
|
app.setLoginItemSettings({openAtLogin: false, path: updateExe, args: processStartArgs})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns the login item status of the app', function () {
|
it('returns the login item status of the app', () => {
|
||||||
app.setLoginItemSettings({openAtLogin: true})
|
app.setLoginItemSettings({openAtLogin: true})
|
||||||
assert.deepEqual(app.getLoginItemSettings(), {
|
assert.deepEqual(app.getLoginItemSettings(), {
|
||||||
openAtLogin: true,
|
openAtLogin: true,
|
||||||
|
@ -409,35 +386,35 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isAccessibilitySupportEnabled API', function () {
|
describe('isAccessibilitySupportEnabled API', () => {
|
||||||
it('returns whether the Chrome has accessibility APIs enabled', function () {
|
it('returns whether the Chrome has accessibility APIs enabled', () => {
|
||||||
assert.equal(typeof app.isAccessibilitySupportEnabled(), 'boolean')
|
assert.equal(typeof app.isAccessibilitySupportEnabled(), 'boolean')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getPath(name)', function () {
|
describe('getPath(name)', () => {
|
||||||
it('returns paths that exist', function () {
|
it('returns paths that exist', () => {
|
||||||
assert.equal(fs.existsSync(app.getPath('exe')), true)
|
assert.equal(fs.existsSync(app.getPath('exe')), true)
|
||||||
assert.equal(fs.existsSync(app.getPath('home')), true)
|
assert.equal(fs.existsSync(app.getPath('home')), true)
|
||||||
assert.equal(fs.existsSync(app.getPath('temp')), true)
|
assert.equal(fs.existsSync(app.getPath('temp')), true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error when the name is invalid', function () {
|
it('throws an error when the name is invalid', () => {
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
app.getPath('does-not-exist')
|
app.getPath('does-not-exist')
|
||||||
}, /Failed to get 'does-not-exist' path/)
|
}, /Failed to get 'does-not-exist' path/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns the overridden path', function () {
|
it('returns the overridden path', () => {
|
||||||
app.setPath('music', __dirname)
|
app.setPath('music', __dirname)
|
||||||
assert.equal(app.getPath('music'), __dirname)
|
assert.equal(app.getPath('music'), __dirname)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
xdescribe('select-client-certificate event', function () {
|
xdescribe('select-client-certificate event', () => {
|
||||||
let w = null
|
let w = null
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
@ -446,12 +423,10 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => closeWindow(w).then(() => { w = null }))
|
||||||
return closeWindow(w).then(function () { w = null })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can respond with empty certificate list', function (done) {
|
it('can respond with empty certificate list', (done) => {
|
||||||
w.webContents.on('did-finish-load', function () {
|
w.webContents.on('did-finish-load', () => {
|
||||||
assert.equal(w.webContents.getTitle(), 'denied')
|
assert.equal(w.webContents.getTitle(), 'denied')
|
||||||
server.close()
|
server.close()
|
||||||
done()
|
done()
|
||||||
|
@ -498,7 +473,7 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getFileIcon() API', function () {
|
describe('getFileIcon() API', () => {
|
||||||
// FIXME Get these specs running on Linux CI
|
// FIXME Get these specs running on Linux CI
|
||||||
if (process.platform === 'linux' && isCI) return
|
if (process.platform === 'linux' && isCI) return
|
||||||
|
|
||||||
|
@ -509,16 +484,16 @@ describe('app module', function () {
|
||||||
large: process.platform === 'win32' ? 32 : 48
|
large: process.platform === 'win32' ? 32 : 48
|
||||||
}
|
}
|
||||||
|
|
||||||
it('fetches a non-empty icon', function (done) {
|
it('fetches a non-empty icon', (done) => {
|
||||||
app.getFileIcon(iconPath, function (err, icon) {
|
app.getFileIcon(iconPath, (err, icon) => {
|
||||||
assert.equal(err, null)
|
assert.equal(err, null)
|
||||||
assert.equal(icon.isEmpty(), false)
|
assert.equal(icon.isEmpty(), false)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fetches normal icon size by default', function (done) {
|
it('fetches normal icon size by default', (done) => {
|
||||||
app.getFileIcon(iconPath, function (err, icon) {
|
app.getFileIcon(iconPath, (err, icon) => {
|
||||||
const size = icon.getSize()
|
const size = icon.getSize()
|
||||||
assert.equal(err, null)
|
assert.equal(err, null)
|
||||||
assert.equal(size.height, sizes.normal)
|
assert.equal(size.height, sizes.normal)
|
||||||
|
@ -527,9 +502,9 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('size option', function () {
|
describe('size option', () => {
|
||||||
it('fetches a small icon', function (done) {
|
it('fetches a small icon', (done) => {
|
||||||
app.getFileIcon(iconPath, { size: 'small' }, function (err, icon) {
|
app.getFileIcon(iconPath, { size: 'small' }, (err, icon) => {
|
||||||
const size = icon.getSize()
|
const size = icon.getSize()
|
||||||
assert.equal(err, null)
|
assert.equal(err, null)
|
||||||
assert.equal(size.height, sizes.small)
|
assert.equal(size.height, sizes.small)
|
||||||
|
@ -538,7 +513,7 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fetches a normal icon', function (done) {
|
it('fetches a normal icon', (done) => {
|
||||||
app.getFileIcon(iconPath, { size: 'normal' }, function (err, icon) {
|
app.getFileIcon(iconPath, { size: 'normal' }, function (err, icon) {
|
||||||
const size = icon.getSize()
|
const size = icon.getSize()
|
||||||
assert.equal(err, null)
|
assert.equal(err, null)
|
||||||
|
@ -548,7 +523,7 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fetches a large icon', function (done) {
|
it('fetches a large icon', (done) => {
|
||||||
// macOS does not support large icons
|
// macOS does not support large icons
|
||||||
if (process.platform === 'darwin') return done()
|
if (process.platform === 'darwin') return done()
|
||||||
|
|
||||||
|
@ -563,8 +538,8 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getAppMetrics() API', function () {
|
describe('getAppMetrics() API', () => {
|
||||||
it('returns memory and cpu stats of all running electron processes', function () {
|
it('returns memory and cpu stats of all running electron processes', () => {
|
||||||
const appMetrics = app.getAppMetrics()
|
const appMetrics = app.getAppMetrics()
|
||||||
assert.ok(appMetrics.length > 0, 'App memory info object is not > 0')
|
assert.ok(appMetrics.length > 0, 'App memory info object is not > 0')
|
||||||
const types = []
|
const types = []
|
||||||
|
@ -588,15 +563,15 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getGPUFeatureStatus() API', function () {
|
describe('getGPUFeatureStatus() API', () => {
|
||||||
it('returns the graphic features statuses', function () {
|
it('returns the graphic features statuses', () => {
|
||||||
const features = app.getGPUFeatureStatus()
|
const features = app.getGPUFeatureStatus()
|
||||||
assert.equal(typeof features.webgl, 'string')
|
assert.equal(typeof features.webgl, 'string')
|
||||||
assert.equal(typeof features.gpu_compositing, 'string')
|
assert.equal(typeof features.gpu_compositing, 'string')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('mixed sandbox option', function () {
|
describe('mixed sandbox option', () => {
|
||||||
// FIXME Get these specs running on Linux
|
// FIXME Get these specs running on Linux
|
||||||
if (process.platform === 'linux') return
|
if (process.platform === 'linux') return
|
||||||
|
|
||||||
|
@ -604,7 +579,7 @@ describe('app module', function () {
|
||||||
let server = null
|
let server = null
|
||||||
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'
|
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'
|
||||||
|
|
||||||
beforeEach(function (done) {
|
beforeEach((done) => {
|
||||||
fs.unlink(socketPath, () => {
|
fs.unlink(socketPath, () => {
|
||||||
server = net.createServer()
|
server = net.createServer()
|
||||||
server.listen(socketPath)
|
server.listen(socketPath)
|
||||||
|
@ -612,18 +587,14 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function (done) {
|
afterEach((done) => {
|
||||||
if (appProcess != null) {
|
if (appProcess != null) appProcess.kill()
|
||||||
appProcess.kill()
|
|
||||||
}
|
|
||||||
|
|
||||||
server.close(() => {
|
server.close(() => {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
done()
|
done()
|
||||||
} else {
|
} else {
|
||||||
fs.unlink(socketPath, () => {
|
fs.unlink(socketPath, () => done())
|
||||||
done()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -680,9 +651,9 @@ describe('app module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('disableDomainBlockingFor3DAPIs() API', function () {
|
describe('disableDomainBlockingFor3DAPIs() API', () => {
|
||||||
it('throws when called after app is ready', function () {
|
it('throws when called after app is ready', () => {
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
app.disableDomainBlockingFor3DAPIs()
|
app.disableDomainBlockingFor3DAPIs()
|
||||||
}, /before app is ready/)
|
}, /before app is ready/)
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,11 +6,11 @@ const {closeWindow} = require('./window-helpers')
|
||||||
const {remote} = require('electron')
|
const {remote} = require('electron')
|
||||||
const {BrowserView, BrowserWindow} = remote
|
const {BrowserView, BrowserWindow} = remote
|
||||||
|
|
||||||
describe('BrowserView module', function () {
|
describe('BrowserView module', () => {
|
||||||
var w = null
|
let w = null
|
||||||
var view = null
|
let view = null
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
|
@ -21,68 +21,68 @@ describe('BrowserView module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
if (view) {
|
if (view) {
|
||||||
view.destroy()
|
view.destroy()
|
||||||
view = null
|
view = null
|
||||||
}
|
}
|
||||||
|
|
||||||
return closeWindow(w).then(function () { w = null })
|
return closeWindow(w).then(() => { w = null })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserView.setBackgroundColor()', function () {
|
describe('BrowserView.setBackgroundColor()', () => {
|
||||||
it('does not throw for valid args', function () {
|
it('does not throw for valid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
view.setBackgroundColor('#000')
|
view.setBackgroundColor('#000')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws for invalid args', function () {
|
it('throws for invalid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
view.setBackgroundColor(null)
|
view.setBackgroundColor(null)
|
||||||
}, /conversion failure/)
|
}, /conversion failure/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserView.setAutoResize()', function () {
|
describe('BrowserView.setAutoResize()', () => {
|
||||||
it('does not throw for valid args', function () {
|
it('does not throw for valid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
view.setAutoResize({})
|
view.setAutoResize({})
|
||||||
view.setAutoResize({ width: true, height: false })
|
view.setAutoResize({ width: true, height: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws for invalid args', function () {
|
it('throws for invalid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
view.setAutoResize(null)
|
view.setAutoResize(null)
|
||||||
}, /conversion failure/)
|
}, /conversion failure/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserView.setBounds()', function () {
|
describe('BrowserView.setBounds()', () => {
|
||||||
it('does not throw for valid args', function () {
|
it('does not throw for valid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
view.setBounds({ x: 0, y: 0, width: 1, height: 1 })
|
view.setBounds({ x: 0, y: 0, width: 1, height: 1 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws for invalid args', function () {
|
it('throws for invalid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
view.setBounds(null)
|
view.setBounds(null)
|
||||||
}, /conversion failure/)
|
}, /conversion failure/)
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
view.setBounds({})
|
view.setBounds({})
|
||||||
}, /conversion failure/)
|
}, /conversion failure/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserWindow.setBrowserView()', function () {
|
describe('BrowserWindow.setBrowserView()', () => {
|
||||||
it('does not throw for valid args', function () {
|
it('does not throw for valid args', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
w.setBrowserView(view)
|
w.setBrowserView(view)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not throw if called multiple times with same view', function () {
|
it('does not throw if called multiple times with same view', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
w.setBrowserView(view)
|
w.setBrowserView(view)
|
||||||
w.setBrowserView(view)
|
w.setBrowserView(view)
|
||||||
|
@ -90,8 +90,23 @@ describe('BrowserView module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserView.webContents.getOwnerBrowserWindow()', function () {
|
describe('BrowserWindow.getBrowserView()', () => {
|
||||||
it('points to owning window', function () {
|
it('returns the set view', () => {
|
||||||
|
view = new BrowserView()
|
||||||
|
w.setBrowserView(view)
|
||||||
|
assert.notEqual(view.id, null)
|
||||||
|
let view2 = w.getBrowserView()
|
||||||
|
assert.equal(view2.webContents.id, view.webContents.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns null if none is set', () => {
|
||||||
|
let view = w.getBrowserView()
|
||||||
|
assert.equal(null, view)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('BrowserView.webContents.getOwnerBrowserWindow()', () => {
|
||||||
|
it('points to owning window', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
assert.ok(!view.webContents.getOwnerBrowserWindow())
|
assert.ok(!view.webContents.getOwnerBrowserWindow())
|
||||||
w.setBrowserView(view)
|
w.setBrowserView(view)
|
||||||
|
@ -101,8 +116,8 @@ describe('BrowserView module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserView.fromId()', function () {
|
describe('BrowserView.fromId()', () => {
|
||||||
it('returns the view with given id', function () {
|
it('returns the view with given id', () => {
|
||||||
view = new BrowserView()
|
view = new BrowserView()
|
||||||
w.setBrowserView(view)
|
w.setBrowserView(view)
|
||||||
assert.notEqual(view.id, null)
|
assert.notEqual(view.id, null)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,45 +4,45 @@ const {Buffer} = require('buffer')
|
||||||
|
|
||||||
const {clipboard, nativeImage} = require('electron')
|
const {clipboard, nativeImage} = require('electron')
|
||||||
|
|
||||||
describe('clipboard module', function () {
|
describe('clipboard module', () => {
|
||||||
var fixtures = path.resolve(__dirname, 'fixtures')
|
const fixtures = path.resolve(__dirname, 'fixtures')
|
||||||
|
|
||||||
describe('clipboard.readImage()', function () {
|
describe('clipboard.readImage()', () => {
|
||||||
it('returns NativeImage intance', function () {
|
it('returns NativeImage instance', () => {
|
||||||
var p = path.join(fixtures, 'assets', 'logo.png')
|
const p = path.join(fixtures, 'assets', 'logo.png')
|
||||||
var i = nativeImage.createFromPath(p)
|
const i = nativeImage.createFromPath(p)
|
||||||
clipboard.writeImage(p)
|
clipboard.writeImage(p)
|
||||||
assert.equal(clipboard.readImage().toDataURL(), i.toDataURL())
|
assert.equal(clipboard.readImage().toDataURL(), i.toDataURL())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.readText()', function () {
|
describe('clipboard.readText()', () => {
|
||||||
it('returns unicode string correctly', function () {
|
it('returns unicode string correctly', () => {
|
||||||
var text = '千江有水千江月,万里无云万里天'
|
const text = '千江有水千江月,万里无云万里天'
|
||||||
clipboard.writeText(text)
|
clipboard.writeText(text)
|
||||||
assert.equal(clipboard.readText(), text)
|
assert.equal(clipboard.readText(), text)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.readHTML()', function () {
|
describe('clipboard.readHTML()', () => {
|
||||||
it('returns markup correctly', function () {
|
it('returns markup correctly', () => {
|
||||||
var text = '<string>Hi</string>'
|
const text = '<string>Hi</string>'
|
||||||
var markup = process.platform === 'darwin' ? "<meta charset='utf-8'><string>Hi</string>" : process.platform === 'linux' ? '<meta http-equiv="content-type" ' + 'content="text/html; charset=utf-8"><string>Hi</string>' : '<string>Hi</string>'
|
const markup = process.platform === 'darwin' ? "<meta charset='utf-8'><string>Hi</string>" : process.platform === 'linux' ? '<meta http-equiv="content-type" ' + 'content="text/html; charset=utf-8"><string>Hi</string>' : '<string>Hi</string>'
|
||||||
clipboard.writeHTML(text)
|
clipboard.writeHTML(text)
|
||||||
assert.equal(clipboard.readHTML(), markup)
|
assert.equal(clipboard.readHTML(), markup)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.readRTF', function () {
|
describe('clipboard.readRTF', () => {
|
||||||
it('returns rtf text correctly', function () {
|
it('returns rtf text correctly', () => {
|
||||||
var rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}'
|
const rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}'
|
||||||
clipboard.writeRTF(rtf)
|
clipboard.writeRTF(rtf)
|
||||||
assert.equal(clipboard.readRTF(), rtf)
|
assert.equal(clipboard.readRTF(), rtf)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.readBookmark', function () {
|
describe('clipboard.readBookmark', () => {
|
||||||
it('returns title and url', function () {
|
it('returns title and url', () => {
|
||||||
if (process.platform === 'linux') return
|
if (process.platform === 'linux') return
|
||||||
|
|
||||||
clipboard.writeBookmark('a title', 'https://electron.atom.io')
|
clipboard.writeBookmark('a title', 'https://electron.atom.io')
|
||||||
|
@ -59,14 +59,14 @@ describe('clipboard module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.write()', function () {
|
describe('clipboard.write()', () => {
|
||||||
it('returns data correctly', function () {
|
it('returns data correctly', () => {
|
||||||
var text = 'test'
|
const text = 'test'
|
||||||
var rtf = '{\\rtf1\\utf8 text}'
|
const rtf = '{\\rtf1\\utf8 text}'
|
||||||
var p = path.join(fixtures, 'assets', 'logo.png')
|
const p = path.join(fixtures, 'assets', 'logo.png')
|
||||||
var i = nativeImage.createFromPath(p)
|
const i = nativeImage.createFromPath(p)
|
||||||
var markup = process.platform === 'darwin' ? "<meta charset='utf-8'><b>Hi</b>" : process.platform === 'linux' ? '<meta http-equiv="content-type" ' + 'content="text/html; charset=utf-8"><b>Hi</b>' : '<b>Hi</b>'
|
const markup = process.platform === 'darwin' ? "<meta charset='utf-8'><b>Hi</b>" : process.platform === 'linux' ? '<meta http-equiv="content-type" ' + 'content="text/html; charset=utf-8"><b>Hi</b>' : '<b>Hi</b>'
|
||||||
var bookmark = {title: 'a title', url: 'test'}
|
const bookmark = {title: 'a title', url: 'test'}
|
||||||
clipboard.write({
|
clipboard.write({
|
||||||
text: 'test',
|
text: 'test',
|
||||||
html: '<b>Hi</b>',
|
html: '<b>Hi</b>',
|
||||||
|
@ -85,8 +85,8 @@ describe('clipboard module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.read/writeFindText(text)', function () {
|
describe('clipboard.read/writeFindText(text)', () => {
|
||||||
it('reads and write text to the find pasteboard', function () {
|
it('reads and write text to the find pasteboard', () => {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
clipboard.writeFindText('find this')
|
clipboard.writeFindText('find this')
|
||||||
|
@ -110,8 +110,8 @@ describe('clipboard module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clipboard.readBuffer(format)', function () {
|
describe('clipboard.readBuffer(format)', () => {
|
||||||
it('returns a Buffer of the content for the specified format', function () {
|
it('returns a Buffer of the content for the specified format', () => {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
const buffer = Buffer.from('this is binary', 'utf8')
|
const buffer = Buffer.from('this is binary', 'utf8')
|
||||||
|
|
|
@ -11,46 +11,40 @@ const {closeWindow} = require('./window-helpers')
|
||||||
const {remote} = require('electron')
|
const {remote} = require('electron')
|
||||||
const {app, BrowserWindow, crashReporter} = remote.require('electron')
|
const {app, BrowserWindow, crashReporter} = remote.require('electron')
|
||||||
|
|
||||||
describe('crashReporter module', function () {
|
describe('crashReporter module', () => {
|
||||||
if (process.mas || process.env.DISABLE_CRASH_REPORTER_TESTS) {
|
if (process.mas || process.env.DISABLE_CRASH_REPORTER_TESTS) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalTempDirectory = null
|
let originalTempDirectory = null
|
||||||
var tempDirectory = null
|
let tempDirectory = null
|
||||||
|
|
||||||
before(function () {
|
before(() => {
|
||||||
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
|
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
|
||||||
originalTempDirectory = app.getPath('temp')
|
originalTempDirectory = app.getPath('temp')
|
||||||
app.setPath('temp', tempDirectory)
|
app.setPath('temp', tempDirectory)
|
||||||
})
|
})
|
||||||
|
|
||||||
after(function () {
|
after(() => {
|
||||||
app.setPath('temp', originalTempDirectory)
|
app.setPath('temp', originalTempDirectory)
|
||||||
})
|
})
|
||||||
|
|
||||||
var fixtures = path.resolve(__dirname, 'fixtures')
|
const fixtures = path.resolve(__dirname, 'fixtures')
|
||||||
const generateSpecs = (description, browserWindowOpts) => {
|
const generateSpecs = (description, browserWindowOpts) => {
|
||||||
describe(description, function () {
|
describe(description, () => {
|
||||||
var w = null
|
let w = null
|
||||||
var stopServer = null
|
let stopServer = null
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
stopServer = null
|
stopServer = null
|
||||||
w = new BrowserWindow(Object.assign({
|
w = new BrowserWindow(Object.assign({ show: false }, browserWindowOpts))
|
||||||
show: false
|
|
||||||
}, browserWindowOpts))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => closeWindow(w).then(() => { w = null }))
|
||||||
return closeWindow(w).then(function () { w = null })
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
stopCrashService()
|
stopCrashService()
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function (done) {
|
afterEach((done) => {
|
||||||
if (stopServer != null) {
|
if (stopServer != null) {
|
||||||
stopServer(done)
|
stopServer(done)
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,7 +71,6 @@ describe('crashReporter module', function () {
|
||||||
done: done
|
done: done
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should send minidump when node processes crash', function (done) {
|
it('should send minidump when node processes crash', function (done) {
|
||||||
if (process.env.APPVEYOR === 'True') return done()
|
if (process.env.APPVEYOR === 'True') return done()
|
||||||
if (process.env.TRAVIS === 'true') return done()
|
if (process.env.TRAVIS === 'true') return done()
|
||||||
|
@ -110,7 +103,6 @@ describe('crashReporter module', function () {
|
||||||
done: done
|
done: done
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not send minidump if uploadToServer is false', function (done) {
|
it('should not send minidump if uploadToServer is false', function (done) {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
|
|
||||||
|
@ -123,12 +115,8 @@ describe('crashReporter module', function () {
|
||||||
crashReporter.setUploadToServer(false)
|
crashReporter.setUploadToServer(false)
|
||||||
}
|
}
|
||||||
const testDone = (uploaded) => {
|
const testDone = (uploaded) => {
|
||||||
if (uploaded) {
|
if (uploaded) return done(new Error('Uploaded crash report'))
|
||||||
return done(new Error('Uploaded crash report'))
|
if (process.platform === 'darwin') crashReporter.setUploadToServer(true)
|
||||||
}
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
crashReporter.setUploadToServer(true)
|
|
||||||
}
|
|
||||||
assert(fs.existsSync(dumpFile))
|
assert(fs.existsSync(dumpFile))
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
@ -136,13 +124,10 @@ describe('crashReporter module', function () {
|
||||||
let pollInterval
|
let pollInterval
|
||||||
const pollDumpFile = () => {
|
const pollDumpFile = () => {
|
||||||
fs.readdir(crashesDir, (err, files) => {
|
fs.readdir(crashesDir, (err, files) => {
|
||||||
if (err) {
|
if (err) return
|
||||||
return
|
|
||||||
}
|
|
||||||
const dumps = files.filter((file) => /\.dmp$/.test(file) && !existingDumpFiles.has(file))
|
const dumps = files.filter((file) => /\.dmp$/.test(file) && !existingDumpFiles.has(file))
|
||||||
if (!dumps.length) {
|
if (!dumps.length) return
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.equal(1, dumps.length)
|
assert.equal(1, dumps.length)
|
||||||
dumpFile = path.join(crashesDir, dumps[0])
|
dumpFile = path.join(crashesDir, dumps[0])
|
||||||
clearInterval(pollInterval)
|
clearInterval(pollInterval)
|
||||||
|
@ -179,7 +164,6 @@ describe('crashReporter module', function () {
|
||||||
done: testDone.bind(null, true)
|
done: testDone.bind(null, true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should send minidump with updated extra parameters', function (done) {
|
it('should send minidump with updated extra parameters', function (done) {
|
||||||
if (process.env.APPVEYOR === 'True') return done()
|
if (process.env.APPVEYOR === 'True') return done()
|
||||||
if (process.env.TRAVIS === 'true') return done()
|
if (process.env.TRAVIS === 'true') return done()
|
||||||
|
@ -191,7 +175,7 @@ describe('crashReporter module', function () {
|
||||||
const crashUrl = url.format({
|
const crashUrl = url.format({
|
||||||
protocol: 'file',
|
protocol: 'file',
|
||||||
pathname: path.join(fixtures, 'api', 'crash-restart.html'),
|
pathname: path.join(fixtures, 'api', 'crash-restart.html'),
|
||||||
search: '?port=' + port
|
search: `?port=${port}`
|
||||||
})
|
})
|
||||||
w.loadURL(crashUrl)
|
w.loadURL(crashUrl)
|
||||||
},
|
},
|
||||||
|
@ -210,22 +194,32 @@ describe('crashReporter module', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.start(options)', function () {
|
describe('getProductName', () => {
|
||||||
it('requires that the companyName and submitURL options be specified', function () {
|
it('returns the product name if one is specified', () => {
|
||||||
assert.throws(function () {
|
const name = crashReporter.getProductName()
|
||||||
crashReporter.start({
|
const expectedName = (process.platform === 'darwin') ? 'Electron Test' : 'Zombies'
|
||||||
companyName: 'Missing submitURL'
|
assert.equal(name, expectedName)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getTempDirectory', () => {
|
||||||
|
it('returns temp directory for app if one is specified', () => {
|
||||||
|
const tempDir = crashReporter.getTempDirectory()
|
||||||
|
assert.equal(tempDir, app.getPath('temp'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('start(options)', () => {
|
||||||
|
it('requires that the companyName and submitURL options be specified', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
crashReporter.start({companyName: 'Missing submitURL'})
|
||||||
}, /submitURL is a required option to crashReporter\.start/)
|
}, /submitURL is a required option to crashReporter\.start/)
|
||||||
assert.throws(function () {
|
assert.throws(() => {
|
||||||
crashReporter.start({
|
crashReporter.start({submitURL: 'Missing companyName'})
|
||||||
submitURL: 'Missing companyName'
|
|
||||||
})
|
|
||||||
}, /companyName is a required option to crashReporter\.start/)
|
}, /companyName is a required option to crashReporter\.start/)
|
||||||
})
|
})
|
||||||
|
it('can be called multiple times', () => {
|
||||||
it('can be called multiple times', function () {
|
assert.doesNotThrow(() => {
|
||||||
assert.doesNotThrow(function () {
|
|
||||||
crashReporter.start({
|
crashReporter.start({
|
||||||
companyName: 'Umbrella Corporation',
|
companyName: 'Umbrella Corporation',
|
||||||
submitURL: 'http://127.0.0.1/crashes'
|
submitURL: 'http://127.0.0.1/crashes'
|
||||||
|
@ -239,12 +233,41 @@ describe('crashReporter module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.get/setUploadToServer', function () {
|
describe('getCrashesDirectory', () => {
|
||||||
it('throws an error when called from the renderer process', function () {
|
it('correctly returns the directory', () => {
|
||||||
|
const crashesDir = crashReporter.getCrashesDirectory()
|
||||||
|
let dir
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
dir = `${app.getPath('temp')}/Zombies Crashes`
|
||||||
|
} else {
|
||||||
|
dir = `${app.getPath('temp')}/Electron Test Crashes`
|
||||||
|
}
|
||||||
|
assert.equal(crashesDir, dir)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getUploadedReports', () => {
|
||||||
|
it('returns an array of reports', () => {
|
||||||
|
const reports = crashReporter.getUploadedReports()
|
||||||
|
assert(typeof reports === 'object')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getLastCrashReport', () => {
|
||||||
|
it('correctly returns the most recent report', () => {
|
||||||
|
if (process.env.TRAVIS === 'False') {
|
||||||
|
const reports = crashReporter.getUploadedReports()
|
||||||
|
const lastReport = reports[0]
|
||||||
|
assert(lastReport != null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getUploadToServer()', () => {
|
||||||
|
it('throws an error when called from the renderer process', () => {
|
||||||
assert.throws(() => require('electron').crashReporter.getUploadToServer())
|
assert.throws(() => require('electron').crashReporter.getUploadToServer())
|
||||||
})
|
})
|
||||||
|
it('returns true when uploadToServer is set to true', () => {
|
||||||
it('can be read/set from the main process', function () {
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
crashReporter.start({
|
crashReporter.start({
|
||||||
companyName: 'Umbrella Corporation',
|
companyName: 'Umbrella Corporation',
|
||||||
|
@ -252,13 +275,87 @@ describe('crashReporter module', function () {
|
||||||
uploadToServer: true
|
uploadToServer: true
|
||||||
})
|
})
|
||||||
assert.equal(crashReporter.getUploadToServer(), true)
|
assert.equal(crashReporter.getUploadToServer(), true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it('returns false when uploadToServer is set to false', () => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
crashReporter.start({
|
||||||
|
companyName: 'Umbrella Corporation',
|
||||||
|
submitURL: 'http://127.0.0.1/crashes',
|
||||||
|
uploadToServer: true
|
||||||
|
})
|
||||||
crashReporter.setUploadToServer(false)
|
crashReporter.setUploadToServer(false)
|
||||||
assert.equal(crashReporter.getUploadToServer(), false)
|
assert.equal(crashReporter.getUploadToServer(), false)
|
||||||
} else {
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('setUploadToServer(uploadToServer)', () => {
|
||||||
|
it('throws an error when called from the renderer process', () => {
|
||||||
|
assert.throws(() => require('electron').crashReporter.setUploadToServer('arg'))
|
||||||
|
})
|
||||||
|
it('sets uploadToServer false when called with false', () => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
crashReporter.start({
|
||||||
|
companyName: 'Umbrella Corporation',
|
||||||
|
submitURL: 'http://127.0.0.1/crashes',
|
||||||
|
uploadToServer: true
|
||||||
|
})
|
||||||
|
crashReporter.setUploadToServer(false)
|
||||||
|
assert.equal(crashReporter.getUploadToServer(), false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it('sets uploadToServer true when called with true', () => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
crashReporter.start({
|
||||||
|
companyName: 'Umbrella Corporation',
|
||||||
|
submitURL: 'http://127.0.0.1/crashes',
|
||||||
|
uploadToServer: false
|
||||||
|
})
|
||||||
|
crashReporter.setUploadToServer(true)
|
||||||
assert.equal(crashReporter.getUploadToServer(), true)
|
assert.equal(crashReporter.getUploadToServer(), true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Parameters', () => {
|
||||||
|
it('returns all of the current parameters', () => {
|
||||||
|
crashReporter.start({
|
||||||
|
companyName: 'Umbrella Corporation',
|
||||||
|
submitURL: 'http://127.0.0.1/crashes'
|
||||||
|
})
|
||||||
|
|
||||||
|
const parameters = crashReporter.getParameters()
|
||||||
|
assert(typeof parameters === 'object')
|
||||||
|
})
|
||||||
|
it('adds a parameter to current parameters', () => {
|
||||||
|
// only run on MacOS
|
||||||
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
|
crashReporter.start({
|
||||||
|
companyName: 'Umbrella Corporation',
|
||||||
|
submitURL: 'http://127.0.0.1/crashes'
|
||||||
|
})
|
||||||
|
|
||||||
|
crashReporter.addExtraParameter('hello', 'world')
|
||||||
|
assert('hello' in crashReporter.getParameters())
|
||||||
|
})
|
||||||
|
it('removes a parameter from current parameters', () => {
|
||||||
|
// only run on MacOS
|
||||||
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
|
crashReporter.start({
|
||||||
|
companyName: 'Umbrella Corporation',
|
||||||
|
submitURL: 'http://127.0.0.1/crashes'
|
||||||
|
})
|
||||||
|
|
||||||
|
crashReporter.addExtraParameter('hello', 'world')
|
||||||
|
assert('hello' in crashReporter.getParameters())
|
||||||
|
|
||||||
|
crashReporter.removeExtraParameter('hello')
|
||||||
|
assert(!('hello' in crashReporter.getParameters()))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const waitForCrashReport = () => {
|
const waitForCrashReport = () => {
|
||||||
|
@ -279,9 +376,9 @@ const waitForCrashReport = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const startServer = ({callback, processType, done}) => {
|
const startServer = ({callback, processType, done}) => {
|
||||||
var called = false
|
let called = false
|
||||||
var server = http.createServer((req, res) => {
|
let server = http.createServer((req, res) => {
|
||||||
var form = new multiparty.Form()
|
const form = new multiparty.Form()
|
||||||
form.parse(req, (error, fields) => {
|
form.parse(req, (error, fields) => {
|
||||||
if (error) throw error
|
if (error) throw error
|
||||||
if (called) return
|
if (called) return
|
||||||
|
@ -335,7 +432,7 @@ const startServer = ({callback, processType, done}) => {
|
||||||
for (const connection of activeConnections) {
|
for (const connection of activeConnections) {
|
||||||
connection.destroy()
|
connection.destroy()
|
||||||
}
|
}
|
||||||
server.close(function () {
|
server.close(() => {
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ const path = require('path')
|
||||||
const {closeWindow} = require('./window-helpers')
|
const {closeWindow} = require('./window-helpers')
|
||||||
const BrowserWindow = require('electron').remote.BrowserWindow
|
const BrowserWindow = require('electron').remote.BrowserWindow
|
||||||
|
|
||||||
describe('debugger module', function () {
|
describe('debugger module', () => {
|
||||||
var fixtures = path.resolve(__dirname, 'fixtures')
|
const fixtures = path.resolve(__dirname, 'fixtures')
|
||||||
var w = null
|
let w = null
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
|
@ -16,13 +16,11 @@ describe('debugger module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => closeWindow(w).then(() => { w = null }))
|
||||||
return closeWindow(w).then(function () { w = null })
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('debugger.attach', function () {
|
describe('debugger.attach', () => {
|
||||||
it('fails when devtools is already open', function (done) {
|
it('fails when devtools is already open', (done) => {
|
||||||
w.webContents.on('did-finish-load', function () {
|
w.webContents.on('did-finish-load', () => {
|
||||||
w.webContents.openDevTools()
|
w.webContents.openDevTools()
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach()
|
w.webContents.debugger.attach()
|
||||||
|
@ -31,10 +29,10 @@ describe('debugger module', function () {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
w.webContents.loadURL('file://' + path.join(fixtures, 'pages', 'a.html'))
|
w.webContents.loadURL(`file://${path.join(fixtures, 'pages', 'a.html')}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails when protocol version is not supported', function (done) {
|
it('fails when protocol version is not supported', (done) => {
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach('2.0')
|
w.webContents.debugger.attach('2.0')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -43,20 +41,20 @@ describe('debugger module', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('attaches when no protocol version is specified', function (done) {
|
it('attaches when no protocol version is specified', (done) => {
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach()
|
w.webContents.debugger.attach()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
done('unexpected error : ' + err)
|
done(`unexpected error : ${err}`)
|
||||||
}
|
}
|
||||||
assert(w.webContents.debugger.isAttached())
|
assert(w.webContents.debugger.isAttached())
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('debugger.detach', function () {
|
describe('debugger.detach', () => {
|
||||||
it('fires detach event', function (done) {
|
it('fires detach event', (done) => {
|
||||||
w.webContents.debugger.on('detach', function (e, reason) {
|
w.webContents.debugger.on('detach', (e, reason) => {
|
||||||
assert.equal(reason, 'target closed')
|
assert.equal(reason, 'target closed')
|
||||||
assert(!w.webContents.debugger.isAttached())
|
assert(!w.webContents.debugger.isAttached())
|
||||||
done()
|
done()
|
||||||
|
@ -64,23 +62,23 @@ describe('debugger module', function () {
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach()
|
w.webContents.debugger.attach()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
done('unexpected error : ' + err)
|
done(`unexpected error : ${err}`)
|
||||||
}
|
}
|
||||||
w.webContents.debugger.detach()
|
w.webContents.debugger.detach()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('debugger.sendCommand', function () {
|
describe('debugger.sendCommand', () => {
|
||||||
let server
|
let server
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(() => {
|
||||||
if (server != null) {
|
if (server != null) {
|
||||||
server.close()
|
server.close()
|
||||||
server = null
|
server = null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('retuns response', function (done) {
|
it('retuns response', (done) => {
|
||||||
w.webContents.loadURL('about:blank')
|
w.webContents.loadURL('about:blank')
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach()
|
w.webContents.debugger.attach()
|
||||||
|
@ -100,9 +98,9 @@ describe('debugger module', function () {
|
||||||
w.webContents.debugger.sendCommand('Runtime.evaluate', params, callback)
|
w.webContents.debugger.sendCommand('Runtime.evaluate', params, callback)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fires message event', function (done) {
|
it('fires message event', (done) => {
|
||||||
var url = process.platform !== 'win32'
|
const url = process.platform !== 'win32'
|
||||||
? 'file://' + path.join(fixtures, 'pages', 'a.html')
|
? `file://${path.join(fixtures, 'pages', 'a.html')}`
|
||||||
: 'file:///' + path.join(fixtures, 'pages', 'a.html').replace(/\\/g, '/')
|
: 'file:///' + path.join(fixtures, 'pages', 'a.html').replace(/\\/g, '/')
|
||||||
w.webContents.loadURL(url)
|
w.webContents.loadURL(url)
|
||||||
try {
|
try {
|
||||||
|
@ -110,7 +108,7 @@ describe('debugger module', function () {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
done('unexpected error : ' + err)
|
done('unexpected error : ' + err)
|
||||||
}
|
}
|
||||||
w.webContents.debugger.on('message', function (e, method, params) {
|
w.webContents.debugger.on('message', (e, method, params) => {
|
||||||
if (method === 'Console.messageAdded') {
|
if (method === 'Console.messageAdded') {
|
||||||
assert.equal(params.message.level, 'log')
|
assert.equal(params.message.level, 'log')
|
||||||
assert.equal(params.message.url, url)
|
assert.equal(params.message.url, url)
|
||||||
|
@ -122,25 +120,25 @@ describe('debugger module', function () {
|
||||||
w.webContents.debugger.sendCommand('Console.enable')
|
w.webContents.debugger.sendCommand('Console.enable')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns error message when command fails', function (done) {
|
it('returns error message when command fails', (done) => {
|
||||||
w.webContents.loadURL('about:blank')
|
w.webContents.loadURL('about:blank')
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach()
|
w.webContents.debugger.attach()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
done('unexpected error : ' + err)
|
done(`unexpected error : ${err}`)
|
||||||
}
|
}
|
||||||
w.webContents.debugger.sendCommand('Test', function (err) {
|
w.webContents.debugger.sendCommand('Test', (err) => {
|
||||||
assert.equal(err.message, "'Test' wasn't found")
|
assert.equal(err.message, "'Test' wasn't found")
|
||||||
w.webContents.debugger.detach()
|
w.webContents.debugger.detach()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handles invalid unicode characters in message', function (done) {
|
it('handles invalid unicode characters in message', (done) => {
|
||||||
try {
|
try {
|
||||||
w.webContents.debugger.attach()
|
w.webContents.debugger.attach()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
done('unexpected error : ' + err)
|
done(`unexpected error : ${err}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.webContents.debugger.on('message', (event, method, params) => {
|
w.webContents.debugger.on('message', (event, method, params) => {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue