diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 709696340274..115041d0082d 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -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) diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 8b276f334d5c..a87b88bc4642 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -70,6 +70,8 @@ class App : public AtomBrowserClient::Delegate, std::unique_ptr 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); }; diff --git a/atom/browser/api/atom_api_auto_updater.cc b/atom/browser/api/atom_api_auto_updater.cc index ea3024191e97..c23e488f64fd 100644 --- a/atom/browser/api/atom_api_auto_updater.cc +++ b/atom/browser/api/atom_api_auto_updater.cc @@ -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. diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index ddb6910b509a..2ce9e12f6c5b 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -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 getter, + const base::Closure& callback) { + GetCookieStore(getter)->FlushStore(base::Bind(RunCallbackInUI, callback)); +} + // Sets cookie with |details| in IO thread. void SetCookieOnIO(scoped_refptr getter, std::unique_ptr 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 diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h index 3a7a98fbafd9..d20dab8394c6 100644 --- a/atom/browser/api/atom_api_cookies.h +++ b/atom/browser/api/atom_api_cookies.h @@ -53,6 +53,7 @@ class Cookies : public mate::TrackableObject, 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, diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index c6707624913d..72b8e33ef7b5 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -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"); } diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index d464af58ea9d..75f0328ba64f 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -63,6 +63,7 @@ class Window : public mate::TrackableObject, 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, 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; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 5620e53c1554..9598a0d1e1c1 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -261,6 +261,11 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( } #endif + if (delegate_) { + auto app_path = static_cast(delegate_)->GetAppPath(); + command_line->AppendSwitchPath(switches::kAppPath, app_path); + } + content::WebContents* web_contents = GetWebContentsFromProcessID(process_id); if (!web_contents) return; diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 316cc8dc2e3d..9e2c11aec4ab 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -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(); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 56702daef63b..d3f18d8fb95c 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -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(); diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index b695f8eafa89..9dc119e239dd 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -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 diff --git a/atom/browser/native_window_observer.h b/atom/browser/native_window_observer.h index 3b8d86e6fb0f..8c908dc8237a 100644 --- a/atom/browser/native_window_observer.h +++ b/atom/browser/native_window_observer.h @@ -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() {} diff --git a/atom/browser/native_window_views_win.cc b/atom/browser/native_window_views_win.cc index 1b523e90b804..abda0d0b0262 100644 --- a/atom/browser/native_window_views_win.cc +++ b/atom/browser/native_window_views_win.cc @@ -147,6 +147,11 @@ bool NativeWindowViews::PreHandleMSG( } return false; } + case WM_ENDSESSION: { + if (w_param) { + NotifyWindowEndSession(); + } + } default: return false; } diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 5509969014a1..ecd6a28b5cdf 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.6.6 + 1.6.7 CFBundleShortVersionString - 1.6.6 + 1.6.7 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index d572d20ada20..f7a9b32351b7 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -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 diff --git a/atom/browser/ui/cocoa/atom_touch_bar.mm b/atom/browser/ui/cocoa/atom_touch_bar.mm index a3e846df4871..a21fbc5497a3 100644 --- a/atom/browser/ui/cocoa/atom_touch_bar.mm +++ b/atom/browser/ui/cocoa/atom_touch_bar.mm @@ -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; + } } } diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 3afa163b90aa..79ece65408bf 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -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 diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 7613f2988ece..657a36ac41ce 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -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"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 6a309c5dcfa7..057aa26e080c 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -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[]; diff --git a/default_app/index.html b/default_app/index.html index 3f29908a5635..41b5396360db 100644 --- a/default_app/index.html +++ b/default_app/index.html @@ -113,24 +113,6 @@ - -
- +
Docs @@ -162,25 +144,15 @@ Console (or Terminal):

- +

 
     

The path-to-your-app should be the path to your own Electron app.

-

You can read the - - guide in Electron's - +

You can read the quick start + guide in Electron's docs to learn how to write one.

@@ -195,25 +167,7 @@
diff --git a/default_app/renderer.js b/default_app/renderer.js new file mode 100644 index 000000000000..6196195eab93 --- /dev/null +++ b/default_app/renderer.js @@ -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` diff --git a/docs-translations/ko-KR/api/protocol.md b/docs-translations/ko-KR/api/protocol.md index dc3bb51ba2f3..38a17bed14ff 100644 --- a/docs-translations/ko-KR/api/protocol.md +++ b/docs-translations/ko-KR/api/protocol.md @@ -1,6 +1,6 @@ # protocol -> 커스텀 프로토콜을 등록하거나 이미 존재하능 프로토콜의 요청의 동작을 변경합니다. +> 커스텀 프로토콜을 등록하거나 이미 존재하는 프로토콜의 요청의 동작을 변경합니다. 프로세스: [메인](../tutorial/quick-start.md#main-process) diff --git a/docs-translations/tr-TR/README.md b/docs-translations/tr-TR/README.md index a3487d298a2e..ac11a02f4d3a 100644 --- a/docs-translations/tr-TR/README.md +++ b/docs-translations/tr-TR/README.md @@ -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) diff --git a/docs-translations/tr-TR/tutorial/quick-start.md b/docs-translations/tr-TR/tutorial/quick-start.md new file mode 100644 index 000000000000..1941060df2a9 --- /dev/null +++ b/docs-translations/tr-TR/tutorial/quick-start.md @@ -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 + + + + + Hello World! + + +

Hello World!

+ We are using node , + Chrome , + and Electron . + + +``` + +## 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 diff --git a/docs-translations/zh-CN/api/shell.md b/docs-translations/zh-CN/api/shell.md index 1f36046b1e17..22fa0d1cd1ff 100644 --- a/docs-translations/zh-CN/api/shell.md +++ b/docs-translations/zh-CN/api/shell.md @@ -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` 的快捷连接的信息. + +发生错误时,会抛出异常信息. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 3bf9c1fb1a73..f3e041778ecb 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -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: diff --git a/docs/api/cookies.md b/docs/api/cookies.md index 8e6420296733..ba3cff33609f 100644 --- a/docs/api/cookies.md +++ b/docs/api/cookies.md @@ -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. diff --git a/docs/api/download-item.md b/docs/api/download-item.md index 6603b619aaf9..c07bf98a869f 100644 --- a/docs/api/download-item.md +++ b/docs/api/download-item.md @@ -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. diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 494bdb74f196..a7486f3003cf 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -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 diff --git a/docs/api/menu.md b/docs/api/menu.md index 72d8164270dc..beceb2059c6c 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -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 diff --git a/docs/api/touch-bar-button.md b/docs/api/touch-bar-button.md index 177e51bcdb20..456fc207fe98 100644 --- a/docs/api/touch-bar-button.md +++ b/docs/api/touch-bar-button.md @@ -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 diff --git a/docs/api/touch-bar.md b/docs/api/touch-bar.md index 7ac578da46db..0a0827a2b2fa 100644 --- a/docs/api/touch-bar.md +++ b/docs/api/touch-bar.md @@ -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`: diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index 9c09c8ef730e..1da5679c18ad 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -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. diff --git a/docs/api/window-open.md b/docs/api/window-open.md index 79158b292a74..30ea85119451 100644 --- a/docs/api/window-open.md +++ b/docs/api/window-open.md @@ -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. diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 4f7335e108fd..6bfd24257677 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -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/) diff --git a/docs/faq.md b/docs/faq.md index 0079d43f4e26..abeac23f76ed 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -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. diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index 412341fe7006..1c82e360e8e9 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -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 diff --git a/electron.gyp b/electron.gyp index 8c3718b1cd60..ce3673abf114 100644 --- a/electron.gyp +++ b/electron.gyp @@ -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': [ diff --git a/filenames.gypi b/filenames.gypi index b007e38d935c..86ed08daf2d1 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -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', diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index c5865f2242b8..d1ca60280e93 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -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) }, diff --git a/lib/browser/api/touch-bar.js b/lib/browser/api/touch-bar.js index 1a9c3238a97f..4bb129449eb9 100644 --- a/lib/browser/api/touch-bar.js +++ b/lib/browser/api/touch-bar.js @@ -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() diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js index e06dd27d203d..aff73ebe6f63 100644 --- a/lib/browser/guest-window-manager.js +++ b/lib/browser/guest-window-manager.js @@ -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 = {} diff --git a/lib/common/api/crash-reporter.js b/lib/common/api/crash-reporter.js index 658622e8f5a7..7a54e24fbc2a 100644 --- a/lib/common/api/crash-reporter.js +++ b/lib/common/api/crash-reporter.js @@ -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 }) diff --git a/lib/renderer/init.js b/lib/renderer/init.js index 38441c9ec143..9b7ea9dabdb6 100644 --- a/lib/renderer/init.js +++ b/lib/renderer/init.js @@ -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. diff --git a/lib/renderer/window-setup.js b/lib/renderer/window-setup.js index 510c46e3e0a6..b1187e96f18f 100644 --- a/lib/renderer/window-setup.js +++ b/lib/renderer/window-setup.js @@ -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', { diff --git a/package.json b/package.json index c34a27e2751c..bfb35f29689a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "1.6.6", + "version": "1.6.7", "devDependencies": { "asar": "^0.11.0", "browserify": "^13.1.0", diff --git a/script/cibuild b/script/cibuild index dc2d9d081045..419404fca8a5 100755 --- a/script/cibuild +++ b/script/cibuild @@ -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']) diff --git a/spec/api-auto-updater-spec.js b/spec/api-auto-updater-spec.js index 0716c2245df8..df77822e456d 100644 --- a/spec/api-auto-updater-spec.js +++ b/spec/api-auto-updater-spec.js @@ -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() + } + }) + }) }) } diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 6a1d75bf0eda..703f5a6d3ecf 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -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() + }) }) }) diff --git a/spec/api-crash-reporter-spec.js b/spec/api-crash-reporter-spec.js index a0fde2d54fe9..92020dbea623 100644 --- a/spec/api-crash-reporter-spec.js +++ b/spec/api-crash-reporter-spec.js @@ -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 + } + } + } } diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index c34cdfdab0ff..d361c10d947e 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -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 () { diff --git a/spec/api-touch-bar-spec.js b/spec/api-touch-bar-spec.js index 420ef3fc7b37..6177e3cb85fa 100644 --- a/spec/api-touch-bar-spec.js +++ b/spec/api-touch-bar-spec.js @@ -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, diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index a54c6744f7c8..d5d1b3f4782a 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -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/) + }) + }) }) diff --git a/spec/fixtures/api/crash-restart.html b/spec/fixtures/api/crash-restart.html index 2f55c539bbd8..22f3b45b5c82 100644 --- a/spec/fixtures/api/crash-restart.html +++ b/spec/fixtures/api/crash-restart.html @@ -3,7 +3,7 @@