Merge branch 'master' into native-window-open

This commit is contained in:
Ryohei Ikegami 2017-04-27 12:03:55 +09:00
commit 1d73e84a29
59 changed files with 989 additions and 241 deletions

View file

@ -655,6 +655,14 @@ void App::OnGpuProcessCrashed(base::TerminationStatus status) {
status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
}
base::FilePath App::GetAppPath() const {
return app_path_;
}
void App::SetAppPath(const base::FilePath& app_path) {
app_path_ = app_path;
}
base::FilePath App::GetPath(mate::Arguments* args, const std::string& name) {
bool succeed = false;
base::FilePath path;
@ -959,6 +967,8 @@ void App::BuildPrototype(
.SetMethod("isUnityRunning",
base::Bind(&Browser::IsUnityRunning, browser))
#endif
.SetMethod("setAppPath", &App::SetAppPath)
.SetMethod("getAppPath", &App::GetAppPath)
.SetMethod("setPath", &App::SetPath)
.SetMethod("getPath", &App::GetPath)
.SetMethod("setDesktopName", &App::SetDesktopName)

View file

@ -70,6 +70,8 @@ class App : public AtomBrowserClient::Delegate,
std::unique_ptr<CertificateManagerModel> model);
#endif
base::FilePath GetAppPath() const;
protected:
explicit App(v8::Isolate* isolate);
~App() override;
@ -115,6 +117,8 @@ class App : public AtomBrowserClient::Delegate,
void OnGpuProcessCrashed(base::TerminationStatus status) override;
private:
void SetAppPath(const base::FilePath& app_path);
// Get/Set the pre-defined path in PathService.
base::FilePath GetPath(mate::Arguments* args, const std::string& name);
void SetPath(mate::Arguments* args,
@ -154,6 +158,8 @@ class App : public AtomBrowserClient::Delegate,
// Tracks tasks requesting file icons.
base::CancelableTaskTracker cancelable_task_tracker_;
base::FilePath app_path_;
DISALLOW_COPY_AND_ASSIGN(App);
};

View file

@ -7,6 +7,7 @@
#include "atom/browser/browser.h"
#include "atom/browser/native_window.h"
#include "atom/browser/window_list.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/node_includes.h"
#include "base/time/time.h"
@ -47,7 +48,9 @@ void AutoUpdater::OnError(const std::string& message) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto error = v8::Exception::Error(mate::StringToV8(isolate(), message));
EmitCustomEvent(
mate::EmitEvent(
isolate(),
GetWrapper(),
"error",
error->ToObject(isolate()->GetCurrentContext()).ToLocalChecked(),
// Message is also emitted to keep compatibility with old code.

View file

@ -179,6 +179,13 @@ void OnSetCookie(const Cookies::SetCallback& callback, bool success) {
base::Bind(callback, success ? Cookies::SUCCESS : Cookies::FAILED));
}
// Flushes cookie store in IO thread.
void FlushCookieStoreOnIOThread(
scoped_refptr<net::URLRequestContextGetter> getter,
const base::Closure& callback) {
GetCookieStore(getter)->FlushStore(base::Bind(RunCallbackInUI, callback));
}
// Sets cookie with |details| in IO thread.
void SetCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
std::unique_ptr<base::DictionaryValue> details,
@ -265,6 +272,13 @@ void Cookies::Set(const base::DictionaryValue& details,
base::Bind(SetCookieOnIO, getter, Passed(&copied), callback));
}
void Cookies::FlushStore(const base::Closure& callback) {
auto getter = make_scoped_refptr(request_context_getter_);
content::BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(FlushCookieStoreOnIOThread, getter, callback));
}
void Cookies::OnCookieChanged(const net::CanonicalCookie& cookie,
bool removed,
net::CookieStore::ChangeCause cause) {
@ -286,7 +300,8 @@ void Cookies::BuildPrototype(v8::Isolate* isolate,
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("get", &Cookies::Get)
.SetMethod("remove", &Cookies::Remove)
.SetMethod("set", &Cookies::Set);
.SetMethod("set", &Cookies::Set)
.SetMethod("flushStore", &Cookies::FlushStore);
}
} // namespace api

View file

@ -53,6 +53,7 @@ class Cookies : public mate::TrackableObject<Cookies>,
void Remove(const GURL& url, const std::string& name,
const base::Closure& callback);
void Set(const base::DictionaryValue& details, const SetCallback& callback);
void FlushStore(const base::Closure& callback);
// AtomCookieDelegate::Observer:
void OnCookieChanged(const net::CanonicalCookie& cookie,

View file

@ -191,6 +191,10 @@ void Window::OnWindowClosed() {
FROM_HERE, GetDestroyClosure());
}
void Window::OnWindowEndSession() {
Emit("session-end");
}
void Window::OnWindowBlur() {
Emit("blur");
}
@ -263,6 +267,14 @@ void Window::OnWindowSwipe(const std::string& direction) {
Emit("swipe", direction);
}
void Window::OnWindowSheetBegin() {
Emit("sheet-begin");
}
void Window::OnWindowSheetEnd() {
Emit("sheet-end");
}
void Window::OnWindowEnterHtmlFullScreen() {
Emit("enter-html-full-screen");
}

View file

@ -63,6 +63,7 @@ class Window : public mate::TrackableObject<Window>,
void WillCloseWindow(bool* prevent_default) override;
void WillDestroyNativeObject() override;
void OnWindowClosed() override;
void OnWindowEndSession() override;
void OnWindowBlur() override;
void OnWindowFocus() override;
void OnWindowShow() override;
@ -79,6 +80,8 @@ class Window : public mate::TrackableObject<Window>,
void OnWindowScrollTouchEnd() override;
void OnWindowScrollTouchEdge() override;
void OnWindowSwipe(const std::string& direction) override;
void OnWindowSheetBegin() override;
void OnWindowSheetEnd() override;
void OnWindowEnterFullScreen() override;
void OnWindowLeaveFullScreen() override;
void OnWindowEnterHtmlFullScreen() override;

View file

@ -261,6 +261,11 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
}
#endif
if (delegate_) {
auto app_path = static_cast<api::App*>(delegate_)->GetAppPath();
command_line->AppendSwitchPath(switches::kAppPath, app_path);
}
content::WebContents* web_contents = GetWebContentsFromProcessID(process_id);
if (!web_contents)
return;

View file

@ -474,6 +474,11 @@ void NativeWindow::NotifyWindowClosed() {
observer.OnWindowClosed();
}
void NativeWindow::NotifyWindowEndSession() {
for (NativeWindowObserver& observer : observers_)
observer.OnWindowEndSession();
}
void NativeWindow::NotifyWindowBlur() {
for (NativeWindowObserver& observer : observers_)
observer.OnWindowBlur();
@ -554,6 +559,16 @@ void NativeWindow::NotifyWindowSwipe(const std::string& direction) {
observer.OnWindowSwipe(direction);
}
void NativeWindow::NotifyWindowSheetBegin() {
for (NativeWindowObserver& observer : observers_)
observer.OnWindowSheetBegin();
}
void NativeWindow::NotifyWindowSheetEnd() {
for (NativeWindowObserver& observer : observers_)
observer.OnWindowSheetEnd();
}
void NativeWindow::NotifyWindowLeaveFullScreen() {
for (NativeWindowObserver& observer : observers_)
observer.OnWindowLeaveFullScreen();

View file

@ -218,6 +218,7 @@ class NativeWindow : public base::SupportsUserData,
// Public API used by platform-dependent delegates and observers to send UI
// related notifications.
void NotifyWindowClosed();
void NotifyWindowEndSession();
void NotifyWindowBlur();
void NotifyWindowFocus();
void NotifyWindowShow();
@ -233,6 +234,8 @@ class NativeWindow : public base::SupportsUserData,
void NotifyWindowScrollTouchEnd();
void NotifyWindowScrollTouchEdge();
void NotifyWindowSwipe(const std::string& direction);
void NotifyWindowSheetBegin();
void NotifyWindowSheetEnd();
void NotifyWindowEnterFullScreen();
void NotifyWindowLeaveFullScreen();
void NotifyWindowEnterHtmlFullScreen();

View file

@ -313,6 +313,14 @@ bool ScopedDisableResize::disable_resize_ = false;
return rect;
}
- (void)windowWillBeginSheet:(NSNotification *)notification {
shell_->NotifyWindowSheetBegin();
}
- (void)windowDidEndSheet:(NSNotification *)notification {
shell_->NotifyWindowSheetEnd();
}
@end
@interface AtomPreviewItem : NSObject <QLPreviewItem>

View file

@ -40,6 +40,9 @@ class NativeWindowObserver {
// Called when the window is closed.
virtual void OnWindowClosed() {}
// Called when Windows sends WM_ENDSESSION message
virtual void OnWindowEndSession() {}
// Called when window loses focus.
virtual void OnWindowBlur() {}
@ -67,6 +70,8 @@ class NativeWindowObserver {
virtual void OnWindowScrollTouchEnd() {}
virtual void OnWindowScrollTouchEdge() {}
virtual void OnWindowSwipe(const std::string& direction) {}
virtual void OnWindowSheetBegin() {}
virtual void OnWindowSheetEnd() {}
virtual void OnWindowEnterFullScreen() {}
virtual void OnWindowLeaveFullScreen() {}
virtual void OnWindowEnterHtmlFullScreen() {}

View file

@ -147,6 +147,11 @@ bool NativeWindowViews::PreHandleMSG(
}
return false;
}
case WM_ENDSESSION: {
if (w_param) {
NotifyWindowEndSession();
}
}
default:
return false;
}

View file

@ -17,9 +17,9 @@
<key>CFBundleIconFile</key>
<string>electron.icns</string>
<key>CFBundleVersion</key>
<string>1.6.6</string>
<string>1.6.7</string>
<key>CFBundleShortVersionString</key>
<string>1.6.6</string>
<string>1.6.7</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>

View file

@ -56,8 +56,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,6,6,0
PRODUCTVERSION 1,6,6,0
FILEVERSION 1,6,7,0
PRODUCTVERSION 1,6,7,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -74,12 +74,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron"
VALUE "FileVersion", "1.6.6"
VALUE "FileVersion", "1.6.7"
VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron"
VALUE "ProductVersion", "1.6.6"
VALUE "ProductVersion", "1.6.7"
VALUE "SquirrelAwareVersion", "1"
END
END

View file

@ -310,6 +310,16 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
gfx::Image image;
if (settings.Get("icon", &image)) {
button.image = image.AsNSImage();
std::string iconPosition;
settings.Get("iconPosition", &iconPosition);
if (iconPosition == "left") {
button.imagePosition = NSImageLeft;
} else if (iconPosition == "right") {
button.imagePosition = NSImageRight;
} else {
button.imagePosition = NSImageOverlaps;
}
}
}

View file

@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 1
#define ATOM_MINOR_VERSION 6
#define ATOM_PATCH_VERSION 6
#define ATOM_PATCH_VERSION 7
#define ATOM_VERSION_IS_RELEASE 1

View file

@ -159,6 +159,9 @@ const char kSecureSchemes[] = "secure-schemes";
// The browser process app model ID
const char kAppUserModelId[] = "app-user-model-id";
// The application path
const char kAppPath[] = "app-path";
// The command line switch versions of the options.
const char kBackgroundColor[] = "background-color";
const char kPreloadScript[] = "preload";

View file

@ -81,6 +81,7 @@ extern const char kStandardSchemes[];
extern const char kRegisterServiceWorkerSchemes[];
extern const char kSecureSchemes[];
extern const char kAppUserModelId[];
extern const char kAppPath[];
extern const char kBackgroundColor[];
extern const char kPreloadScript[];

File diff suppressed because one or more lines are too long

45
default_app/renderer.js Normal file
View file

@ -0,0 +1,45 @@
const {remote, shell} = require('electron')
const {execFile} = require('child_process')
const {execPath} = remote.process
document.onclick = function (e) {
e.preventDefault()
if (e.target.tagName === 'A') {
shell.openExternal(e.target.href)
}
return false
}
document.ondragover = document.ondrop = function (e) {
e.preventDefault()
return false
}
const holder = document.getElementById('holder')
holder.ondragover = function () {
this.className = 'hover'
return false
}
holder.ondragleave = holder.ondragend = function () {
this.className = ''
return false
}
holder.ondrop = function (e) {
this.className = ''
e.preventDefault()
const file = e.dataTransfer.files[0]
execFile(execPath, [file.path], {
detached: true, stdio: 'ignore'
}).unref()
return false
}
const version = process.versions.electron
document.querySelector('.header-version').innerText = version
document.querySelector('.command-example').innerText = `${execPath} path-to-your-app`
document.querySelector('.quick-start-link').href = `https://github.com/electron/electron/blob/v${version}/docs/tutorial/quick-start.md`
document.querySelector('.docs-link').href = `https://github.com/electron/electron/tree/v${version}/docs#readme`

View file

@ -1,6 +1,6 @@
# protocol
> 커스텀 프로토콜을 등록하거나 이미 존재하 프로토콜의 요청의 동작을 변경합니다.
> 커스텀 프로토콜을 등록하거나 이미 존재하 프로토콜의 요청의 동작을 변경합니다.
프로세스: [메인](../tutorial/quick-start.md#main-process)

View file

@ -23,7 +23,7 @@ Bir problem(issue) bildirmeden önce sıkça sorulan sorulara göz atın:
## Eğitimler
* [Quick Start](https://github.com/electron/electron/tree/master/docs/tutorial/quick-start.md)
* [Hızlı Başlangıç](tutorial/quick-start.md)
* [Desktop Environment Integration](https://github.com/electron/electron/tree/master/docs/tutorial/desktop-environment-integration.md)
* [Online/Offline Event Detection](https://github.com/electron/electron/tree/master/docs/tutorial/online-offline-events.md)

View file

@ -0,0 +1,252 @@
# Hızlı Başlangıç
Electron, zengin native(işletim sistemi) API runtime sağlayarak, saf Javascript
ile masaüstü uygulamalar geliştirmenize yarar. Electron'u Node.js in, web serverları
yerine masaüstü uygulamalara odaklanmış bir variyasyonu olarak kabul edebilirsiniz.
Bu Electronun, grafik kullanıcı arayüzüne bir JavaScript bağlantısı olduğu
anlamına gelmez. Aksine, Electron web sayfalarını GUI'si olarak kullanır,
yani onu Javascript tarafından kontrol edilen bir minimal Chromium tarayıcısı
olarak görebilirsiniz.
### Ana İşlem
Electron da, `package.json` nun `main` skriptini cağıran işlem _the main process__ dir.
Ana işlemde çalışan bu script, GUI'yi web sayfalarını oluşturarak gösterebilir.
### Render İşlemi
Electron, web sayfalarını görüntülemek için Chromium kullandığından,
aynı zamanda Chromiumun multi-işlem mimarisinide kullanmaktadır.
Electron da calıştırılan her web sayfası, __the renderer process__
adı altında kendi işlemlerini çalıştırırlar.
Normal tarayıcılarda, web sayfaları genellikle korumalı bir ortamda çalışır ve
yerel kaynaklara erişmesine izin verilmez. Bununla birlikte, elektron kullanıcıları,
alt düzey işletim sistemi etkileşimlerine izin veren web sayfalarında
Node.js API'lerini kullanma imkanina sahiplerdir.
### Ana işlem ile render işlemi arasındaki farklar
Ana işlem, `BrowserWindow` örneklerini oluşturarak, web sayfalarını hazır
hale getirir. Her bir `BrowserWindow` örneği web sayfasını kendi render
işleminde çalıştırır. Eger bir `BrowserWindow` örneği ortadan kaldırıldıysa,
bununla bağlantılı olan render işlemide aynı şekilde sonlandırılır.
Ana işlem tüm web sayfaları ve onların ilgili olduğu render işlemlerini yönetir.
Her bir render işlemi izole edilmiş ve sadece kendisinde çalışan web sayfasıyla ilgilenir.
Native GUI ile çalışan API ları web sayfalarında çalıştırmaya izin verilmemektedir,
çünkü native GUI kaynaklarının web sayfalarında yönetimi çok tehlikeli ve
kaynakların sızdırılması gayet kolaydır. Eğer GUI operasyonlarını bir web sayfasinda
gerçekleştirmek istiyorsanız, web sayfasının render işlemi, ana işlem ile, bu tür
işlemleri gerçekleştirilmesini talep etmek için kommunikasyon halinde olmalı.
Electron da ana işlem ve render işlemi arasında birden fazla kommunikasyon yolu vardır.
[`ipcRenderer`](../api/ipc-renderer.md) gibi ve mesaj gönderimi icin
[`ipcMain`](../api/ipc-main.md) modülleri, RPC tarzında kommunikasyon
için ise [remote](../api/remote.md) modülü barındırmakta.
Ayrıca SSS başlıkları [how to share data between web pages][share-data] adresinde bulunabilir.
## İlk Electron uygulamanızı yazın
Electron uygulaması genellikle aşağıdaki gibi yapılandırılmıştır:
```text
your-app/
├── package.json
├── main.js
└── index.html
```
`package.json` dosyasının formatı tamamen Node modüllerine benzer veya aynıdır ve
`main` şeklinde adlandırılmış script uygulamanızı başlatan komut dosyasıdır,
bu komut dosyası daha sonra main process'i çalıştıracak dosyadır.
`package.json` dosyasınızın bir örneği aşağıdaki gibi olabilir:
```json
{
"name" : "your-app",
"version" : "0.1.0",
"main" : "main.js"
}
```
__Note__: Eğer `package.json` dosyasında `main` kısmı bulunmuyorsa, Electron standart olarak
`index.js` dosyasını cağıracaktır.
`main.js` dosyası pencereleri oluşturur, sistem durumlarını handle eder, tipik bir
örnek asağıdaki gibidir:
```javascript
const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')
// Pencere objesini daima global referans olarak tanımla, aksi takdirde,
// eğer JavaScript objesi gereksiz veriler toplayacağı için, pencere
// otomatik olarak kapanacaktır.
let win
function createWindow () {
// Tarayıcı pencerelerini oluşturur.
win = new BrowserWindow({width: 800, height: 600})
// ve uygulamanın index.html sayfasını yükler.
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
// DevTools her uygulama başlatıldığında açılır.
win.webContents.openDevTools()
// Pencere kapandıktan sonra çağrılacaktır.
win.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null
})
}
// Bu metod Electronun başlatılması tamamlandıktan sonra
// çagrılacak ve yeni tarayıcı pencereleri açmaya hazır hale gelecektir.
// Bazı API lar sadece bu event gerçekleştikten sonra kullanılabilir.
app.on('ready', createWindow)
// Eğer tüm pencereler kapandıysa, çıkış yap.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow()
}
})
// Bu sayfada, uygulamanızın spesifik main process kodlarını dahil edebilirsiniz.
// Aynı zamanda bu kodları ayrı dosyalar halinde oluştura bilir
// ve buraya require yoluyla ekleye bilirsiniz.
```
Son olarak `index.html` yani göstermek istediğiniz web sayfası:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
</body>
</html>
```
## Uygulamanızı çalıştırın
`main.js`, `index.html`, ve `package.json` dosyalarını oluşturduktan sonra,
uygulamanızı lokal olarak test ederek, doğru çalışıp çalışmadığını
test etmek isteye bilirsiniz. O halde aşağıdaki yönergeleri takip edin:
### `electron`
[`electron`](https://github.com/electron-userland/electron-prebuilt),
Electron'un pre-compiled versiyonunu içeren bir `npm` modülüdür.
Eğer bunu global olarak `npm` yoluyla yüklediyseniz, o halde sadece aşağıdaki komutu
uygulamanızın kaynak klasöründe çalıstırmanız yeterlidir:
```bash
electron .
```
Eğer lokal olarak yüklediyseniz, o zaman aşağıda ki gibi
çalıştırın:
#### macOS / Linux
```bash
$ ./node_modules/.bin/electron .
```
#### Windows
```bash
$ .\node_modules\.bin\electron .
```
### Manuel olarak indirilmiş Electron mimarisi
Eğer Electronu manuel olarak indirdiyseniz, aynı zamanda dahili olan
mimariyide kullanarak, uygulamanızı çalıştıra bilirsiniz.
#### Windows
```bash
$ .\electron\electron.exe your-app\
```
#### Linux
```bash
$ ./electron/electron your-app/
```
#### macOS
```bash
$ ./Electron.app/Contents/MacOS/Electron your-app/
```
`Electron.app` Electron un dağı₺tım paketinin bir parçasıdır,
bunu [adresinden](https://github.com/electron/electron/releases) indirebilirsiniz.
### Dağıtım olarak çalıştır
Uygulamanızı yazdıktan sonra, bir dağıtım oluşturmak için
[Application Distribution](./application-distribution.md)
sayfasında ki yönergeleri izleyin ve daha sonra arşivlenmiş uygulamayı çalıştırın.
### Örneği deneyin
[`electron/electron-quick-start`](https://github.com/electron/electron-quick-start) repository klonlayarak bu eğitimdeki kodu çalıştıra bilirsiniz.
**Note**: Bu işlemleri uygulamak için [Git](https://git-scm.com) ve [Node.js](https://nodejs.org/en/download/) ([npm](https://npmjs.org) da bununla birlikte gelir) sisteminizde yüklü olması gerekmektedir.
```bash
# Repository klonla
$ git clone https://github.com/electron/electron-quick-start
# Electron repositorye git
$ cd electron-quick-start
# Gerekli kütüphaneleri yükle
$ npm install
# Uygulamayı çalıştır
$ npm start
```
Daha fazla örnek uygulama için, harika electron topluluğu tarafından oluşturulan,
[list of boilerplates](https://electron.atom.io/community/#boilerplates)
sayfasını ziyaret edin.
[share-data]: ../faq.md#how-to-share-data-between-web-pages

View file

@ -1,4 +1,7 @@
# shell
> 使用系统默认应用管理文件和 URL .
进程: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
`shell` 模块提供了集成其他桌面客户端的关联功能.
@ -11,7 +14,7 @@ const {shell} = require('electron')
shell.openExternal('https://github.com')
```
## Methods
## 方法
`shell` 模块包含以下函数:
@ -19,27 +22,60 @@ shell.openExternal('https://github.com')
* `fullPath` String
打开文件所在文件夹,一般情况下还会选中它.
Returns `Boolean` -
是否成功打开文件所在文件夹,一般情况下还会选中它.
### `shell.openItem(fullPath)`
* `fullPath` String
以默认打开方式打开文件.
Returns `Boolean` - 是否成功的以默认打开方式打开文件.
### `shell.openExternal(url)`
* `url` String
* `options` Object (可选) _macOS_
* `activate` Boolean - `true` 让打开的应用在前面显示,默认为 `true`.
* `callback` Function (可选) - 如果指定将执行异步打开. _macOS_
* `error` Error
以系统默认设置打开外部协议.(例如,mailto: somebody@somewhere.io会打开用户默认的邮件客户端)
Returns `Boolean` - 应用程序是否打开URL.如果指定了 callback 回调方法, 则返回 true.
以系统默认设置打开外部协议.(例如,mailto: URLs 会打开用户默认的邮件客户端)
### `shell.moveItemToTrash(fullPath)`
* `fullPath` String
Returns `Boolean` - 文件是否成功移动到垃圾桶
删除指定路径文件,并返回此操作的状态值(boolean类型).
### `shell.beep()`
播放 beep 声音.
### `shell.writeShortcutLink(shortcutPath[, operation], options)` _Windows_
* `shortcutPath` String
* `operation` String (可选) - 默认为 `create`, 可以为下列的值:
* `create` - 创建一个新的快捷方式,如果存在的话会覆盖.
* `update` - 仅在现有快捷方式上更新指定属性.
* `replace` - 覆盖现有的快捷方式,如果快捷方式不存在则会失败.
* `options` [ShortcutDetails](structures/shortcut-details.md)
Returns `Boolean` - 快捷方式是否成功创建
`shortcutPath` 创建或更新快捷链接.
### `shell.readShortcutLink(shortcutPath)` _Windows_
* `shortcutPath` String
Returns [`ShortcutDetails`](structures/shortcut-details.md)
读取 `shortcutPath` 的快捷连接的信息.
发生错误时,会抛出异常信息.

View file

@ -377,6 +377,11 @@ window.onbeforeunload = (e) => {
Emitted when the window is closed. After you have received this event you should
remove the reference to the window and avoid using it any more.
#### Event: 'session-end' _Windows_
Emitted when window session is going to end due to force shutdown or machine restart
or session log off.
#### Event: 'unresponsive'
Emitted when the web page becomes unresponsive.
@ -499,6 +504,14 @@ Returns:
Emitted on 3-finger swipe. Possible directions are `up`, `right`, `down`, `left`.
#### Event: 'sheet-begin' _macOS_
Emitted when the window opens a sheet.
#### Event: 'sheet-end' _macOS_
Emitted when the window has closed a sheet.
### Static Methods
The `BrowserWindow` class has the following static methods:

View file

@ -104,3 +104,9 @@ on complete.
Removes the cookies matching `url` and `name`, `callback` will called with
`callback()` on complete.
#### `cookies.flushStore(callback)`
* `callback` Function
Writes any unwritten cookies data to disk.

View file

@ -100,6 +100,8 @@ Returns `Boolean` - Whether the download is paused.
Resumes the download that has been paused.
**Note:** To enable resumable downloads the server you are downloading from must support range requests and provide both `Last-Modified` and `ETag` header values. Otherwise `resume()` will dismiss previously received bytes and restart the download from the beginning.
#### `downloadItem.canResume()`
Resumes `Boolean` - Whether the download can resume.

View file

@ -70,7 +70,7 @@ The `role` property can have following values:
* `editMenu` - Whole default "Edit" menu (Undo, Copy, etc.)
* `windowMenu` - Whole default "Window" menu (Minimize, Close, etc.)
The following additional roles are avaiable on macOS:
The following additional roles are available on macOS:
* `about` - Map to the `orderFrontStandardAboutPanel` action
* `hide` - Map to the `hide` action
@ -120,4 +120,4 @@ A String representing the menu items visible label
#### `menuItem.click`
A Function that is fired when the MenuItem recieves a click event
A Function that is fired when the MenuItem receives a click event

View file

@ -28,6 +28,10 @@ effect on macOS.
Returns `Menu` - The application menu, if set, or `null`, if not set.
**Note:** The returned `Menu` instance doesn't support dynamic addition or
removal of menu items. [Instance properties](#instance-properties) can still
be dynamically modified.
#### `Menu.sendActionToFirstResponder(action)` _macOS_
* `action` String

View file

@ -11,6 +11,7 @@ Process: [Main](../tutorial/quick-start.md#main-process)
* `backgroundColor` String (optional) - Button background color in hex format,
i.e `#ABCDEF`.
* `icon` [NativeImage](native-image.md) (optional) - Button icon.
* `iconPosition` String - Can be `left`, `right` or `overlay`.
* `click` Function (optional) - Function to call when the button is clicked.
### Instance Properties

View file

@ -16,6 +16,10 @@ Creates a new touch bar with the specified items. Use
**Note:** The TouchBar API is currently experimental and may change or be
removed in future Electron releases.
**Tip:** If you don't have a MacBook with Touch Bar, you can use
[Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator)
to test Touch Bar usage in your app.
### Instance Properties
The following properties are available on instances of `TouchBar`:

View file

@ -12,7 +12,8 @@ rendered.
Unlike an `iframe`, the `webview` runs in a separate process than your
app. It doesn't have the same permissions as your web page and all interactions
between your app and embedded content will be asynchronous. This keeps your app
safe from the embedded content.
safe from the embedded content. **Note:** Most methods called on the
webview from the host page require a syncronous call to the main process.
For security purposes, `webview` can only be used in `BrowserWindow`s that have
`nodeIntegration` enabled.

View file

@ -30,6 +30,10 @@ has to be a field of `BrowserWindow`'s options.
* Node integration will always be disabled in the opened `window` if it is
disabled on the parent window.
* Context isolation will always be enabled in the opened `window` if it is
enabled on the parent window.
* JavaScript will always be disabled in the opened `window` if it is disabled on
the parent window.
* Non-standard features (that are not handled by Chromium or Electron) given in
`features` will be passed to any registered `webContent`'s `new-window` event
handler in the `additionalFeatures` argument.

View file

@ -10,6 +10,9 @@ Follow the guidelines below for building Electron on Windows.
* [Python 2.7](http://www.python.org/download/releases/2.7/)
* [Node.js](http://nodejs.org/download/)
* [Git](http://git-scm.com)
* [Debugging Tools for Windows](https://msdn.microsoft.com/en-us/library/windows/hardware/ff551063.aspx)
if you plan on creating a full distribution since `symstore.exe` is used for
creating a symbol store from `.pdb` files.
If you don't currently have a Windows installation,
[dev.microsoftedge.com](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/)

View file

@ -148,7 +148,7 @@ npm uninstall electron
npm uninstall -g electron
```
However if your are using the built-in module but still getting this error, it
However if you are using the built-in module but still getting this error, it
is very likely you are using the module in the wrong process. For example
`electron.app` can only be used in the main process, while `electron.webFrame`
is only available in renderer processes.

View file

@ -36,8 +36,8 @@ are fine differences.
* On Windows 8.1 and Windows 8, a shortcut to your app, with a [Application User
Model ID][app-user-model-id], must be installed to the Start screen. Note,
however, that it does not need to be pinned to the Start screen.
* On Windows 7, notifications are not supported. You can however send
"balloon notifications" using the [Tray API][tray-balloon].
* On Windows 7, notifications work via a custom implemetation which visually
resembles the native one on newer systems.
Furthermore, the maximum length for the notification body is 250 characters,
with the Windows team recommending that notifications should be kept to 200

View file

@ -4,7 +4,7 @@
'product_name%': 'Electron',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '1.6.6',
'version%': '1.6.7',
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
},
'includes': [

View file

@ -89,6 +89,7 @@
'default_app/index.html',
'default_app/main.js',
'default_app/package.json',
'default_app/renderer.js',
],
'lib_sources': [
'atom/app/atom_content_client.cc',

View file

@ -12,11 +12,7 @@ const {EventEmitter} = require('events')
Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
let appPath = null
Object.assign(app, {
getAppPath () { return appPath },
setAppPath (path) { appPath = path },
setApplicationMenu (menu) {
return Menu.setApplicationMenu(menu)
},

View file

@ -159,10 +159,11 @@ TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
super()
if (config == null) config = {}
this.type = 'button'
const {click, icon, label, backgroundColor} = config
const {click, icon, iconPosition, label, backgroundColor} = config
this._addLiveProperty('label', label)
this._addLiveProperty('backgroundColor', backgroundColor)
this._addLiveProperty('icon', icon)
this._addLiveProperty('iconPosition', iconPosition)
if (typeof click === 'function') {
this.onInteraction = () => {
config.click()

View file

@ -5,7 +5,7 @@ const {isSameOrigin} = process.atomBinding('v8_util')
const parseFeaturesString = require('../common/parse-features-string')
const hasProp = {}.hasOwnProperty
const frameToGuest = {}
const frameToGuest = new Map()
// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) {
@ -48,11 +48,16 @@ const mergeBrowserWindowOptions = function (embedder, options) {
options.webPreferences.nodeIntegration = false
}
// Enable context isolation on child window if enable on parent window
// Enable context isolation on child window if enabled on parent window
if (embedder.getWebPreferences().contextIsolation === true) {
options.webPreferences.contextIsolation = true
}
// Disable JavaScript on child window if disabled on parent window
if (embedder.getWebPreferences().javascript === false) {
options.webPreferences.javascript = false
}
// Sets correct openerId here to give correct options to 'new-window' event handler
options.webPreferences.openerId = embedder.id
@ -87,10 +92,10 @@ const setupGuest = function (embedder, frameName, guest, options) {
guest.once('closed', closedByUser)
}
if (frameName) {
frameToGuest[frameName] = guest
frameToGuest.set(frameName, guest)
guest.frameName = frameName
guest.once('closed', function () {
delete frameToGuest[frameName]
frameToGuest.delete(frameName)
})
}
return guestId
@ -98,7 +103,7 @@ const setupGuest = function (embedder, frameName, guest, options) {
// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, frameName, options, postData) {
let guest = frameToGuest[frameName]
let guest = frameToGuest.get(frameName)
if (frameName && (guest != null)) {
guest.loadURL(url)
return guest.webContents.id
@ -186,7 +191,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName,
const options = {}
const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload']
const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload', 'javascript', 'contextIsolation']
const disposition = 'new-window'
// Used to store additional features
@ -197,6 +202,10 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName,
if (value === undefined) {
additionalFeatures.push(key)
} else {
// Don't allow webPreferences to be set since it must be an object
// that cannot be directly overridden
if (key === 'webPreferences') return
if (webPreferences.includes(key)) {
if (options.webPreferences == null) {
options.webPreferences = {}

View file

@ -56,7 +56,7 @@ class CrashReporter {
const env = {
ELECTRON_INTERNAL_CRASH_SERVICE: 1
}
spawn(process.execPath, args, {
this._crashServiceProcess = spawn(process.execPath, args, {
env: env,
detached: true
})

View file

@ -56,6 +56,7 @@ electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (ev
let nodeIntegration = 'false'
let preloadScript = null
let isBackgroundPage = false
let appPath = null
for (let arg of process.argv) {
if (arg.indexOf('--guest-instance-id=') === 0) {
// This is a guest web view.
@ -69,13 +70,15 @@ for (let arg of process.argv) {
preloadScript = arg.substr(arg.indexOf('=') + 1)
} else if (arg === '--background-page') {
isBackgroundPage = true
} else if (arg.indexOf('--app-path=') === 0) {
appPath = arg.substr(arg.indexOf('=') + 1)
}
}
if (window.location.protocol === 'chrome-devtools:') {
// Override some inspector APIs.
require('./inspector')
nodeIntegration = 'true'
nodeIntegration = 'false'
} else if (window.location.protocol === 'chrome-extension:') {
// Add implementations of chrome API.
require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
@ -116,6 +119,11 @@ if (nodeIntegration === 'true') {
} else {
global.__filename = __filename
global.__dirname = __dirname
if (appPath) {
// Search for module under the app directory
module.paths = module.paths.concat(Module._nodeModulePaths(appPath))
}
}
// Redirect window.onerror to uncaughtException.

View file

@ -32,6 +32,13 @@ const resolveURL = function (url) {
return a.href
}
// Use this method to ensure values expected as strings in the main process
// are convertible to strings in the renderer process. This ensures exceptions
// converting values to strings are thrown in this process.
const toString = (value) => {
return value != null ? `${value}` : value
}
const windowProxies = {}
const getOrCreateProxy = (ipcRenderer, guestId) => {
@ -82,7 +89,7 @@ function BrowserWindowProxy (ipcRenderer, guestId) {
}
this.postMessage = (message, targetOrigin) => {
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', guestId, message, targetOrigin, window.location.origin)
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', guestId, message, toString(targetOrigin), window.location.origin)
}
this.eval = (...args) => {
@ -113,7 +120,7 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNative
if (url != null && url !== '') {
url = resolveURL(url)
}
const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, features)
const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
if (guestId != null) {
return getOrCreateProxy(ipcRenderer, guestId)
} else {
@ -123,11 +130,11 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNative
}
window.alert = function (message, title) {
ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_ALERT', message, title)
ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_ALERT', toString(message), toString(title))
}
window.confirm = function (message, title) {
return ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CONFIRM', message, title)
return ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CONFIRM', toString(message), toString(title))
}
// But we do not support prompt().
@ -159,7 +166,7 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNative
}
window.history.go = function (offset) {
sendHistoryOperation(ipcRenderer, 'goToOffset', offset)
sendHistoryOperation(ipcRenderer, 'goToOffset', +offset)
}
defineProperty(window.history, 'length', {

View file

@ -1,6 +1,6 @@
{
"name": "electron",
"version": "1.6.6",
"version": "1.6.7",
"devDependencies": {
"asar": "^0.11.0",
"browserify": "^13.1.0",

View file

@ -86,8 +86,6 @@ def main():
run_script('create-dist.py')
run_script('upload.py')
else:
if PLATFORM == 'win32':
os.environ['OUTPUT_TO_FILE'] = 'output.log'
run_script('build.py', ['-c', 'D'])
if PLATFORM == 'win32' or target_arch == 'x64':
run_script('test.py', ['--ci'])

View file

@ -1,6 +1,6 @@
const assert = require('assert')
const autoUpdater = require('electron').remote.autoUpdater
const ipcRenderer = require('electron').ipcRenderer
const {autoUpdater} = require('electron').remote
const {ipcRenderer} = require('electron')
// Skip autoUpdater tests in MAS build.
if (!process.mas) {
@ -64,5 +64,25 @@ if (!process.mas) {
autoUpdater.quitAndInstall()
})
})
describe('error event', function () {
it('serializes correctly over the remote module', function (done) {
if (process.platform === 'linux') {
return done()
}
autoUpdater.once('error', function (error) {
assert.equal(error instanceof Error, true)
assert.deepEqual(Object.getOwnPropertyNames(error), ['stack', 'message', 'name'])
done()
})
autoUpdater.setFeedURL('')
if (process.platform === 'win32') {
autoUpdater.checkForUpdates()
}
})
})
})
}

View file

@ -1228,6 +1228,54 @@ describe('BrowserWindow module', function () {
})
})
describe('sheet-begin event', function () {
if (process.platform !== 'darwin') {
return
}
let sheet = null
afterEach(function () {
return closeWindow(sheet, {assertSingleWindow: false}).then(function () { sheet = null })
})
it('emits when window opens a sheet', function (done) {
w.show()
w.once('sheet-begin', function () {
sheet.close()
done()
})
sheet = new BrowserWindow({
modal: true,
parent: w
})
})
})
describe('sheet-end event', function () {
if (process.platform !== 'darwin') {
return
}
let sheet = null
afterEach(function () {
return closeWindow(sheet, {assertSingleWindow: false}).then(function () { sheet = null })
})
it('emits when window has closed a sheet', function (done) {
w.show()
sheet = new BrowserWindow({
modal: true,
parent: w
})
w.once('sheet-end', function () {
done()
})
sheet.close()
})
})
describe('beginFrameSubscription method', function () {
// This test is too slow, only test it on CI.
if (!isCI) return
@ -1511,13 +1559,19 @@ describe('BrowserWindow module', function () {
// Only implemented on macOS.
if (process.platform !== 'darwin') return
it('can be changed with setKiosk method', function () {
it('can be changed with setKiosk method', function (done) {
w.destroy()
w = new BrowserWindow()
w.setKiosk(true)
assert.equal(w.isKiosk(), true)
w.setKiosk(false)
assert.equal(w.isKiosk(), false)
w.once('enter-full-screen', () => {
w.setKiosk(false)
assert.equal(w.isKiosk(), false)
})
w.once('leave-full-screen', () => {
done()
})
})
})

View file

@ -33,8 +33,10 @@ describe('crashReporter module', function () {
const generateSpecs = (description, browserWindowOpts) => {
describe(description, function () {
var w = null
var stopServer = null
beforeEach(function () {
stopServer = null
w = new BrowserWindow(Object.assign({
show: false
}, browserWindowOpts))
@ -44,13 +46,25 @@ describe('crashReporter module', function () {
return closeWindow(w).then(function () { w = null })
})
afterEach(function () {
stopCrashService()
})
afterEach(function (done) {
if (stopServer != null) {
stopServer(done)
} else {
done()
}
})
it('should send minidump when renderer crashes', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
this.timeout(120000)
startServer({
stopServer = startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
@ -70,11 +84,26 @@ describe('crashReporter module', function () {
this.timeout(120000)
startServer({
stopServer = startServer({
callback (port) {
const crashesDir = path.join(app.getPath('temp'), `${app.getName()} Crashes`)
const crashesDir = path.join(app.getPath('temp'), `${process.platform === 'win32' ? 'Zombies' : app.getName()} Crashes`)
const version = app.getVersion()
const crashPath = path.join(fixtures, 'module', 'crash.js')
if (process.platform === 'win32') {
const crashServiceProcess = childProcess.spawn(process.execPath, [
`--reporter-url=http://127.0.0.1:${port}`,
'--application-name=Zombies',
`--crashes-directory=${crashesDir}`
], {
env: {
ELECTRON_INTERNAL_CRASH_SERVICE: 1
},
detached: true
})
remote.process.crashServicePid = crashServiceProcess.pid
}
childProcess.fork(crashPath, [port, version, crashesDir], {silent: true})
},
processType: 'browser',
@ -85,7 +114,6 @@ describe('crashReporter module', function () {
it('should not send minidump if uploadToServer is false', function (done) {
this.timeout(120000)
let server
let dumpFile
let crashesDir = crashReporter.getCrashesDirectory()
const existingDumpFiles = new Set()
@ -96,9 +124,8 @@ describe('crashReporter module', function () {
}
const testDone = (uploaded) => {
if (uploaded) {
return done(new Error('fail'))
return done(new Error('Uploaded crash report'))
}
server.close()
if (process.platform === 'darwin') {
crashReporter.setUploadToServer(true)
}
@ -139,7 +166,7 @@ describe('crashReporter module', function () {
})
})
server = startServer({
stopServer = startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
@ -157,9 +184,9 @@ describe('crashReporter module', function () {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
this.timeout(10000)
this.timeout(120000)
startServer({
stopServer = startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
@ -176,7 +203,7 @@ describe('crashReporter module', function () {
}
generateSpecs('without sandbox', {})
generateSpecs('with sandbox ', {
generateSpecs('with sandbox', {
webPreferences: {
sandbox: true,
preload: path.join(fixtures, 'module', 'preload-sandbox.js')
@ -254,7 +281,6 @@ const waitForCrashReport = () => {
const startServer = ({callback, processType, done}) => {
var called = false
var server = http.createServer((req, res) => {
server.close()
var form = new multiparty.Form()
form.parse(req, (error, fields) => {
if (error) throw error
@ -283,6 +309,15 @@ const startServer = ({callback, processType, done}) => {
})
})
})
const activeConnections = new Set()
server.on('connection', (connection) => {
activeConnections.add(connection)
connection.once('close', () => {
activeConnections.delete(connection)
})
})
let {port} = remote.process
server.listen(port, '127.0.0.1', () => {
port = server.address().port
@ -295,5 +330,27 @@ const startServer = ({callback, processType, done}) => {
}
callback(port)
})
return server
return function stopServer (done) {
for (const connection of activeConnections) {
connection.destroy()
}
server.close(function () {
done()
})
}
}
const stopCrashService = () => {
const {crashServicePid} = remote.process
if (crashServicePid) {
remote.process.crashServicePid = 0
try {
process.kill(crashServicePid)
} catch (error) {
if (error.code !== 'ESRCH') {
throw error
}
}
}
}

View file

@ -219,6 +219,21 @@ describe('session module', function () {
if (error) return done(error)
})
})
describe('ses.cookies.flushStore(callback)', function () {
it('flushes the cookies to disk and invokes the callback when done', function (done) {
session.defaultSession.cookies.set({
url: url,
name: 'foo',
value: 'bar'
}, (error) => {
if (error) return done(error)
session.defaultSession.cookies.flushStore(() => {
done()
})
})
})
})
})
describe('ses.clearStorageData(options)', function () {

View file

@ -1,4 +1,5 @@
const assert = require('assert')
const path = require('path')
const {BrowserWindow, TouchBar} = require('electron').remote
const {closeWindow} = require('./window-helpers')
@ -48,6 +49,11 @@ describe('TouchBar module', function () {
const label = new TouchBarLabel({label: 'bar'})
const touchBar = new TouchBar([
new TouchBarButton({label: 'foo', backgroundColor: '#F00', click: () => {}}),
new TouchBarButton({
icon: path.join(__dirname, 'fixtures', 'assets', 'logo.png'),
iconPosition: 'right',
click: () => {}
}),
new TouchBarColorPicker({selectedColor: '#F00', change: () => {}}),
new TouchBarGroup({items: new TouchBar([new TouchBarLabel({label: 'hello'})])}),
label,

View file

@ -13,6 +13,7 @@ const isCI = remote.getGlobal('isCi')
describe('chromium feature', function () {
var fixtures = path.resolve(__dirname, 'fixtures')
var listener = null
let w = null
afterEach(function () {
if (listener != null) {
@ -21,6 +22,10 @@ describe('chromium feature', function () {
listener = null
})
afterEach(function () {
return closeWindow(w).then(function () { w = null })
})
describe('heap snapshot', function () {
it('does not crash', function () {
if (process.env.TRAVIS === 'true') return
@ -44,11 +49,6 @@ describe('chromium feature', function () {
describe('document.hidden', function () {
var url = 'file://' + fixtures + '/pages/document-hidden.html'
var w = null
afterEach(function () {
return closeWindow(w).then(function () { w = null })
})
it('is set correctly when window is not shown', function (done) {
w = new BrowserWindow({
@ -90,13 +90,7 @@ describe('chromium feature', function () {
})
describe('navigator.mediaDevices', function () {
if (process.env.TRAVIS === 'true') {
return
}
if (isCI && process.platform === 'linux') {
return
}
if (isCI && process.platform === 'win32') {
if (isCI) {
return
}
@ -107,7 +101,7 @@ describe('chromium feature', function () {
if (labelFound) {
done()
} else {
done('No device labels found: ' + JSON.stringify(labels))
done(new Error(`No device labels found: ${JSON.stringify(labels)}`))
}
}).catch(done)
})
@ -119,7 +113,7 @@ describe('chromium feature', function () {
}
const deviceIds = []
const ses = session.fromPartition('persist:media-device-id')
let w = new BrowserWindow({
w = new BrowserWindow({
show: false,
webPreferences: {
session: ses
@ -155,11 +149,6 @@ describe('chromium feature', function () {
describe('navigator.serviceWorker', function () {
var url = 'file://' + fixtures + '/pages/service-worker/index.html'
var w = null
afterEach(function () {
return closeWindow(w).then(function () { w = null })
})
it('should register for file scheme', function (done) {
w = new BrowserWindow({
@ -188,12 +177,6 @@ describe('chromium feature', function () {
return
}
let w = null
afterEach(() => {
return closeWindow(w).then(function () { w = null })
})
it('returns a BrowserWindowProxy object', function () {
var b = window.open('about:blank', '', 'show=no')
assert.equal(b.closed, false)
@ -246,6 +229,45 @@ describe('chromium feature', function () {
b = window.open(windowUrl, '', 'nodeIntegration=no,show=no')
})
it('disables node integration when it is disabled on the parent window for chrome devtools URLs', function (done) {
var b
app.once('web-contents-created', (event, contents) => {
contents.once('did-finish-load', () => {
contents.executeJavaScript('typeof process').then((typeofProcessGlobal) => {
assert.equal(typeofProcessGlobal, 'undefined')
b.close()
done()
}).catch(done)
})
})
b = window.open('chrome-devtools://devtools/bundled/inspector.html', '', 'nodeIntegration=no,show=no')
})
it('disables JavaScript when it is disabled on the parent window', function (done) {
var b
app.once('web-contents-created', (event, contents) => {
contents.once('did-finish-load', () => {
app.once('browser-window-created', (event, window) => {
const preferences = window.webContents.getWebPreferences()
assert.equal(preferences.javascript, false)
window.destroy()
b.close()
done()
})
// Click link on page
contents.sendInputEvent({type: 'mouseDown', clickCount: 1, x: 1, y: 1})
contents.sendInputEvent({type: 'mouseUp', clickCount: 1, x: 1, y: 1})
})
})
var windowUrl = require('url').format({
pathname: `${fixtures}/pages/window-no-javascript.html`,
protocol: 'file',
slashes: true
})
b = window.open(windowUrl, '', 'javascript=no,show=no')
})
it('does not override child options', function (done) {
var b, size
size = {
@ -339,15 +361,48 @@ describe('chromium feature', function () {
})
b = window.open()
})
it('throws an exception when the arguments cannot be converted to strings', function () {
assert.throws(function () {
window.open('', {toString: null})
}, /Cannot convert object to primitive value/)
assert.throws(function () {
window.open('', '', {toString: 3})
}, /Cannot convert object to primitive value/)
})
it('sets the window title to the specified frameName', function (done) {
let b
app.once('browser-window-created', (event, createdWindow) => {
assert.equal(createdWindow.getTitle(), 'hello')
b.close()
done()
})
b = window.open('', 'hello')
})
it('does not throw an exception when the frameName is a built-in object property', function (done) {
let b
app.once('browser-window-created', (event, createdWindow) => {
assert.equal(createdWindow.getTitle(), '__proto__')
b.close()
done()
})
b = window.open('', '__proto__')
})
it('does not throw an exception when the features include webPreferences', function () {
let b
assert.doesNotThrow(function () {
b = window.open('', '', 'webPreferences=')
})
b.close()
})
})
describe('window.opener', function () {
let url = 'file://' + fixtures + '/pages/window-opener.html'
let w = null
afterEach(function () {
return closeWindow(w).then(function () { w = null })
})
it('is null for main window', function (done) {
w = new BrowserWindow({
@ -521,6 +576,14 @@ describe('chromium feature', function () {
})
b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no')
})
it('throws an exception when the targetOrigin cannot be converted to a string', function () {
var b = window.open('')
assert.throws(function () {
b.postMessage('test', {toString: null})
}, /Cannot convert object to primitive value/)
b.close()
})
})
describe('window.opener.postMessage', function () {
@ -849,7 +912,6 @@ describe('chromium feature', function () {
})
describe('PDF Viewer', function () {
let w = null
const pdfSource = url.format({
pathname: path.join(fixtures, 'assets', 'cat.pdf').replace(/\\/g, '/'),
protocol: 'file',
@ -865,10 +927,6 @@ describe('chromium feature', function () {
})
})
afterEach(function () {
return closeWindow(w).then(function () { w = null })
})
it('opens when loading a pdf resource as top level navigation', function (done) {
ipcMain.once('pdf-loaded', function (event, success) {
if (success) done()
@ -907,4 +965,36 @@ describe('chromium feature', function () {
})
})
})
describe('window.alert(message, title)', function () {
it('throws an exception when the arguments cannot be converted to strings', function () {
assert.throws(function () {
window.alert({toString: null})
}, /Cannot convert object to primitive value/)
assert.throws(function () {
window.alert('message', {toString: 3})
}, /Cannot convert object to primitive value/)
})
})
describe('window.confirm(message, title)', function () {
it('throws an exception when the arguments cannot be converted to strings', function () {
assert.throws(function () {
window.confirm({toString: null}, 'title')
}, /Cannot convert object to primitive value/)
assert.throws(function () {
window.confirm('message', {toString: 3})
}, /Cannot convert object to primitive value/)
})
})
describe('window.history.go(offset)', function () {
it('throws an exception when the argumnet cannot be converted to a string', function () {
assert.throws(function () {
window.history.go({toString: null})
}, /Cannot convert object to primitive value/)
})
})
})

View file

@ -3,7 +3,7 @@
<script type="text/javascript" charset="utf-8">
const {port} = require('url').parse(window.location.href, true).query
const {crashReporter} = require('electron')
const {crashReporter, ipcRenderer} = require('electron')
crashReporter.start({
productName: 'Zombies',
@ -18,6 +18,10 @@ crashReporter.start({
}
})
if (process.platform === 'win32') {
ipcRenderer.sendSync('crash-service-pid', crashReporter._crashServiceProcess.pid)
}
setImmediate(() => {
if (process.platform === 'darwin') {
crashReporter.setExtraParameter('extra2', 'extra2')

View file

@ -16,6 +16,11 @@ crashReporter.start({
'extra2': 'extra2',
}
});
if (process.platform === 'win32') {
ipcRenderer.sendSync('crash-service-pid', crashReporter._crashServiceProcess.pid)
}
if (!uploadToServer) {
ipcRenderer.sendSync('list-existing-dumps')
}

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<style>
* {
padding: 0;
margin: 0;
}
</style>
<body>
<a href="about:blank>" target="_blank">CLICK</a>
</body>
</html>

View file

@ -1,60 +1,63 @@
const assert = require('assert')
const Module = require('module')
const path = require('path')
const temp = require('temp')
const {remote} = require('electron')
const {BrowserWindow} = remote
const {closeWindow} = require('./window-helpers')
describe('third-party module', function () {
describe('modules support', function () {
var fixtures = path.join(__dirname, 'fixtures')
temp.track()
if (process.platform !== 'win32' || process.execPath.toLowerCase().indexOf('\\out\\d\\') === -1) {
describe('runas', function () {
it('can be required in renderer', function () {
require('runas')
describe('third-party module', function () {
if (process.platform !== 'win32' || process.execPath.toLowerCase().indexOf('\\out\\d\\') === -1) {
describe('runas', function () {
it('can be required in renderer', function () {
require('runas')
})
it('can be required in node binary', function (done) {
var runas = path.join(fixtures, 'module', 'runas.js')
var child = require('child_process').fork(runas)
child.on('message', function (msg) {
assert.equal(msg, 'ok')
done()
})
})
})
it('can be required in node binary', function (done) {
var runas = path.join(fixtures, 'module', 'runas.js')
var child = require('child_process').fork(runas)
child.on('message', function (msg) {
assert.equal(msg, 'ok')
done()
describe('ffi', function () {
if (process.platform === 'win32') return
it('does not crash', function () {
var ffi = require('ffi')
var libm = ffi.Library('libm', {
ceil: ['double', ['double']]
})
assert.equal(libm.ceil(1.5), 2)
})
})
}
describe('q', function () {
var Q = require('q')
describe('Q.when', function () {
it('emits the fullfil callback', function (done) {
Q(true).then(function (val) {
assert.equal(val, true)
done()
})
})
})
})
describe('ffi', function () {
if (process.platform === 'win32') return
it('does not crash', function () {
var ffi = require('ffi')
var libm = ffi.Library('libm', {
ceil: ['double', ['double']]
describe('coffee-script', function () {
it('can be registered and used to require .coffee files', function () {
assert.doesNotThrow(function () {
require('coffee-script').register()
})
assert.equal(libm.ceil(1.5), 2)
assert.strictEqual(require('./fixtures/module/test.coffee'), true)
})
})
}
describe('q', function () {
var Q = require('q')
describe('Q.when', function () {
it('emits the fullfil callback', function (done) {
Q(true).then(function (val) {
assert.equal(val, true)
done()
})
})
})
})
describe('coffee-script', function () {
it('can be registered and used to require .coffee files', function () {
assert.doesNotThrow(function () {
require('coffee-script').register()
})
assert.strictEqual(require('./fixtures/module/test.coffee'), true)
})
})
describe('global variables', function () {
@ -76,56 +79,79 @@ describe('third-party module', function () {
})
})
})
})
describe('Module._nodeModulePaths', function () {
describe('when the path is inside the resources path', function () {
it('does not include paths outside of the resources path', function () {
let modulePath = process.resourcesPath
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules')
])
describe('Module._nodeModulePaths', function () {
describe('when the path is inside the resources path', function () {
it('does not include paths outside of the resources path', function () {
let modulePath = process.resourcesPath
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules')
])
modulePath = process.resourcesPath + '-foo'
let nodeModulePaths = Module._nodeModulePaths(modulePath)
assert(nodeModulePaths.includes(path.join(modulePath, 'node_modules')))
assert(nodeModulePaths.includes(path.join(modulePath, '..', 'node_modules')))
modulePath = process.resourcesPath + '-foo'
let nodeModulePaths = Module._nodeModulePaths(modulePath)
assert(nodeModulePaths.includes(path.join(modulePath, 'node_modules')))
assert(nodeModulePaths.includes(path.join(modulePath, '..', 'node_modules')))
modulePath = path.join(process.resourcesPath, 'foo')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'foo')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'node_modules', 'foo')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'node_modules', 'foo')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'bar')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules', 'foo', 'bar', 'node_modules'),
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'bar')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules', 'foo', 'bar', 'node_modules'),
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar', 'node_modules'),
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar', 'node_modules'),
path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'),
path.join(process.resourcesPath, 'node_modules')
])
})
})
describe('when the path is outside the resources path', function () {
it('includes paths outside of the resources path', function () {
let modulePath = path.resolve('/foo')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(modulePath, 'node_modules'),
path.resolve('/node_modules')
])
})
})
})
describe('when the path is outside the resources path', function () {
it('includes paths outside of the resources path', function () {
let modulePath = path.resolve('/foo')
assert.deepEqual(Module._nodeModulePaths(modulePath), [
path.join(modulePath, 'node_modules'),
path.resolve('/node_modules')
])
describe('require', () => {
describe('when loaded URL is not file: protocol', () => {
let w
beforeEach(() => {
w = new BrowserWindow({
show: false
})
})
afterEach(async () => {
await closeWindow(w)
w = null
})
it('searches for module under app directory', async () => {
w.loadURL('about:blank')
const result = await w.webContents.executeJavaScript('typeof require("q").when')
assert.equal(result, 'function')
})
})
})
})

View file

@ -85,7 +85,7 @@ describe('node feature', function () {
child.stdout.on('data', (chunk) => {
data += String(chunk)
})
child.on('exit', (code) => {
child.on('close', (code) => {
assert.equal(code, 0)
assert.equal(data, 'pipes stdio')
done()

View file

@ -2,20 +2,15 @@
process.throwDeprecation = true
const electron = require('electron')
const app = electron.app
const crashReporter = electron.crashReporter
const ipcMain = electron.ipcMain
const dialog = electron.dialog
const BrowserWindow = electron.BrowserWindow
const protocol = electron.protocol
const webContents = electron.webContents
const v8 = require('v8')
const {app, BrowserWindow, crashReporter, dialog, ipcMain, protocol, webContents} = electron
const {Coverage} = require('electabul')
const Coverage = require('electabul').Coverage
const fs = require('fs')
const path = require('path')
const url = require('url')
const util = require('util')
const v8 = require('v8')
var argv = require('yargs')
.boolean('ci')
@ -24,7 +19,10 @@ var argv = require('yargs')
.argv
var window = null
process.port = 0 // will be used by crash-reporter spec.
// will be used by crash-reporter spec.
process.port = 0
process.crashServicePid = 0
v8.setFlagsFromString('--expose_gc')
app.commandLine.appendSwitch('js-flags', '--expose_gc')
@ -100,6 +98,12 @@ app.on('window-all-closed', function () {
app.quit()
})
app.on('web-contents-created', (event, contents) => {
contents.on('crashed', (event, killed) => {
console.log(`webContents ${contents.id} crashed: ${contents.getURL()} (killed=${killed})`)
})
})
app.on('ready', function () {
// Test if using protocol module would crash.
electron.protocol.registerStringProtocol('test-if-crashes', function () {})
@ -329,6 +333,11 @@ ipcMain.on('navigate-with-pending-entry', (event, id) => {
})
})
ipcMain.on('crash-service-pid', (event, pid) => {
process.crashServicePid = pid
event.returnValue = null
})
// Suspend listeners until the next event and then restore them
const suspendListeners = (emitter, eventName, callback) => {
const listeners = emitter.listeners(eventName)