Browse Source

Merge branch 'master' into native-window-open

Ryohei Ikegami 8 years ago
parent
commit
1d73e84a29
59 changed files with 985 additions and 209 deletions
  1. 10 0
      atom/browser/api/atom_api_app.cc
  2. 6 0
      atom/browser/api/atom_api_app.h
  3. 4 1
      atom/browser/api/atom_api_auto_updater.cc
  4. 16 1
      atom/browser/api/atom_api_cookies.cc
  5. 1 0
      atom/browser/api/atom_api_cookies.h
  6. 12 0
      atom/browser/api/atom_api_window.cc
  7. 3 0
      atom/browser/api/atom_api_window.h
  8. 5 0
      atom/browser/atom_browser_client.cc
  9. 15 0
      atom/browser/native_window.cc
  10. 3 0
      atom/browser/native_window.h
  11. 8 0
      atom/browser/native_window_mac.mm
  12. 5 0
      atom/browser/native_window_observer.h
  13. 5 0
      atom/browser/native_window_views_win.cc
  14. 2 2
      atom/browser/resources/mac/Info.plist
  15. 4 4
      atom/browser/resources/win/atom.rc
  16. 10 0
      atom/browser/ui/cocoa/atom_touch_bar.mm
  17. 1 1
      atom/common/atom_version.h
  18. 3 0
      atom/common/options_switches.cc
  19. 1 0
      atom/common/options_switches.h
  20. 0 18
      default_app/index.html
  21. 45 0
      default_app/renderer.js
  22. 1 1
      docs-translations/ko-KR/api/protocol.md
  23. 1 1
      docs-translations/tr-TR/README.md
  24. 252 0
      docs-translations/tr-TR/tutorial/quick-start.md
  25. 40 4
      docs-translations/zh-CN/api/shell.md
  26. 13 0
      docs/api/browser-window.md
  27. 6 0
      docs/api/cookies.md
  28. 2 0
      docs/api/download-item.md
  29. 2 2
      docs/api/menu-item.md
  30. 4 0
      docs/api/menu.md
  31. 1 0
      docs/api/touch-bar-button.md
  32. 4 0
      docs/api/touch-bar.md
  33. 2 1
      docs/api/webview-tag.md
  34. 4 0
      docs/api/window-open.md
  35. 3 0
      docs/development/build-instructions-windows.md
  36. 1 1
      docs/faq.md
  37. 2 2
      docs/tutorial/desktop-environment-integration.md
  38. 1 1
      electron.gyp
  39. 1 0
      filenames.gypi
  40. 0 4
      lib/browser/api/app.js
  41. 2 1
      lib/browser/api/touch-bar.js
  42. 15 6
      lib/browser/guest-window-manager.js
  43. 1 1
      lib/common/api/crash-reporter.js
  44. 9 1
      lib/renderer/init.js
  45. 12 5
      lib/renderer/window-setup.js
  46. 1 1
      package.json
  47. 0 2
      script/cibuild
  48. 22 2
      spec/api-auto-updater-spec.js
  49. 57 3
      spec/api-browser-window-spec.js
  50. 69 12
      spec/api-crash-reporter-spec.js
  51. 15 0
      spec/api-session-spec.js
  52. 6 0
      spec/api-touch-bar-spec.js
  53. 125 35
      spec/chromium-spec.js
  54. 5 1
      spec/fixtures/api/crash-restart.html
  55. 5 0
      spec/fixtures/api/crash.html
  56. 12 0
      spec/fixtures/pages/window-no-javascript.html
  57. 110 84
      spec/modules-spec.js
  58. 1 1
      spec/node-spec.js
  59. 19 10
      spec/static/main.js

+ 10 - 0
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)

+ 6 - 0
atom/browser/api/atom_api_app.h

@@ -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);
 };
 

+ 4 - 1
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.

+ 16 - 1
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<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

+ 1 - 0
atom/browser/api/atom_api_cookies.h

@@ -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,

+ 12 - 0
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");
 }

+ 3 - 0
atom/browser/api/atom_api_window.h

@@ -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;

+ 5 - 0
atom/browser/atom_browser_client.cc

@@ -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;

+ 15 - 0
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();

+ 3 - 0
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();

+ 8 - 0
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 <QLPreviewItem>

+ 5 - 0
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() {}

+ 5 - 0
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;
   }

+ 2 - 2
atom/browser/resources/mac/Info.plist

@@ -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>

+ 4 - 4
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

+ 10 - 0
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;
+    }
   }
 }
 

+ 1 - 1
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
 

+ 3 - 0
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";

+ 1 - 0
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[];

File diff suppressed because it is too large
+ 0 - 18
default_app/index.html


+ 45 - 0
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`

+ 1 - 1
docs-translations/ko-KR/api/protocol.md

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

+ 1 - 1
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)
 

+ 252 - 0
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
+<!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

+ 40 - 4
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: [email protected]会打开用户默认的邮件客户端)
+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` 的快捷连接的信息.
+
+发生错误时,会抛出异常信息.

+ 13 - 0
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:

+ 6 - 0
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.

+ 2 - 0
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.

+ 2 - 2
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

+ 4 - 0
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

+ 1 - 0
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

+ 4 - 0
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`:

+ 2 - 1
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.

+ 4 - 0
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.

+ 3 - 0
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/)

+ 1 - 1
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.

+ 2 - 2
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

+ 1 - 1
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': [

+ 1 - 0
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',

+ 0 - 4
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)
   },

+ 2 - 1
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()

+ 15 - 6
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 = {}

+ 1 - 1
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
       })

+ 9 - 1
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.

+ 12 - 5
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', {

+ 1 - 1
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",

+ 0 - 2
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'])

+ 22 - 2
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()
+        }
+      })
+    })
   })
 }

+ 57 - 3
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()
+        })
       })
     })
 

+ 69 - 12
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
+      }
+    }
+  }
 }

+ 15 - 0
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 () {

+ 6 - 0
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,

+ 125 - 35
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/)
+    })
+  })
 })

+ 5 - 1
spec/fixtures/api/crash-restart.html

@@ -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')

+ 5 - 0
spec/fixtures/api/crash.html

@@ -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')
 }

+ 12 - 0
spec/fixtures/pages/window-no-javascript.html

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

+ 110 - 84
spec/modules-spec.js

@@ -1,59 +1,62 @@
 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
+      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']]
+        it('does not crash', function () {
+          var ffi = require('ffi')
+          var libm = ffi.Library('libm', {
+            ceil: ['double', ['double']]
+          })
+          assert.equal(libm.ceil(1.5), 2)
         })
-        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('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()
+    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)
       })
-      assert.strictEqual(require('./fixtures/module/test.coffee'), true)
     })
   })
 
@@ -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')
-      ])
-
-      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, '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', '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('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 = 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', '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')
+        ])
+      })
+    })
+
+    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')
+      })
     })
   })
 })

+ 1 - 1
spec/node-spec.js

@@ -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()

+ 19 - 10
spec/static/main.js

@@ -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)

Some files were not shown because too many files changed in this diff