diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..a78e30d8620 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,2 @@ +* Electron version: +* Operating system: diff --git a/README-ko.md b/README-ko.md index 664075dd481..b9085828d46 100644 --- a/README-ko.md +++ b/README-ko.md @@ -57,6 +57,7 @@ API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝 - [중국어 번체](https://github.com/atom/electron/tree/master/docs-translations/zh-TW) - [우크라이나어](https://github.com/atom/electron/tree/master/docs-translations/uk-UA) - [러시아어](https://github.com/atom/electron/tree/master/docs-translations/ru-RU) +- [프랑스어](https://github.com/atom/electron/tree/master/docs-translations/fr-FR) ## 시작하기 diff --git a/README.md b/README.md index a7306ddabbb..0080585b54c 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ contains documents describing how to build and contribute to Electron. - [Traditional Chinese](https://github.com/atom/electron/tree/master/docs-translations/zh-TW) - [Ukrainian](https://github.com/atom/electron/tree/master/docs-translations/uk-UA) - [Russian](https://github.com/atom/electron/tree/master/docs-translations/ru-RU) +- [French](https://github.com/atom/electron/tree/master/docs-translations/fr-FR) ## Quick Start @@ -70,6 +71,7 @@ forums - [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack - [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)* - [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)* +- [`electron-jp`](https://electron-jp-slackin.herokuapp.com/) *(Japanese)* Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. diff --git a/atom.gyp b/atom.gyp index dbaf6db5629..0bccc254b1b 100644 --- a/atom.gyp +++ b/atom.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.36.7', + 'version%': '0.36.9', }, 'includes': [ 'filenames.gypi', @@ -144,6 +144,7 @@ }, { 'copied_libraries': [ '<(libchromiumcontent_dir)/pdf.dll', + '<(libchromiumcontent_dir)/ffmpeg.dll', ], }], ], @@ -193,6 +194,7 @@ }, { 'copied_libraries': [ '<(PRODUCT_DIR)/lib/libnode.so', + '<(libchromiumcontent_dir)/libffmpeg.so', ], }], ], @@ -461,6 +463,7 @@ }, { 'copied_libraries': [ '<(PRODUCT_DIR)/libnode.dylib', + '<(libchromiumcontent_dir)/libffmpeg.dylib', ], }], ], diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 80e5b606ef8..12e0c56ceb6 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -218,7 +218,8 @@ WebContents::WebContents(content::WebContents* web_contents) WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options) - : request_id_(0) { + : embedder_(nullptr), + request_id_(0) { // Whether it is a guest WebContents. bool is_guest = false; options.Get("isGuest", &is_guest); @@ -273,10 +274,10 @@ WebContents::WebContents(v8::Isolate* isolate, guest_delegate_->Initialize(this); NativeWindow* owner_window = nullptr; - WebContents* embedder = nullptr; - if (options.Get("embedder", &embedder) && embedder) { + if (options.Get("embedder", &embedder_) && embedder_) { // New WebContents's owner_window is the embedder's owner_window. - auto relay = NativeWindowRelay::FromWebContents(embedder->web_contents()); + auto relay = + NativeWindowRelay::FromWebContents(embedder_->web_contents()); if (relay) owner_window = relay->window.get(); } @@ -296,6 +297,7 @@ WebContents::~WebContents() { // The WebContentsDestroyed will not be called automatically because we // unsubscribe from webContents before destroying it. So we have to manually // call it here to make sure "destroyed" event is emitted. + RenderViewDeleted(web_contents()->GetRenderViewHost()); WebContentsDestroyed(); } } @@ -461,6 +463,13 @@ void WebContents::FindReply(content::WebContents* web_contents, } } +bool WebContents::CheckMediaAccessPermission( + content::WebContents* web_contents, + const GURL& security_origin, + content::MediaStreamType type) { + return true; +} + void WebContents::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, @@ -485,17 +494,7 @@ void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { } void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) { - int process_id = render_view_host->GetProcess()->GetID(); - Emit("render-view-deleted", process_id); - - // process.emit('ATOM_BROWSER_RELEASE_RENDER_VIEW', processId); - // Tell the rpc server that a render view has been deleted and we need to - // release all objects owned by it. - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - node::Environment* env = node::Environment::GetCurrent(isolate()); - mate::EmitEvent(isolate(), env->process_object(), - "ATOM_BROWSER_RELEASE_RENDER_VIEW", process_id); + Emit("render-view-deleted", render_view_host->GetProcess()->GetID()); } void WebContents::RenderProcessGone(base::TerminationStatus status) { @@ -675,9 +674,6 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) { // be destroyed on close, and WebContentsDestroyed would be called for it, so // we need to make sure the api::WebContents is also deleted. void WebContents::WebContentsDestroyed() { - // The RenderViewDeleted was not called when the WebContents is destroyed. - RenderViewDeleted(web_contents()->GetRenderViewHost()); - // This event is only for internal use, which is emitted when WebContents is // being destroyed. Emit("will-destroy"); @@ -710,6 +706,14 @@ bool WebContents::Equal(const WebContents* web_contents) const { } void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { + if (!url.is_valid()) { + Emit("did-fail-load", + static_cast(net::ERR_INVALID_URL), + net::ErrorToShortString(net::ERR_INVALID_URL), + url.possibly_invalid_spec()); + return; + } + content::NavigationController::LoadURLParams params(url); GURL http_referrer; @@ -1065,7 +1069,7 @@ void WebContents::BeginFrameSubscription( const auto view = web_contents()->GetRenderWidgetHostView(); if (view) { scoped_ptr frame_subscriber(new FrameSubscriber( - isolate(), view->GetVisibleViewportSize(), callback)); + isolate(), view, callback)); view->BeginFrameSubscription(frame_subscriber.Pass()); } } @@ -1120,6 +1124,12 @@ v8::Local WebContents::Session(v8::Isolate* isolate) { return v8::Local::New(isolate, session_); } +content::WebContents* WebContents::HostWebContents() { + if (!embedder_) + return nullptr; + return embedder_->web_contents(); +} + v8::Local WebContents::DevToolsWebContents(v8::Isolate* isolate) { if (devtools_web_contents_.IsEmpty()) return v8::Null(isolate); @@ -1203,6 +1213,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) .SetProperty("session", &WebContents::Session) + .SetProperty("hostWebContents", &WebContents::HostWebContents) .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents) .SetProperty("debugger", &WebContents::Debugger); } diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 63c4a017771..a785e1c070d 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -147,6 +147,7 @@ class WebContents : public mate::TrackableObject, // Properties. v8::Local Session(v8::Isolate* isolate); + content::WebContents* HostWebContents(); v8::Local DevToolsWebContents(v8::Isolate* isolate); v8::Local Debugger(v8::Isolate* isolate); @@ -201,6 +202,10 @@ class WebContents : public mate::TrackableObject, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) override; + bool CheckMediaAccessPermission( + content::WebContents* web_contents, + const GURL& security_origin, + content::MediaStreamType type) override; void RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, @@ -287,6 +292,9 @@ class WebContents : public mate::TrackableObject, scoped_ptr guest_delegate_; + // The host webcontents that may contain this webcontents. + WebContents* embedder_; + // The type of current WebContents. Type type_; diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index bcf6ccc5a33..ea1d95b79ca 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -109,7 +109,7 @@ void TranslateOldOptions(v8::Isolate* isolate, v8::Local options) { // Converts binary data to Buffer. v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { - auto buffer = node::Buffer::New(isolate, static_cast(val), size); + auto buffer = node::Buffer::Copy(isolate, static_cast(val), size); if (buffer.IsEmpty()) return v8::Null(isolate); else diff --git a/atom/browser/api/frame_subscriber.cc b/atom/browser/api/frame_subscriber.cc index 5b7241486b7..c76d5ffc87d 100644 --- a/atom/browser/api/frame_subscriber.cc +++ b/atom/browser/api/frame_subscriber.cc @@ -4,19 +4,18 @@ #include "atom/browser/api/frame_subscriber.h" -#include "atom/common/node_includes.h" #include "base/bind.h" -#include "media/base/video_frame.h" -#include "media/base/yuv_convert.h" +#include "atom/common/node_includes.h" +#include "content/public/browser/render_widget_host.h" namespace atom { namespace api { FrameSubscriber::FrameSubscriber(v8::Isolate* isolate, - const gfx::Size& size, + content::RenderWidgetHostView* view, const FrameCaptureCallback& callback) - : isolate_(isolate), size_(size), callback_(callback) { + : isolate_(isolate), view_(view), callback_(callback), weak_factory_(this) { } bool FrameSubscriber::ShouldCaptureFrame( @@ -24,39 +23,39 @@ bool FrameSubscriber::ShouldCaptureFrame( base::TimeTicks present_time, scoped_refptr* storage, DeliverFrameCallback* callback) { - *storage = media::VideoFrame::CreateFrame( - media::PIXEL_FORMAT_YV12, - size_, gfx::Rect(size_), size_, base::TimeDelta()); - *callback = base::Bind(&FrameSubscriber::OnFrameDelivered, - base::Unretained(this), *storage); - return true; + const auto host = view_ ? view_->GetRenderWidgetHost() : nullptr; + if (!view_ || !host) + return false; + + const auto size = view_->GetVisibleViewportSize(); + + host->CopyFromBackingStore( + gfx::Rect(size), + size, + base::Bind(&FrameSubscriber::OnFrameDelivered, + weak_factory_.GetWeakPtr(), callback_), + kBGRA_8888_SkColorType); + + return false; } -void FrameSubscriber::OnFrameDelivered( - scoped_refptr frame, base::TimeTicks, bool result) { - if (!result) +void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, + const SkBitmap& bitmap, content::ReadbackResponse response) { + if (bitmap.computeSize64() == 0) return; v8::Locker locker(isolate_); v8::HandleScope handle_scope(isolate_); - gfx::Rect rect = frame->visible_rect(); - size_t rgb_arr_size = rect.width() * rect.height() * 4; + size_t rgb_arr_size = bitmap.width() * bitmap.height() * + bitmap.bytesPerPixel(); v8::MaybeLocal buffer = node::Buffer::New(isolate_, rgb_arr_size); if (buffer.IsEmpty()) return; - // Convert a frame of YUV to 32 bit ARGB. - media::ConvertYUVToRGB32(frame->data(media::VideoFrame::kYPlane), - frame->data(media::VideoFrame::kUPlane), - frame->data(media::VideoFrame::kVPlane), - reinterpret_cast( - node::Buffer::Data(buffer.ToLocalChecked())), - rect.width(), rect.height(), - frame->stride(media::VideoFrame::kYPlane), - frame->stride(media::VideoFrame::kUVPlane), - rect.width() * 4, - media::YV12); + bitmap.copyPixelsTo( + reinterpret_cast(node::Buffer::Data(buffer.ToLocalChecked())), + rgb_arr_size); callback_.Run(buffer.ToLocalChecked()); } diff --git a/atom/browser/api/frame_subscriber.h b/atom/browser/api/frame_subscriber.h index f7748aa5790..a803d75dff2 100644 --- a/atom/browser/api/frame_subscriber.h +++ b/atom/browser/api/frame_subscriber.h @@ -6,7 +6,11 @@ #define ATOM_BROWSER_API_FRAME_SUBSCRIBER_H_ #include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/render_widget_host_view_frame_subscriber.h" +#include "content/public/browser/readback_types.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/size.h" #include "v8/include/v8.h" @@ -19,7 +23,7 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { using FrameCaptureCallback = base::Callback)>; FrameSubscriber(v8::Isolate* isolate, - const gfx::Size& size, + content::RenderWidgetHostView* view, const FrameCaptureCallback& callback); bool ShouldCaptureFrame(const gfx::Rect& damage_rect, @@ -28,13 +32,15 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { DeliverFrameCallback* callback) override; private: - void OnFrameDelivered( - scoped_refptr frame, base::TimeTicks, bool); + void OnFrameDelivered(const FrameCaptureCallback& callback, + const SkBitmap& bitmap, content::ReadbackResponse response); v8::Isolate* isolate_; - gfx::Size size_; + content::RenderWidgetHostView* view_; FrameCaptureCallback callback_; + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(FrameSubscriber); }; diff --git a/atom/browser/api/lib/auto-updater/auto-updater-win.js b/atom/browser/api/lib/auto-updater/auto-updater-win.js index f8a07902f21..1270f8f2bdb 100644 --- a/atom/browser/api/lib/auto-updater/auto-updater-win.js +++ b/atom/browser/api/lib/auto-updater/auto-updater-win.js @@ -3,60 +3,61 @@ const app = require('electron').app; const EventEmitter = require('events').EventEmitter; const squirrelUpdate = require('./squirrel-update-win'); +const util = require('util'); -class AutoUpdater extends EventEmitter { - constructor() { - super(); +function AutoUpdater() { + EventEmitter.call(this); +} + +util.inherits(AutoUpdater, EventEmitter); + +AutoUpdater.prototype.quitAndInstall = function() { + squirrelUpdate.processStart(); + return app.quit(); +}; + +AutoUpdater.prototype.setFeedURL = function(updateURL) { + return this.updateURL = updateURL; +}; + +AutoUpdater.prototype.checkForUpdates = function() { + if (!this.updateURL) { + return this.emitError('Update URL is not set'); } - - quitAndInstall() { - squirrelUpdate.processStart(); - return app.quit(); + if (!squirrelUpdate.supported()) { + return this.emitError('Can not find Squirrel'); } - - setFeedURL(updateURL) { - return this.updateURL = updateURL; - } - - checkForUpdates() { - if (!this.updateURL) { - return this.emitError('Update URL is not set'); - } - if (!squirrelUpdate.supported()) { - return this.emitError('Can not find Squirrel'); - } - this.emit('checking-for-update'); - return squirrelUpdate.download(this.updateURL, (function(_this) { - return function(error, update) { + this.emit('checking-for-update'); + return squirrelUpdate.download(this.updateURL, (function(_this) { + return function(error, update) { + if (error != null) { + return _this.emitError(error); + } + if (update == null) { + return _this.emit('update-not-available'); + } + _this.emit('update-available'); + return squirrelUpdate.update(_this.updateURL, function(error) { + var date, releaseNotes, version; if (error != null) { return _this.emitError(error); } - if (update == null) { - return _this.emit('update-not-available'); - } - _this.emit('update-available'); - return squirrelUpdate.update(_this.updateURL, function(error) { - var date, releaseNotes, version; - if (error != null) { - return _this.emitError(error); - } - releaseNotes = update.releaseNotes, version = update.version; + releaseNotes = update.releaseNotes, version = update.version; - // Following information is not available on Windows, so fake them. - date = new Date; - return _this.emit('update-downloaded', {}, releaseNotes, version, date, _this.updateURL, function() { - return _this.quitAndInstall(); - }); + // Following information is not available on Windows, so fake them. + date = new Date; + return _this.emit('update-downloaded', {}, releaseNotes, version, date, _this.updateURL, function() { + return _this.quitAndInstall(); }); - }; - })(this)); - } + }); + }; + })(this)); +}; - // Private: Emit both error object and message, this is to keep compatibility - // with Old APIs. - emitError(message) { - return this.emit('error', new Error(message), message); - } -} +// Private: Emit both error object and message, this is to keep compatibility +// with Old APIs. +AutoUpdater.prototype.emitError = function(message) { + return this.emit('error', new Error(message), message); +}; module.exports = new AutoUpdater; diff --git a/atom/browser/api/lib/session.js b/atom/browser/api/lib/session.js index dc65264349f..8662759192d 100644 --- a/atom/browser/api/lib/session.js +++ b/atom/browser/api/lib/session.js @@ -1,6 +1,6 @@ const EventEmitter = require('events').EventEmitter; const bindings = process.atomBinding('session'); -const PERSIST_PERFIX = 'persist:'; +const PERSIST_PREFIX = 'persist:'; // Returns the Session from |partition| string. exports.fromPartition = function(partition) { @@ -10,8 +10,8 @@ exports.fromPartition = function(partition) { if (partition === '') { return exports.defaultSession; } - if (partition.startsWith(PERSIST_PERFIX)) { - return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false); + if (partition.startsWith(PERSIST_PREFIX)) { + return bindings.fromPartition(partition.substr(PERSIST_PREFIX.length), false); } else { return bindings.fromPartition(partition, true); } diff --git a/atom/browser/api/lib/web-contents.js b/atom/browser/api/lib/web-contents.js index 709694bcf72..9486378264b 100644 --- a/atom/browser/api/lib/web-contents.js +++ b/atom/browser/api/lib/web-contents.js @@ -58,7 +58,6 @@ let PDFPageSize = { // Following methods are mapped to webFrame. const webFrameMethods = [ - 'executeJavaScript', 'insertText', 'setZoomFactor', 'setZoomLevel', @@ -76,8 +75,11 @@ let wrapWebContents = function(webContents) { // WebContents::send(channel, args..) webContents.send = function() { - var args, channel; - channel = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + var channel = arguments[0]; + if (channel == null) { + throw new Error('Missing required channel argument'); + } return this._send(channel, slice.call(args)); }; @@ -103,14 +105,26 @@ let wrapWebContents = function(webContents) { }; } + const asyncWebFrameMethods = function(requestId, method, callback, ...args) { + this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args); + ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function(event, result) { + if (callback) + callback(result); + }); + }; + // Make sure webContents.executeJavaScript would run the code only when the // webContents has been loaded. - const executeJavaScript = webContents.executeJavaScript; - webContents.executeJavaScript = function(code, hasUserGesture) { + webContents.executeJavaScript = function(code, hasUserGesture, callback) { + let requestId = getNextId(); + if (typeof hasUserGesture === "function") { + callback = hasUserGesture; + hasUserGesture = false; + } if (this.getURL() && !this.isLoading()) - return executeJavaScript.call(this, code, hasUserGesture); + return asyncWebFrameMethods.call(this, requestId, "executeJavaScript", callback, code, hasUserGesture); else - return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture)); + return this.once('did-finish-load', asyncWebFrameMethods.bind(this, requestId, "executeJavaScript", callback, code, hasUserGesture)); }; // Dispatch IPC messages to the ipc module. diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc index aaba1f31045..4d969786c19 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.cc +++ b/atom/browser/atom_resource_dispatcher_host_delegate.cc @@ -26,7 +26,8 @@ bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol( bool has_user_gesture) { GURL escaped_url(net::EscapeExternalHandlerValue(url.spec())); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(platform_util::OpenExternal), escaped_url)); + base::Bind( + base::IgnoreResult(platform_util::OpenExternal), escaped_url, true)); return true; } diff --git a/atom/browser/default_app/default_app.js b/atom/browser/default_app/default_app.js index 2ec765d0d69..ebcca2a5d9a 100644 --- a/atom/browser/default_app/default_app.js +++ b/atom/browser/default_app/default_app.js @@ -9,13 +9,15 @@ app.on('window-all-closed', function() { app.quit(); }); -app.on('ready', function() { - mainWindow = new BrowserWindow({ - width: 800, - height: 600, - autoHideMenuBar: true, - useContentSize: true, +exports.load = function(appUrl) { + app.on('ready', function() { + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + autoHideMenuBar: true, + useContentSize: true, + }); + mainWindow.loadURL(appUrl); + mainWindow.focus(); }); - mainWindow.loadURL('file://' + __dirname + '/index.html'); - mainWindow.focus(); -}); +}; diff --git a/atom/browser/default_app/index.html b/atom/browser/default_app/index.html index ec16a38bc42..80c347eff6d 100644 --- a/atom/browser/default_app/index.html +++ b/atom/browser/default_app/index.html @@ -3,31 +3,39 @@ Electron @@ -78,40 +91,47 @@

-

- To run your app with Electron, execute the following command under your - Console (or Terminal): -

+
- +

+ To run your app with Electron, execute the following command in your + 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 - - on how to write one. -

+ -

- Or you can just drag your app here to run it: -

+

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

+ +

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

+ +

+ Or you can just drag your app here to run it: +

+ +
+ Drag your app here to run it +
-
- Drag your app here to run it
+ + +``` + +## `require('electron').xxx` is undefined. + +Lors de l'utilisation des modules d'Electron, vous pouvez avoir une erreur : + +``` +> require('electron').webFrame.setZoomFactor(1.0); +Uncaught TypeError: Cannot read property 'setZoomLevel' of undefined +``` + +Ceci se produit quand vous avez le [module npm `electron`][electron-module] +d'installé, soit en local ou en global, ce qui a pour effet d'écraser les +modules de base d'Electron. + +Vous vérifiez que vous utilisez les bons modules, vous pouvez afficher le +chemin du module `electron` : + +```javascript +console.log(require.resolve('electron')); +``` + +et vérifier si il est de la forme : + +``` +"/path/to/Electron.app/Contents/Resources/atom.asar/renderer/api/lib/exports/electron.js" +``` + +S'il est de la forme `node_modules/electron/index.js`, vous devez supprimer le +module npm `electron`, ou le renommer. + +```bash +npm uninstall electron +npm uninstall -g electron +``` + +Si vous utilisez le module de base mais que vous continuez d'avoir +l'erreur, ça vient probablement du fait que vous utilisez le module dans le +mauvais processus. Par exemple `electron.app` peut uniquement être utilisé +dans le processus principal, tandis que `electron.webFrame` est uniquement +disponible dans le processus d'affichage. + +[memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management +[variable-scope]: https://msdn.microsoft.com/library/bzt2dkta(v=vs.94).aspx +[electron-module]: https://www.npmjs.com/package/electron diff --git a/docs-translations/fr-FR/styleguide.md b/docs-translations/fr-FR/styleguide.md new file mode 100644 index 00000000000..5fe781773c1 --- /dev/null +++ b/docs-translations/fr-FR/styleguide.md @@ -0,0 +1,101 @@ +# Règles de style pour la documentation d'Electron + +Choisissez la section appropriée : [lire la documentation d'Electron](#reading-electron-documentation) +ou [écrire de la documentation pour Electron](#writing-electron-documentation). + +## Ecrire de la documentation pour Electron + +La documentation d'Electron a été écrite en suivant les règles ci-dessous : + +- Maximum un titre `h1` par page. +- Utilisation de `bash` au lieu de `cmd` dans les blocs de code (à cause de la + coloration syntaxique). +- Les titres `h1` devraient reprendre le nom de l'objet (i.e. `browser-window` → + `BrowserWindow`). + - Cependant, les traits d'union sont acceptés pour les noms de fichier. +- Pas de titre directement après un autre, ajoutez au minimum une ligne de + description entre les deux. +- Les entêtes des méthodes sont entre accents graves (backquotes) `code`. +- Les entêtes des évènements sont entre des apostrophes 'quotation'. +- Les listes ne doivent pas dépasser 2 niveaux (à cause du formattage du + markdown). +- Ajouter des titres de section: Evènements, Méthodes de classe, et Méthodes + d'instance. +- Utiliser 'will' au lieu de 'would' lors de la description du retour. +- Les évènements et méthodes sont des titres `h3`. +- Les arguments optionnels sont notés `function (required[, optional])`. +- Les arguments optionnels sont indiqués quand appelés dans la liste. +- La longueur des lignes ne dépasse pas 80 caractères. +- Les méthodes spécifiques à une plateforme sont notées en italique. + - ```### `method(foo, bar)` _OS X_``` +- Préférer 'in the ___ process' au lieu de 'on' + +### Traductions de la Documentation + +Les traductions de la documentation d'Electron sont dans le dossier +`docs-translations`. + +Pour ajouter une nouvelle langue (ou commencer) : + +- Créer un sous-dossier avec comme nom le code langage. +- A l'intérieur de ce dossier, dupliquer le dossier `docs`, en gardant le même + nom de dossiers et de fichiers. +- Traduire les fichiers. +- Mettre à jour le `README.md` à l'intérieur du dossier de langue en mettant les + liens vers les fichiers traduits. +- Ajouter un lien vers le nouveau dossier de langue dans le [README](https://github.com/atom/electron#documentation-translations) + principal d'Electron. + +## Lire la documentation d'Electron + +Quelques indications pour comprendre la syntaxe de la documentation d'Electron. + +### Méthodes + +Un exemple de la documentation d'une [méthode](https://developer.mozilla.org/en-US/docs/Glossary/Method) +(Anglais) + +--- + +`methodName(required[, optional]))` + +* `require` String (**required**) +* `optional` Integer + +--- + +Le nom de la méthode est suivi des arguments de celle-ci. Les arguments +optionnels sont notés entre crochets, avec une virgule si ceux-ci suivent un +autre argument. + +En-dessous de la méthode, chaque argument est détaillé avec son type. +Celui-ci peut être un type générique : +[`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), +[`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), +[`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), +[`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +ou un type personnalisé comme le [`webContent`](api/web-content.md) d'Electron. + +### Evènements + +Un exemple d'une documentation d'un [évènement](https://developer.mozilla.org/en-US/docs/Web/API/Event) +(Anglais) +--- + +Event: 'wake-up' + +Returns: + +* `time` String + +--- + +L'évènement est une chaine utilisée après un listener `.on`. Si il retourne une +valeur, elle est écrite en dessous ainsi que son type. Si vous voulez écouter et +répondre à l'évènement wake-up, ça donne quelque chose comme : + +```javascript +Alarm.on('wake-up', function(time) { + console.log(time) +}) +``` diff --git a/docs-translations/jp/api/ipc-renderer.md b/docs-translations/jp/api/ipc-renderer.md index 5854157363f..edbd862ffaa 100644 --- a/docs-translations/jp/api/ipc-renderer.md +++ b/docs-translations/jp/api/ipc-renderer.md @@ -52,7 +52,7 @@ * `channel` String - イベント名 * `arg` (optional) -`channel`経由でメインプロセスに非同期にイベントを送信し、任意の引数を送信できます。 +`channel`経由でメインプロセスに同期的にイベントを送信し、任意の引数を送信できます。 メインプロセスは`ipcMain`で`channel`を受信することでハンドルし、 `event.returnValue`を設定してリプライします。 diff --git a/docs-translations/jp/api/menu.md b/docs-translations/jp/api/menu.md index e68a69cca83..12039b1d6ad 100644 --- a/docs-translations/jp/api/menu.md +++ b/docs-translations/jp/api/menu.md @@ -135,7 +135,7 @@ var template = [ ]; if (process.platform == 'darwin') { - var name = require('electron').app.getName(); + var name = require('electron').remote.app.getName(); template.unshift({ label: name, submenu: [ diff --git a/docs-translations/jp/faq/electron-faq.md b/docs-translations/jp/faq/electron-faq.md index c8a65227717..eef30c013a3 100644 --- a/docs-translations/jp/faq/electron-faq.md +++ b/docs-translations/jp/faq/electron-faq.md @@ -12,6 +12,27 @@ Node.js の新しいバージョンがリリースされたとき、私たちは 通常、Node.js の新しい機能は V8 のアップグレードによってもたらされますが、Electron は Chrome ブラウザーに搭載されている V8 を使用しているので、新しい Node.js に入ったばかりのピカピカに新しい JavaScript 機能は Electron ではたいてい既に導入されています。 +## ウェブページ間のデータを共有する方法は? + +ウェブページ(レンダラープロセス)間のデータを共有するために最も単純な方法は、ブラウザで、すでに提供されているHTML5 APIを使用することです。もっとも良い方法は、[Storage API][storage]、[`localStorage`][local-storage]、[`sessionStorage`][session-storage]、[IndexedDB][indexed-db]です。 + +```javascript +// In the main process. +global.sharedObject = { + someProperty: 'default value' +}; +``` + +```javascript +// In page 1. +require('remote').getGlobal('sharedObject').someProperty = 'new value'; +``` + +```javascript +// In page 2. +console.log(require('remote').getGlobal('sharedObject').someProperty); +``` + ## 何分か経つと、アプリの Window/tray が消えてしまいます これは、Window/trayを格納するのに使用している変数がガベージコレクトされたときに発生します。 @@ -104,3 +125,7 @@ npm uninstall -g electron [memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management [variable-scope]: https://msdn.microsoft.com/library/bzt2dkta(v=vs.94).aspx [electron-module]: https://www.npmjs.com/package/electron +[storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage +[local-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage +[session-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage +[indexed-db]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API diff --git a/docs-translations/jp/tutorial/desktop-environment-integration.md b/docs-translations/jp/tutorial/desktop-environment-integration.md index 0ef44b2f47c..96bf76d7370 100644 --- a/docs-translations/jp/tutorial/desktop-environment-integration.md +++ b/docs-translations/jp/tutorial/desktop-environment-integration.md @@ -210,6 +210,23 @@ var window = new BrowserWindow({...}); window.setProgressBar(0.5); ``` +## タスクバーでアイコンをオーバーレイする (Windows) + +Windowsで、タスクバーボタンはアプリケーションステータスを表示するために小さなオーバーレイを使うことができます。MSDNから引用します。 + +> アイコン オーバーレイは、状況に応じた状態通知として機能し、通知領域に状態アイコンを個別に表示する必要性をなくして、情報をユーザーに伝えることを目的としています。たとえば、現在、通知領域に表示される Microsoft Office Outlook の新着メールの通知は、タスク バー ボタンのオーバーレイとして表示できるようになります。ここでも、開発サイクルの間に、アプリケーションに最適な方法を決定する必要があります。アイコン オーバーレイは、重要で長期にわたる状態や通知 (ネットワークの状態、メッセンジャーの状態、新着メールなど) を提供することを目的としています。ユーザーに対して、絶えず変化するオーバーレイやアニメーションを表示しないようにしてください。 + +__タスクバーボタンでのオーバーレイ:__ + +![Overlay on taskbar button](https://i-msdn.sec.s-msft.com/dynimg/IC420441.png) + +ウィンドウでオーバーレイアイコンを設定するために、[BrowserWindow.setOverlayIcon][setoverlayicon] APIを使用できます。 + +```javascript +var window = new BrowserWindow({...}); +window.setOverlayIcon('path/to/overlay.png', 'Description for overlay'); +``` + ## Windowのファイル表示 (OS X) OS Xでは、ウィンドウがrepresented fileを設定でき、タイトルバー上にファイルのアイコンを表示でき、タイトル上でCommand-クリックまたはControl-クリックをすると、パスがポップアップ表示されます。 @@ -228,15 +245,16 @@ window.setRepresentedFilename('/etc/passwd'); window.setDocumentEdited(true); ``` -[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath -[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments -[setusertaskstasks]: ../api/app.md#appsetusertaskstasks -[setprogressbar]: ../api/browser-window.md#browserwindowsetprogressbarprogress -[setrepresentedfilename]: ../api/browser-window.md#browserwindowsetrepresentedfilenamefilename -[setdocumentedited]: ../api/browser-window.md#browserwindowsetdocumenteditededited +[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows +[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows +[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows +[setprogressbar]: ../api/browser-window.md#winsetprogressbarprogress +[setoverlayicon]: ../api/browser-window.md#winsetoverlayiconoverlay-description-windows-7 +[setrepresentedfilename]: ../api/browser-window.md#winsetrepresentedfilenamefilename-os-x +[setdocumentedited]: ../api/browser-window.md#winsetdocumenteditededited-os-x [app-registration]: http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx [unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher -[setthumbarbuttons]: ../api/browser-window.md#browserwindowsetthumbarbuttonsbuttons +[setthumbarbuttons]: ../api/browser-window.md#winsetthumbarbuttonsbuttons-windows-7 [tray-balloon]: ../api/tray.md#traydisplayballoonoptions-windows [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx [notification-spec]: https://developer.gnome.org/notification-spec/ diff --git a/docs-translations/jp/tutorial/quick-start.md b/docs-translations/jp/tutorial/quick-start.md index bfb6e2a51f7..4f2e5c9a52e 100644 --- a/docs-translations/jp/tutorial/quick-start.md +++ b/docs-translations/jp/tutorial/quick-start.md @@ -24,6 +24,8 @@ Electron はウェブページを表示させるために Chromium を使用し Electron では、メインプロセスとレンダラープロセスとのコミュニケーションをするために [ipc](../api/ipc-renderer.md) モジュールを提供しています。またそれと、RPC 形式の通信を行う [remote](../api/remote.md) モジュールもあります。 +Electron では、メインプロセスとレンダラープロセスとのコミュニケーションをするには幾つかのほうほうがあります。メッセージを送信する[`ipcRenderer`](../api/ipc-renderer.md)モジュールと[`ipcMain`](../api/ipc-main.md)モジュールのように、RPC 形式の通信を行う[remote](../api/remote.md)モジュールです。[ウェブページ間のデータを共有する方法][share-data]にFAQエントリーがあります。 + ## Electronアプリを作成する 一般的に Electron アプリの構成は次のようになります: @@ -169,3 +171,5 @@ $ cd electron-quick-start # Install dependencies and run the app $ npm install && npm start ``` + +[share-data]: ../faq/electron-faq.md#how-to-share-data-between-web-pages diff --git a/docs-translations/jp/tutorial/using-selenium-and-webdriver.md b/docs-translations/jp/tutorial/using-selenium-and-webdriver.md index 3202028ebf5..a1fd0f21d7f 100644 --- a/docs-translations/jp/tutorial/using-selenium-and-webdriver.md +++ b/docs-translations/jp/tutorial/using-selenium-and-webdriver.md @@ -41,7 +41,7 @@ var driver = new webdriver.Builder() .withCapabilities({ chromeOptions: { // Here is the path to your Electron binary. - binary: '/Path-to-Your-App.app/Contents/MacOS/Atom', + binary: '/Path-to-Your-App.app/Contents/MacOS/Electron', } }) .forBrowser('electron') diff --git a/docs-translations/jp/tutorial/using-widevine-cdm-plugin.md b/docs-translations/jp/tutorial/using-widevine-cdm-plugin.md index 5cee7f26dc6..55f9d039f61 100644 --- a/docs-translations/jp/tutorial/using-widevine-cdm-plugin.md +++ b/docs-translations/jp/tutorial/using-widevine-cdm-plugin.md @@ -6,6 +6,8 @@ Electronで、Chromeブラウザーに同梱される Widevine CDMプラグイ Electronは、ライセンス的な理由でWidevine CDMプラグインは同梱されません。Widevine CDMプラグインを取得するために、最初に、使用するElectronビルドのChromバージョンとアーキテクチャを合わせた公式のChromeブラウザーをインストールする必要があります。 +__Note:__ Chromeブラウザの主要バージョンは、Electronが使用するChromeバージョンと同じでなければなりません。そうでなければ、プラグインは、`navigator.plugins`経由でロードされて表示されるにも関わらず動作しません。 + ### Windows & OS X Chromeブラウザーで、`chrome://components/`を開き、 `WidevineCdm` を探し、それが最新であることを確認し、`APP_DATA/Google/Chrome/WidevineCDM/VERSION/_platform_specific/PLATFORM_ARCH/`ディレクトリからすべてのプラグインバイナリを探します。 diff --git a/docs-translations/ko-KR/api/app.md b/docs-translations/ko-KR/api/app.md index ec4cb74c253..048026e0d59 100644 --- a/docs-translations/ko-KR/api/app.md +++ b/docs-translations/ko-KR/api/app.md @@ -92,7 +92,7 @@ Returns: 이 이벤트를 처리할 땐 반드시 `event.preventDefault()`를 호출해야 합니다. -Windows에선 `process.argv`를 통해 파일 경로를 얻을 수 있습니다. +Windows에선 `process.argv` (메인 프로세스에서)를 통해 파일 경로를 얻을 수 있습니다. ### Event: 'open-url' _OS X_ @@ -164,7 +164,7 @@ Returns: 기본 동작을 방지하고 인증을 승인할 수 있습니다. ```javascript -session.on('certificate-error', function(event, webContents, url, error, certificate, callback) { +app.on('certificate-error', function(event, webContents, url, error, certificate, callback) { if (url == "https://github.com") { // Verification logic. event.preventDefault(); @@ -523,7 +523,7 @@ dock 아이콘을 표시합니다. ### `app.dock.setMenu(menu)` _OS X_ -* `menu` Menu +* `menu` [Menu](menu.md) 어플리케이션의 [dock menu][dock-menu]를 설정합니다. diff --git a/docs-translations/ko-KR/api/browser-window.md b/docs-translations/ko-KR/api/browser-window.md index f79b201d0a1..8bc4a1545fc 100644 --- a/docs-translations/ko-KR/api/browser-window.md +++ b/docs-translations/ko-KR/api/browser-window.md @@ -451,7 +451,6 @@ var win = new BrowserWindow({ width: 800, height: 600 }); * `aspectRatio` 유지하려 하는 컨텐츠 뷰 일부의 종횡비 * `extraSize` Object (optional) - 종횡비를 유지하는 동안 포함되지 않을 엑스트라 크기. - 사용 가능한 속성: * `width` Integer * `height` Integer @@ -469,7 +468,7 @@ var win = new BrowserWindow({ width: 800, height: 600 }); ### `win.setBounds(options[, animate])` -* `options` Object, properties: +* `options` Object * `x` Integer * `y` Integer @@ -719,8 +718,7 @@ Windows 메시지 훅을 등록합니다. `callback`은 WndProc에서 메시지 ### `win.capturePage([rect, ]callback)` -* `rect` Object (optional) - 캡쳐할 페이지의 영역. - 사용할 수 있는 속성은 다음과 같습니다: +* `rect` Object (optional) - 캡쳐할 페이지의 영역 * `x` Integer * `y` Integer * `width` Integer @@ -773,7 +771,7 @@ Linux 플랫폼에선 Unity 데스크톱 환경만 지원합니다. 그리고 아이콘입니다. `null`로 지정하면 빈 오버레이가 사용됩니다 * `description` String - 접근성 설정에 의한 스크린 리더에 제공될 설명입니다 -현재 작업표시줄 아이콘에 16px 크기의 오버레이를 지정합니다. 보통 이 기능은 +현재 작업표시줄 아이콘에 16 x 16 픽셀 크기의 오버레이를 지정합니다. 보통 이 기능은 어플리케이션의 여러 상태를 사용자에게 소극적으로 알리기 위한 방법으로 사용됩니다. ### `win.setHasShadow(hasShadow)` _OS X_ @@ -790,34 +788,34 @@ Linux 플랫폼에선 Unity 데스크톱 환경만 지원합니다. 그리고 ### `win.setThumbarButtons(buttons)` _Windows 7+_ -`buttons` - `button` 객체의 배열 - -`button` 객체는 다음과 같은 속성을 가지고 있습니다: - -* `icon` [NativeImage](native-image.md) - 미리보기 툴바에 보여질 아이콘. -* `tooltip` String (optional) - 버튼의 툴팁 텍스트. -* `flags` Array (optional) - 버튼의 특정 동작 및 상태 제어. 기본적으로 `enabled`이 - 사용됩니다. 이 속성은 다음 문자열들을 포함할 수 있습니다: - * `enabled` - 사용자가 사용할 수 있도록 버튼이 활성화 됩니다. - * `disabled` - 버튼이 비활성화 됩니다. 버튼은 표시되지만 시각적인 상태는 사용자의 - 동작에 응답하지 않는 비활성화 상태로 표시됩니다. - * `dismissonclick` - 버튼이 클릭되면 작업표시줄 버튼의 미리보기(flyout)가 즉시 - 종료됩니다. - * `nobackground` - 버튼의 테두리를 표시하지 않습니다. 이미지에만 사용할 수 있습니다. - * `hidden` - 버튼을 사용자에게 표시되지 않도록 숨깁니다. - * `noninteractive` - 버튼은 활성화되어 있지만 반응이 제거되며 버튼을 눌러도 - 눌려지지 않은 상태를 유지합니다. 이 값은 버튼을 알림의 용도로 사용하기 위해 - 만들어졌습니다. -* `click` - Function +* `buttons` - Array 윈도우 작업표시줄 버튼 레이아웃의 미리보기 이미지 영역에 미리보기 툴바와 버튼 세트를 -지정합니다. 반환되는 `Boolean` 값은 미리보기 툴바가 성공적으로 추가됬는지를 알려줍니다. +추가합니다. 반환되는 `Boolean` 값은 미리보기 툴바가 성공적으로 추가됬는지를 알려줍니다. 미리보기 이미지 영역의 제한된 크기로 인해 미리보기 툴바에 추가될 수 있는 최대 버튼의 개수는 7개이며 이 이상 추가될 수 없습니다. 플랫폼의 제약으로 인해 미리보기 툴바는 한 번 설정되면 삭제할 수 없습니다. 하지만 이 API에 빈 배열을 전달하여 버튼들을 제거할 수 있습니다. +`buttons`는 `Button` 객체의 배열입니다: + +* `Button` 객체 + * `icon` [NativeImage](native-image.md) - 미리보기 툴바에 보여질 아이콘. + * `tooltip` String (optional) - 버튼의 툴팁 텍스트. + * `flags` Array (optional) - 버튼의 특정 동작 및 상태 제어. 기본적으로 `enabled`이 + 사용됩니다. 이 속성은 다음 문자열들을 포함할 수 있습니다: + * `enabled` - 사용자가 사용할 수 있도록 버튼이 활성화 됩니다. + * `disabled` - 버튼이 비활성화 됩니다. 버튼은 표시되지만 시각적인 상태는 사용자의 + 동작에 응답하지 않는 비활성화 상태로 표시됩니다. + * `dismissonclick` - 버튼이 클릭되면 작업표시줄 버튼의 미리보기(flyout)가 즉시 + 종료됩니다. + * `nobackground` - 버튼의 테두리를 표시하지 않습니다. 이미지에만 사용할 수 있습니다. + * `hidden` - 버튼을 사용자에게 표시되지 않도록 숨깁니다. + * `noninteractive` - 버튼은 활성화되어 있지만 반응이 제거되며 버튼을 눌러도 + 눌려지지 않은 상태를 유지합니다. 이 값은 버튼을 알림의 용도로 사용하기 위해 + 만들어졌습니다. + ### `win.showDefinitionForSelection()` _OS X_ 페이지의 선택된 단어에 대한 사전 검색 결과 팝업을 표시합니다. diff --git a/docs-translations/ko-KR/api/clipboard.md b/docs-translations/ko-KR/api/clipboard.md index 2a0c98da2c0..df6475ad611 100644 --- a/docs-translations/ko-KR/api/clipboard.md +++ b/docs-translations/ko-KR/api/clipboard.md @@ -62,6 +62,19 @@ console.log(clipboard.readText('selection')); 클립보드에 `image`를 씁니다. +### `clipboard.readRtf([type])` + +* `type` String (optional) + +클립보드로부터 RTF 형식으로 컨텐츠를 읽어옵니다. + +### `clipboard.writeRtf(text[, type])` + +* `text` String +* `type` String (optional) + +클립보드에 `text`를 RTF 형식으로 씁니다. + ### `clipboard.clear([type])` * `type` String (optional) diff --git a/docs-translations/ko-KR/api/crash-reporter.md b/docs-translations/ko-KR/api/crash-reporter.md index 0b7daba14dd..81713760a85 100644 --- a/docs-translations/ko-KR/api/crash-reporter.md +++ b/docs-translations/ko-KR/api/crash-reporter.md @@ -22,19 +22,15 @@ crashReporter.start({ ### `crashReporter.start(options)` -* `options` Object, properties: - -* `productName` String, 기본값: Electron -* `companyName` String (**필수항목**) -* `submitURL` String, (**필수항목**) - * 크래시 리포트는 POST 방식으로 이 URL로 전송됩니다. -* `autoSubmit` Boolean, 기본값: true - * true로 지정할 경우 유저의 승인 없이 자동으로 오류를 보고합니다. -* `ignoreSystemCrashHandler` Boolean, 기본값: false -* `extra` Object - * 크래시 리포트 시 같이 보낼 추가 정보를 지정하는 객체입니다. - * 문자열로 된 속성만 정상적으로 보내집니다. - * 중첩 객체는 지원되지 않습니다. (Nested objects are not supported) +* `options` Object + * `companyName` String + * `submitURL` String - 크래시 리포트는 POST 방식으로 이 URL로 전송됩니다. + * `productName` String (optional) - 기본값은 `Electron` 입니다. + * `autoSubmit` Boolean - 유저의 승인 없이 자동으로 오류를 보고합니다. 기본값은 + `true` 입니다. + * `ignoreSystemCrashHandler` Boolean - 기본값은 `false` 입니다. + * `extra` Object - 크래시 리포트 시 같이 보낼 추가 정보를 지정하는 객체입니다. + 문자열로 된 속성만 정상적으로 보내집니다. 중첩된 객체는 지원되지 않습니다. 다른 crashReporter API를 사용하기 전에 이 메서드를 먼저 호출해야 합니다. diff --git a/docs-translations/ko-KR/api/ipc-main.md b/docs-translations/ko-KR/api/ipc-main.md index 3ae519d294b..30b10247f9d 100644 --- a/docs-translations/ko-KR/api/ipc-main.md +++ b/docs-translations/ko-KR/api/ipc-main.md @@ -8,8 +8,7 @@ ## 메시지 전송 물론 메시지를 받는 것 말고도 메인 프로세스에서 랜더러 프로세스로 보내는 것도 가능합니다. -자세한 내용은 [webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-)를 -참고하세요. +자세한 내용은 [webContents.send][web-contents-send]를 참고하세요. * 메시지를 전송할 때 이벤트 이름은 `channel`이 됩니다. * 메시지에 동기로 응답할 땐 반드시 `event.returnValue`를 설정해야 합니다. @@ -46,34 +45,40 @@ ipcRenderer.send('asynchronous-message', 'ping'); `ipcMain`은 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다: -### `ipcMain.on(channel, callback)` +### `ipcMain.on(channel, listener)` -* `channel` String - 이벤트 이름 -* `callback` Function +* `channel` String +* `listener` Function -이벤트가 발생하면 `event` 객체와 임의 메시지와 함께 `callback`이 호출됩니다. +`channel`에 대해 이벤트를 리스닝합니다. 새로운 메시지가 도착하면 `listener`가 +`listener(event, args...)` 형식으로 호출됩니다. -### `ipcMain.removeListener(channel, callback)` +### `ipcMain.once(channel, listener)` -* `channel` String - 이벤트의 이름 -* `callback` Function - `ipcMain.on(channel, callback)`에서 사용한 함수의 레퍼런스 +* `channel` String +* `listener` Function + +이벤트에 대해 한 번만 작동하는 `listener` 함수를 등록합니다. 이 `listener`는 등록된 +후 `channel`에 보내지는 메시지에 한해 호출됩니다. 호출된 이후엔 리스너가 삭제됩니다. + +### `ipcMain.removeListener(channel, listener)` + +* `channel` String +* `listener` Function 메시지 수신을 완료한 후, 더 이상의 콜백이 필요하지 않을 때 또는 몇 가지 이유로 채널의 메시지 전송을 멈출수 없을 때, 이 함수를 통해 지정한 채널에 대한 콜백을 삭제할 수 있습니다. +지정한 `channel`에 대한 리스너를 저장하는 배열에서 지정한 `listener`를 삭제합니다. + ### `ipcMain.removeAllListeners(channel)` -* `channel` String - 이벤트의 이름 +* `channel` String (optional) -이 ipc 채널에 등록된 *모든* 핸들러들을 삭제합니다. +이 ipc 채널에 등록된 모든 핸들러들을 삭제하거나 지정한 `channel`을 삭제합니다. -### `ipcMain.once(channel, callback)` - -`ipcMain.on()` 대신 이 함수를 사용할 경우 핸들러가 단 한 번만 호출됩니다. -`callback`이 한 번 호출된 이후 활성화되지 않습니다. - -## IPC Event +## Event 객체 `callback`에서 전달된 `event` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: @@ -85,5 +90,6 @@ ipcRenderer.send('asynchronous-message', 'ping'); 메시지를 보낸 `webContents` 객체를 반환합니다. `event.sender.send` 메서드를 통해 비동기로 메시지를 전달할 수 있습니다. 자세한 내용은 -[webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-)를 -참고하세요. +[webContents.send][web-contents-send]를 참고하세요. + +[web-contents-send]: web-contents.md#webcontentssendchannel-arg1-arg2- diff --git a/docs-translations/ko-KR/api/ipc-renderer.md b/docs-translations/ko-KR/api/ipc-renderer.md index aba632430b5..0d43b157d16 100644 --- a/docs-translations/ko-KR/api/ipc-renderer.md +++ b/docs-translations/ko-KR/api/ipc-renderer.md @@ -10,32 +10,38 @@ `ipcRenderer`는 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다: -### `ipcRenderer.on(channel, callback)` +### `ipcMain.on(channel, listener)` -* `channel` String - 이벤트 이름 -* `callback` Function +* `channel` String +* `listener` Function -이벤트가 일어나면 `event` 객체와 임의의 인자와 함께 `callback` 함수가 호출됩니다. +`channel`에 대해 이벤트를 리스닝합니다. 새로운 메시지가 도착하면 `listener`가 +`listener(event, args...)` 형식으로 호출됩니다. -### `ipcMain.removeListener(channel, callback)` +### `ipcMain.once(channel, listener)` -* `channel` String - 이벤트의 이름 -* `callback` Function - `ipcMain.on(channel, callback)`에서 사용한 함수의 레퍼런스 +* `channel` String +* `listener` Function + +이벤트에 대해 한 번만 작동하는 `listener` 함수를 등록합니다. 이 `listener`는 등록된 +후 `channel`에 보내지는 메시지에 한해 호출됩니다. 호출된 이후엔 리스너가 삭제됩니다. + +### `ipcMain.removeListener(channel, listener)` + +* `channel` String +* `listener` Function 메시지 수신을 완료한 후, 더 이상의 콜백이 필요하지 않을 때 또는 몇 가지 이유로 채널의 메시지 전송을 멈출수 없을 때, 이 함수를 통해 지정한 채널에 대한 콜백을 삭제할 수 있습니다. +지정한 `channel`에 대한 리스너를 저장하는 배열에서 지정한 `listener`를 삭제합니다. + ### `ipcMain.removeAllListeners(channel)` -* `channel` String - 이벤트의 이름 +* `channel` String (optional) -이 ipc 채널에 등록된 *모든* 핸들러들을 삭제합니다. - -### `ipcMain.once(channel, callback)` - -`ipcMain.on()` 대신 이 함수를 사용할 경우 핸들러가 단 한 번만 호출됩니다. -`callback`이 한 번 호출된 이후 활성화되지 않습니다. +이 ipc 채널에 등록된 모든 핸들러들을 삭제하거나 지정한 `channel`을 삭제합니다. ## 메시지 보내기 @@ -43,23 +49,26 @@ ### `ipcRenderer.send(channel[, arg1][, arg2][, ...])` -* `channel` String - 이벤트 이름 +* `channel` String * `arg` (optional) `channel`을 통해 메인 프로세스에 비동기 메시지를 보냅니다. 그리고 필요에 따라 임의의 -인자를 사용할 수도 있습니다. 메인 프로세스는 `ipcMain` 모듈의 `channel` 이벤트를 통해 +인수를 사용할 수도 있습니다. 인수들은 내부적으로 JSON 포맷으로 직렬화 되며, 이후 함수와 +프로토타입 체인은 포함되지 않게 됩니다. + +메인 프로세스는 `ipcMain` 모듈의 `channel` 이벤트를 통해 이벤트를 리스닝 할 수 있습니다. ### `ipcRenderer.sendSync(channel[, arg1][, arg2][, ...])` -* `channel` String - 이벤트 이름 +* `channel` String * `arg` (optional) `channel`을 통해 메인 프로세스에 동기 메시지를 보냅니다. 그리고 필요에 따라 임의의 -인자를 사용할 수도 있습니다. 메인 프로세스는 `ipc`를 통해 `channel` 이벤트를 리스닝 -할 수 있습니다. +인자를 사용할 수도 있습니다. 인수들은 내부적으로 JSON 포맷으로 직렬화 되며, 이후 함수와 +프로토타입 체인은 포함되지 않게 됩니다. -메인 프로세스에선 `ipcMain` 모듈의 `channel` 이벤트를 통해 받은 +메인 프로세스는 `ipcMain` 모듈을 통해 `channel` 이벤트를 리스닝 할 수 있고, `event.returnValue`로 회신 할 수 있습니다. __참고:__ 동기 메서드는 모든 랜더러 프로세스의 작업을 일시 중단시킵니다. 사용 목적이 @@ -67,7 +76,7 @@ __참고:__ 동기 메서드는 모든 랜더러 프로세스의 작업을 일 ### `ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])` -* `channel` String - 이벤트 이름 +* `channel` String * `arg` (optional) `ipcRenderer.send`와 비슷하지만 이벤트를 메인 프로세스 대신 호스트 페이지내의 diff --git a/docs-translations/ko-KR/api/menu.md b/docs-translations/ko-KR/api/menu.md index 347ea880dce..61c68023b07 100644 --- a/docs-translations/ko-KR/api/menu.md +++ b/docs-translations/ko-KR/api/menu.md @@ -140,7 +140,7 @@ var template = [ ]; if (process.platform == 'darwin') { - var name = require('electron').app.getName(); + var name = require('electron').remote.app.getName(); template.unshift({ label: name, submenu: [ diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index 59eceb05a7a..5bb4867eda0 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -59,7 +59,8 @@ var ses = session.fromPartition('persist:name'); Electron의 `webContents`에서 `item`을 다운로드할 때 발생하는 이벤트입니다. -`event.preventDefault()` 메서드를 호출하면 다운로드를 취소합니다. +`event.preventDefault()` 메서드를 호출하면 다운로드를 취소하고, 프로세스의 다음 +틱부터 `item`을 사용할 수 없게 됩니다. ```javascript session.defaultSession.on('will-download', function(event, item, webContents) { @@ -293,26 +294,32 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c * `handler` Function * `webContents` Object - [WebContents](web-contents.md) 권한을 요청. * `permission` String - 'media', 'geolocation', 'notifications', - 'midiSysex'의 나열. + 'midiSysex', 'pointerLock', 'fullscreen'의 나열. * `callback` Function - 권한 허용 및 거부. `session`의 권한 요청에 응답을 하는데 사용하는 핸들러를 설정합니다. -`callback('granted')`를 호출하면 권한 제공을 허용하고 `callback('denied')`를 +`callback(true)`를 호출하면 권한 제공을 허용하고 `callback(false)`를 호출하면 권한 제공을 거부합니다. ```javascript session.fromPartition(partition).setPermissionRequestHandler(function(webContents, permission, callback) { if (webContents.getURL() === host) { if (permission == "notifications") { - callback(); // 거부됨. + callback(false); // 거부됨. return; } } - callback('granted'); + callback(true); }); ``` +#### `ses.clearHostResolverCache([callback])` + +* `callback` Function (optional) - 작업이 완료되면 호출됩니다. + +호스트 리소버(resolver) 캐시를 지웁니다. + #### `ses.webRequest` `webRequest` API는 생명주기의 다양한 단계에 맞춰 요청 컨텐츠를 가로채거나 변경할 수 diff --git a/docs-translations/ko-KR/api/shell.md b/docs-translations/ko-KR/api/shell.md index 4183d3b2502..c4831092f3c 100644 --- a/docs-translations/ko-KR/api/shell.md +++ b/docs-translations/ko-KR/api/shell.md @@ -25,12 +25,16 @@ shell.openExternal('https://github.com'); 지정한 파일을 데스크톱 기본 프로그램으로 엽니다. -### `shell.openExternal(url)` +### `shell.openExternal(url[, options])` * `url` String +* `options` Object (optional) _OS X_ + * `activate` Boolean - `true`로 설정하면 어플리케이션을 바로 활성화 상태로 + 실행합니다. 기본값은 `true`입니다. 제공된 외부 프로토콜 URL을 기반으로 데스크톱의 기본 프로그램으로 엽니다. (예를 들어 -mailto: URL은 유저의 기본 이메일 에이전트로 URL을 엽니다.) +mailto: URL은 유저의 기본 이메일 에이전트로 URL을 엽니다.) 어플리케이션이 해당 URL을 +열 수 있을 때 `true`를 반환합니다. 아니라면 `false`를 반환합니다. 역주: 폴더는 'file:\\\\C:\\'와 같이 지정하여 열 수 있습니다. (Windows의 경우) diff --git a/docs-translations/ko-KR/api/tray.md b/docs-translations/ko-KR/api/tray.md index e182f6471e7..d82b0a9d7e4 100644 --- a/docs-translations/ko-KR/api/tray.md +++ b/docs-translations/ko-KR/api/tray.md @@ -32,6 +32,13 @@ __플랫폼별 한계:__ 트레이 아이콘이 작동하도록 만들 수 있습니다. * 앱 알림 표시기는 컨텍스트 메뉴를 가지고 있을 때만 보입니다. * Linux에서 앱 표시기가 사용될 경우, `click` 이벤트는 무시됩니다. +* Linux에서 각각 개별 `MenuItem`의 변경을 적용하려면 `setContextMenu`를 다시 + 호출해야 합니다. 예를 들면: + +```javascript +contextMenu.items[2].checked = false; +appIcon.setContextMenu(contextMenu); +``` 이러한 이유로 Tray API가 모든 플랫폼에서 똑같이 작동하게 하고 싶다면 `click` 이벤트에 의존해선 안되며 언제나 컨텍스트 메뉴를 포함해야 합니다. diff --git a/docs-translations/ko-KR/api/web-contents.md b/docs-translations/ko-KR/api/web-contents.md index 3f9a7daaaa7..6e41f31155c 100644 --- a/docs-translations/ko-KR/api/web-contents.md +++ b/docs-translations/ko-KR/api/web-contents.md @@ -255,8 +255,8 @@ Returns: * `result` Object * `requestId` Integer * `finalUpdate` Boolean - 더 많은 응답이 따르는 경우를 표시합니다. - * `matches` Integer (Optional) - 일치하는 개수. - * `selectionArea` Object (Optional) - 첫 일치 부위의 좌표. + * `matches` Integer (optional) - 일치하는 개수. + * `selectionArea` Object (optional) - 첫 일치 부위의 좌표. [`webContents.findInPage`](web-contents.md#webcontentsfindinpage) 요청의 결과를 사용할 수 있을 때 발생하는 이벤트입니다. @@ -307,7 +307,7 @@ Returns: ### `webContents.loadURL(url[, options])` * `url` URL -* `options` Object (optional), 속성들: +* `options` Object (optional) * `httpReferrer` String - HTTP 레퍼러 url. * `userAgent` String - 요청을 시작한 유저 에이전트. * `extraHeaders` String - "\n"로 구분된 Extra 헤더들. @@ -493,7 +493,7 @@ CSS 코드를 현재 웹 페이지에 삽입합니다. ### `webContents.findInPage(text[, options])` * `text` String - 찾을 컨텐츠, 반드시 공백이 아니여야 합니다. -* `options` Object (Optional) +* `options` Object (optional) * `forward` Boolean - 앞에서부터 검색할지 뒤에서부터 검색할지 여부입니다. 기본값은 `true`입니다. * `findNext` Boolean - 작업을 계속 처리할지 첫 요청만 처리할지 여부입니다. 기본값은 @@ -546,11 +546,10 @@ ServiceWorker가 존재하면 모두 등록을 해제하고 JS Promise가 만족 ### `webContents.print([options])` -`options` Object (optional), properties: - -* `silent` Boolean - 사용자에게 프린트 설정을 묻지 않습니다. 기본값을 `false`입니다. -* `printBackground` Boolean - 웹 페이지의 배경 색과 이미지를 출력합니다. 기본값은 - `false`입니다. +`options` Object (optional) + * `silent` Boolean - 사용자에게 프린트 설정을 묻지 않습니다. 기본값을 `false`입니다. + * `printBackground` Boolean - 웹 페이지의 배경 색과 이미지를 출력합니다. 기본값은 + `false`입니다. 윈도우의 웹 페이지를 프린트합니다. `silent`가 `false`로 지정되어있을 땐, Electron이 시스템의 기본 프린터와 기본 프린터 설정을 가져옵니다. @@ -565,32 +564,23 @@ print기능을 사용하지 않는 경우 전체 바이너리 크기를 줄이 ### `webContents.printToPDF(options, callback)` -`options` Object, properties: - -* `marginsType` Integer - 사용할 마진의 종류를 지정합니다. - * 0 - default - * 1 - none - * 2 - minimum -* `pageSize` String - 생성되는 PDF의 페이지 크기를 지정합니다. - * `A5` - * `A4` - * `A3` - * `Legal` - * `Letter` - * `Tabloid` -* `printBackground` Boolean - CSS 배경을 프린트할지 여부를 정합니다. -* `printSelectionOnly` Boolean - 선택된 영역만 프린트할지 여부를 정합니다. -* `landscape` Boolean - landscape을 위해선 `true`를, portrait를 위해선 `false`를 - 사용합니다. - -`callback` Function - `function(error, data) {}` - -* `error` Error -* `data` Buffer - PDF 파일 내용. +* `options` Object + * `marginsType` Integer - 사용할 마진의 종류를 지정합니다. 0 부터 2 사이 값을 사용할 + 수 있고 각각 기본 마진, 마진 없음, 최소 마진입니다. + * `pageSize` String - 생성되는 PDF의 페이지 크기를 지정합니다. 값은 `A3`, `A4`, + `A5`, `Legal`, `Letter` 와 `Tabloid`가 사용될 수 있습니다. + * `printBackground` Boolean - CSS 배경을 프린트할지 여부를 정합니다. + * `printSelectionOnly` Boolean - 선택된 영역만 프린트할지 여부를 정합니다. + * `landscape` Boolean - landscape을 위해선 `true`를, portrait를 위해선 `false`를 + 사용합니다. +* `callback` Function - `function(error, data) {}` Chromium의 미리보기 프린팅 커스텀 설정을 이용하여 윈도우의 웹 페이지를 PDF로 프린트합니다. +`callback`은 작업이 완료되면 `callback(error, data)` 형식으로 호출됩니다. `data`는 +생성된 PDF 데이터를 담고있는 `Buffer`입니다. + 기본으로 비어있는 `options`은 다음과 같이 여겨지게 됩니다: ```javascript @@ -643,7 +633,7 @@ mainWindow.webContents.on('devtools-opened', function() { ### `webContents.openDevTools([options])` -* `options` Object (optional). Properties: +* `options` Object (optional) * `detach` Boolean - 새 창에서 개발자 도구를 엽니다. 개발자 도구를 엽니다. @@ -681,8 +671,11 @@ mainWindow.webContents.on('devtools-opened', function() { * `arg` (optional) `channel`을 통하여 렌더러 프로세스에 비동기 메시지를 보냅니다. 임의의 인수를 보낼수도 -있습니다. 렌더러 프로세스는 `ipcRenderer` 모듈을 통하여 `channel`를 리슨하여 메시지를 -처리할 수 있습니다. +있습니다. 인수들은 내부적으로 JSON 포맷으로 직렬화 되며, 이후 함수와 프로토타입 체인은 +포함되지 않게 됩니다. + +렌더러 프로세스는 `ipcRenderer` 모듈을 통하여 `channel`를 리스닝하여 메시지를 처리할 +수 있습니다. 메인 프로세스에서 렌더러 프로세스로 메시지를 보내는 예시 입니다: diff --git a/docs-translations/ko-KR/api/web-view-tag.md b/docs-translations/ko-KR/api/web-view-tag.md index bd07c883e06..30da9f253b4 100644 --- a/docs-translations/ko-KR/api/web-view-tag.md +++ b/docs-translations/ko-KR/api/web-view-tag.md @@ -178,7 +178,7 @@ webview.addEventListener("dom-ready", function() { ### `.loadURL(url[, options])` * `url` URL -* `options` Object (optional), 속성들: +* `options` Object (optional) * `httpReferrer` String - HTTP 레퍼러 url. * `userAgent` String - 요청을 시작한 유저 에이전트. * `extraHeaders` String - "\n"로 구분된 Extra 헤더들. @@ -370,7 +370,7 @@ Service worker에 대한 개발자 도구를 엽니다. ### `webContents.findInPage(text[, options])` * `text` String - 찾을 컨텐츠, 반드시 공백이 아니여야 합니다. -* `options` Object (Optional) +* `options` Object (optional) * `forward` Boolean - 앞에서부터 검색할지 뒤에서부터 검색할지 여부입니다. 기본값은 `true`입니다. * `findNext` Boolean - 작업을 계속 처리할지 첫 요청만 처리할지 여부입니다. 기본값은 @@ -557,8 +557,8 @@ Returns: * `result` Object * `requestId` Integer * `finalUpdate` Boolean - 더 많은 응답이 따르는 경우를 표시합니다. - * `matches` Integer (Optional) - 일치하는 개수. - * `selectionArea` Object (Optional) - 첫 일치 부위의 좌표. + * `matches` Integer (optional) - 일치하는 개수. + * `selectionArea` Object (optional) - 첫 일치 부위의 좌표. [`webContents.findInPage`](web-contents.md#webcontentsfindinpage) 요청의 결과를 사용할 수 있을 때 발생하는 이벤트입니다. diff --git a/docs-translations/ko-KR/faq/electron-faq.md b/docs-translations/ko-KR/faq/electron-faq.md index 0978dc00539..ea9953fcd4c 100644 --- a/docs-translations/ko-KR/faq/electron-faq.md +++ b/docs-translations/ko-KR/faq/electron-faq.md @@ -18,6 +18,34 @@ Node.js의 새로운 기능은 보통 V8 업그레이드에서 가져옵니다. 브라우저에 탑재된 V8을 사용하고 있습니다. 눈부신 새로운 Node.js 버전의 자바스크립트 기능은 보통 이미 Electron에 있습니다. +## 어떻게 웹 페이지 간에 데이터를 공유할 수 있나요? + +두 웹페이지 간에 (랜더러 프로세스) 데이터를 공유하려면 간단히 이미 모든 브라우저에서 +사용할 수 있는 HTML5 API들을 사용하면 됩니다. 가장 좋은 후보는 +[Storage API][storage], [`localStorage`][local-storage], +[`sessionStorage`][session-storage], 그리고 [IndexedDB][indexed-db]가 있습니다. + +또는 Electron에서만 사용할 수 있는 IPC 시스템을 사용하여 메인 프로세스의 global +변수에 데이터를 저장한 후 다음과 같이 랜더러 프로세스에서 `remote` 모듈을 사용하여 +접근할 수 있습니다: + +```javascript +// 메인 프로세스에서 +global.sharedObject = { + someProperty: 'default value' +}; +``` + +```javascript +// 첫 번째 페이지에서 +require('remote').getGlobal('sharedObject').someProperty = 'new value'; +``` + +```javascript +// 두 번째 페이지에서 +console.log(require('remote').getGlobal('sharedObject').someProperty); +``` + ## 제작한 어플리케이션의 윈도우/트레이가 몇 분 후에나 나타납니다. 이러한 문제가 발생하는 이유는 보통 윈도우/트레이를 담은 변수에 가비지 컬렉션이 작동해서 @@ -119,3 +147,7 @@ npm uninstall -g electron [memory-management]: https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_Management [variable-scope]: https://msdn.microsoft.com/library/bzt2dkta(v=vs.94).aspx [electron-module]: https://www.npmjs.com/package/electron +[storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage +[local-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage +[session-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage +[indexed-db]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API diff --git a/docs-translations/ko-KR/tutorial/application-distribution.md b/docs-translations/ko-KR/tutorial/application-distribution.md index 63533757415..7072d7c4d95 100644 --- a/docs-translations/ko-KR/tutorial/application-distribution.md +++ b/docs-translations/ko-KR/tutorial/application-distribution.md @@ -59,9 +59,8 @@ electron/resources/ ### Windows -`electron.exe`을 원하는 이름으로 변경할 수 있습니다. -그리고 [rcedit](https://github.com/atom/rcedit) -를 사용하여 아이콘을 변경할 수 있습니다. +[rcedit](https://github.com/atom/rcedit)를 통해 `electron.exe`을 원하는 이름으로 +변경할 수 있고, 또한 아이콘과 기타 정보도 변경할 수 있습니다. ### OS X diff --git a/docs-translations/ko-KR/tutorial/desktop-environment-integration.md b/docs-translations/ko-KR/tutorial/desktop-environment-integration.md index 91fc59a8f2e..38830b91e82 100644 --- a/docs-translations/ko-KR/tutorial/desktop-environment-integration.md +++ b/docs-translations/ko-KR/tutorial/desktop-environment-integration.md @@ -263,6 +263,33 @@ var window = new BrowserWindow({...}); window.setProgressBar(0.5); ``` +## 작업 표시줄의 아이콘 오버레이 (Windows) + +Windows에선 작업 표시줄 버튼에 어플리케이션의 상태를 표시하는 작은 오버레이를 사용할 +수 있습니다. MSDN에서 인용하자면 (영문): + +> Icon overlays serve as a contextual notification of status, and are intended +> to negate the need for a separate notification area status icon to communicate +> that information to the user. For instance, the new mail status in Microsoft +> Outlook, currently shown in the notification area, can now be indicated +> through an overlay on the taskbar button. Again, you must decide during your +> development cycle which method is best for your application. Overlay icons are +> intended to supply important, long-standing status or notifications such as +> network status, messenger status, or new mail. The user should not be +> presented with constantly changing overlays or animations. + +__작업 표시줄 버튼 위의 오버레이:__ + +![작업 표시줄 버튼 위의 오버레이](https://i-msdn.sec.s-msft.com/dynimg/IC420441.png) + +윈도우에 오버레이 아이콘을 설정하려면 [BrowserWindow.setOverlayIcon][setoverlayicon] +API를 사용할 수 있습니다: + +```javascript +var window = new BrowserWindow({...}); +window.setOverlayIcon('path/to/overlay.png', 'Description for overlay'); +``` + ## 대표 파일 제시 (OS X) OS X는 창에서 대표 파일을 설정할 수 있습니다. 타이틀바에서 파일 아이콘이 있고, 사용자가 @@ -283,13 +310,16 @@ window.setRepresentedFilename('/etc/passwd'); window.setDocumentEdited(true); ``` -[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath -[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments -[setusertaskstasks]: ../api/app.md#appsetusertaskstasks -[setprogressbar]: ../api/browser-window.md#browserwindowsetprogressbarprogress -[setrepresentedfilename]: ../api/browser-window.md#browserwindowsetrepresentedfilenamefilename -[setdocumentedited]: ../api/browser-window.md#browserwindowsetdocumenteditededited +[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows +[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows +[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows +[setprogressbar]: ../api/browser-window.md#winsetprogressbarprogress +[setoverlayicon]: ../api/browser-window.md#winsetoverlayiconoverlay-description-windows-7 +[setrepresentedfilename]: ../api/browser-window.md#winsetrepresentedfilenamefilename-os-x +[setdocumentedited]: ../api/browser-window.md#winsetdocumenteditededited-os-x [app-registration]: http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx [unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher -[setthumbarbuttons]: ../api/browser-window.md#browserwindowsetthumbarbuttonsbuttons -[trayballoon]: ../api/tray.md#traydisplayballoonoptions-windows +[setthumbarbuttons]: ../api/browser-window.md#winsetthumbarbuttonsbuttons-windows-7 +[tray-balloon]: ../api/tray.md#traydisplayballoonoptions-windows +[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx +[notification-spec]: https://developer.gnome.org/notification-spec/ diff --git a/docs-translations/ko-KR/tutorial/quick-start.md b/docs-translations/ko-KR/tutorial/quick-start.md index 708a48080ff..82d359e0de5 100644 --- a/docs-translations/ko-KR/tutorial/quick-start.md +++ b/docs-translations/ko-KR/tutorial/quick-start.md @@ -12,7 +12,7 @@ Electron은 웹 페이지의 GUI를 사용합니다. 쉽게 말해 Electron은 ### 메인 프로세스 -Electron은 실행될 때 __메인 프로세스__로 불리는 `package.json`의 `main` 스크립트를 +Electron은 실행될 때 __메인 프로세스__ 로 불리는 `package.json`의 `main` 스크립트를 호출합니다. 이 스크립트는 메인 프로세스에서 작동합니다. GUI 컴포넌트를 조작하거나 웹 페이지 창을 생성할 수 있습니다. @@ -43,6 +43,7 @@ API를 사용하여 low-level 수준으로 운영체제와 상호작용할 수 Electron에는 메인 프로세스와 랜더러 프로세스 사이에 통신을 할 수 있도록 [ipc](../api/ipc-renderer.md) 모듈을 제공하고 있습니다. 또는 [remote](../api/remote.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다. +또한 FAQ에서 [다양한 객체를 공유하는 방법](share-data)도 소개하고 있습니다. ## 첫번째 Electron 앱 만들기 @@ -211,3 +212,5 @@ $ cd electron-quick-start # 어플리케이션의 종속성 모듈을 설치한 후 실행합니다 $ npm install && npm start ``` + +[share-data]: ../faq/electron-faq.md#어떻게-웹-페이지-간에-데이터를-공유할-수-있나요 diff --git a/docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md b/docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md index d0684e0cfe1..6cc5472bb12 100644 --- a/docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md +++ b/docs-translations/ko-KR/tutorial/using-selenium-and-webdriver.md @@ -48,7 +48,7 @@ var driver = new webdriver.Builder() .withCapabilities({ chromeOptions: { // 여기에 사용중인 Electron 바이너리의 경로를 지정하세요. - binary: '/Path-to-Your-App.app/Contents/MacOS/Atom', + binary: '/Path-to-Your-App.app/Contents/MacOS/Electron', } }) .forBrowser('electron') diff --git a/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md b/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md index d729c0efc08..b9fec886769 100644 --- a/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md +++ b/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md @@ -8,6 +8,10 @@ Electron은 라이센스상의 문제로 Widevine CDM 플러그인을 직접 제 따라서 플러그인을 얻으려면 먼저 사용할 Electron 빌드의 아키텍쳐와 버전에 맞춰 공식 Chrome 브라우저를 설치해야 합니다. +__참고:__ Chrome 브라우저의 메이저 버전은 Electron에서 사용하는 Chrome 버전과 +같습니다, 만약 그렇지 않다면 `navigator.plugins`가 로드됐더라도 정상적으로 작동하지 +않습니다. + ### Windows & OS X Chrome 브라우저에서 `chrome://components/`를 열고 `WidevineCdm`을 찾은 후 확실히 diff --git a/docs-translations/pt-BR/api/window-open.md b/docs-translations/pt-BR/api/window-open.md new file mode 100644 index 00000000000..eb2b31cd0e4 --- /dev/null +++ b/docs-translations/pt-BR/api/window-open.md @@ -0,0 +1,67 @@ +# The `window.open` function + +Qunado `window.open` é chamado para criar uma nova janela de uma pagina web uma nova instância de `BrowserWindow` será criado para a `url` e um proxy será devolvido para o `windows.open`, para permitir que a página tenha limitado controle sobre ele. + +O proxy tem funcionalidade limitada padrão implementada para ser compatível com as páginas web tradicionais. +Para controle total da nova janela você deveria criar um `BrowserWindow` diretamente + + +The newly created `BrowserWindow` will inherit parent window's options by +default, to override inherited options you can set them in the `features` +string. + +O recém-criado `BrowserWindow` herdará as opções da janela pai por padrão, para substituir as opções herdadas você pode definilos no `features`(string). +### `window.open(url[, frameName][, features])` + +* `url` String +* `frameName` String (opcional) +* `features` String (opcional) + +Cria uma nova janela e retorna uma instância da classe `BrowserWindowProxy'. + +A string `features` segue o formato padrão do browser, mas cada recurso (feature) tem que ser um campo de opções do `BrowserWindow`. + +### `window.opener.postMessage(message, targetOrigin)` + +* `message` String +* `targetOrigin` String + +Envia uma mensagem para a janela pai com a origem especificada ou `*` preferência de origem não especificada. +Sends a message to the parent window with the specified origin or `*` +origin preference. + +## Class: BrowserWindowProxy + +O objeto `BrowserWindowProxy` é retornado de `window.open` e fornece uma funcionalidade limitada para a janela filha. + +### `BrowserWindowProxy.blur()` + +Remove o foco da janela filha. + +### `BrowserWindowProxy.close()` + +Forçadamente fecha a janela filha sem chamar o evento de descarregamento. + +### `BrowserWindowProxy.closed` + +Define como true após a janela filha ficar fechada. + +### `BrowserWindowProxy.eval(code)` + +* `code` String + +Avalia o código na jánela filha. + +### `BrowserWindowProxy.focus()` + +Concentra-se a janela filha (traz a janela para frente) +### `BrowserWindowProxy.postMessage(message, targetOrigin)` + +* `message` String +* `targetOrigin` String + +Sends a message to the child window with the specified origin or `*` for no +origin preference. + +In addition to these methods, the child window implements `window.opener` object +with no properties and a single method. diff --git a/docs-translations/ru-RU/README.md b/docs-translations/ru-RU/README.md index 07e50028df9..4e7ed443468 100644 --- a/docs-translations/ru-RU/README.md +++ b/docs-translations/ru-RU/README.md @@ -1,6 +1,6 @@ Пожалуйста, убедитесь, что вы используете документацию, которые соответствует вашей версии Electron. Номер версии должен быть частью адреса страницы. Если это не так, вы -возможно,используете документицию ветки разработки, которая может содержать изменения api, +возможно, используете документацию ветки разработки, которая может содержать изменения api, которые не совместимы с вашей версией Electron. Если это так, Вы можете переключиться на другую версию документации в списке [доступные версии](http://electron.atom.io/docs/) на atom.io, или diff --git a/docs-translations/zh-CN/README.md b/docs-translations/zh-CN/README.md index 5c4303a0a6f..d7a8442ad4a 100644 --- a/docs-translations/zh-CN/README.md +++ b/docs-translations/zh-CN/README.md @@ -1,13 +1,19 @@ +## 常见问题 + ++ [Electron 常见问题](faq/electron-faq.md) + ## 向导 * [支持平台](tutorial/supported-platforms.md) -* [应用部署](tutorial/application-distribution.md) -* [应用打包](tutorial/application-packaging.md) -* [使用原生模块](tutorial/using-native-node-modules.md) +* [分发应用](tutorial/application-distribution.md) +* [提交应用到 Mac App Store](tutorial/mac-app-store-submission-guide.md) +* [打包应用](tutorial/application-packaging.md) +* [使用 Node 原生模块](tutorial/using-native-node-modules.md) * [主进程调试](tutorial/debugging-main-process.md) * [使用 Selenium 和 WebDriver](tutorial/using-selenium-and-webdriver.md) -* [调试工具扩展](tutorial/devtools-extension.md) -* [使用 PepperFlash 插件](tutorial/using-pepper-flash-plugin.md) +* [使用开发人员工具扩展](tutorial/devtools-extension.md) +* [使用 Pepper Flash 插件](tutorial/using-pepper-flash-plugin.md) +* [使用 Widevine CDM 插件](tutorial/using-widevine-cdm-plugin.md) ## 教程 @@ -19,53 +25,55 @@ * [简介](api/synopsis.md) * [进程对象](api/process.md) -* [支持的Chrome命令行开关](api/chrome-command-line-switches.md) +* [支持的 Chrome 命令行开关](api/chrome-command-line-switches.md) +* [环境变量](api/environment-variables.md) -定制的DOM元素: +自定义的 DOM 元素: -* [`File`对象](api/file-object.md) -* [``标签](api/web-view-tag.md) -* [`window.open`函数](api/window-open.md) +* [`File` 对象](api/file-object.md) +* [`` 标签](api/web-view-tag.md) +* [`window.open` 函数](api/window-open.md) -主进程可用的模块: +在主进程内可用的模块: * [app](api/app.md) -* [auto-updater](api/auto-updater.md) -* [browser-window](api/browser-window.md) -* [content-tracing](api/content-tracing.md) +* [autoUpdater](api/auto-updater.md) +* [BrowserWindow](api/browser-window.md) +* [contentTracing](api/content-tracing.md) * [dialog](api/dialog.md) -* [global-shortcut](api/global-shortcut.md) -* [ipc (main process)](api/ipc-main-process.md) -* [menu](api/menu.md) -* [menu-item](api/menu-item.md) -* [power-monitor](api/power-monitor.md) -* [power-save-blocker](api/power-save-blocker.md) +* [globalShortcut](api/global-shortcut.md) +* [ipcMain](api/ipc-main.md) +* [Menu](api/menu.md) +* [MenuItem](api/menu-item.md) +* [powerMonitor](api/power-monitor.md) +* [powerSaveBlocker](api/power-save-blocker.md) * [protocol](api/protocol.md) * [session](api/session.md) * [webContents](api/web-contents.md) -* [tray](api/tray.md) +* [Tray](api/tray.md) -渲染进程(网页)可用的模块: +在渲染进程(网页)内可用的模块: -* [ipc (renderer)](api/ipc-renderer.md) +* [desktopCapturer](api/desktop-capturer.md) +* [ipcRenderer](api/ipc-renderer.md) * [remote](api/remote.md) -* [web-frame](api/web-frame.md) +* [webFrame](api/web-frame.md) -两种进程都可用的模块: +在两种进程中都可用的模块: * [clipboard](api/clipboard.md) -* [crash-reporter](api/crash-reporter.md) -* [native-image](api/native-image.md) +* [crashReporter](api/crash-reporter.md) +* [nativeImage](api/native-image.md) * [screen](api/screen.md) * [shell](api/shell.md) ## 开发 -* [编码规范](development/coding-style.md) +* [代码规范](development/coding-style.md) * [源码目录结构](development/source-code-directory-structure.md) -* [与 NW.js (原名 node-webkit) 在技术上的差异](development/atom-shell-vs-node-webkit.md) -* [构建系统概况](development/build-system-overview.md) -* [构建步骤 (Mac)](development/build-instructions-mac.md) -* [构建步骤 (Windows)](development/build-instructions-windows.md) -* [构建步骤 (Linux)](development/build-instructions-linux.md) -* [在调试中使用 SymbolServer](development/setting-up-symbol-server.md) +* [与 NW.js(原 node-webkit)在技术上的差异](development/atom-shell-vs-node-webkit.md) +* [构建系统概览](development/build-system-overview.md) +* [构建步骤(Mac)](development/build-instructions-mac.md) +* [构建步骤(Windows)](development/build-instructions-windows.md) +* [构建步骤(Linux)](development/build-instructions-linux.md) +* [在调试中使用 Symbol Server](development/setting-up-symbol-server.md) diff --git a/docs-translations/zh-CN/api/accelerator.md b/docs-translations/zh-CN/api/accelerator.md index 8858d18e856..06e7397c849 100644 --- a/docs-translations/zh-CN/api/accelerator.md +++ b/docs-translations/zh-CN/api/accelerator.md @@ -1,46 +1,43 @@ # Accelerator -An accelerator is a string that represents a keyboard shortcut. It can contain -multiple modifiers and key codes, combined by the `+` character. +一个 `Accelerator` 是一个表示某个快捷键组合的字符串。它包含了用 `+` 连接的若干个按键。 -Examples: +例如: * `Command+A` * `Ctrl+Shift+Z` -## Platform notice +## 运行平台相关的提示 -On Linux and Windows, the `Command` key does not have any effect so -use `CommandOrControl` which represents `Command` on OS X and `Control` on -Linux and Windows to define some accelerators. +在 Linux 和 Windows 上,`Command` 键并不存在,因此我们通常用 `CommandOrControl` 来表示“在 OS X 下为 `Command` 键,但在 +Linux 和 Windows 下为 `Control` 键。 -The `Super` key is mapped to the `Windows` key on Windows and Linux and -`Cmd` on OS X. +`Super` 键是指 Linux 和 Windows 上的 `Windows` 键,但是在 OS X 下为 `Command` 键。 -## Available modifiers +## 可用的功能按键 -* `Command` (or `Cmd` for short) -* `Control` (or `Ctrl` for short) -* `CommandOrControl` (or `CmdOrCtrl` for short) +* `Command`(缩写为 `Cmd`) +* `Control`(缩写为 `Ctrl`) +* `CommandOrControl`(缩写为 `CmdOrCtrl`) * `Alt` * `Shift` * `Super` -## Available key codes +## 可用的普通按键 -* `0` to `9` -* `A` to `Z` -* `F1` to `F24` -* Punctuations like `~`, `!`, `@`, `#`, `$`, etc. +* `0` 到 `9` +* `A` 到 `Z` +* `F1` 到 `F24` +* 类似与 `~`、`!`、`@`、`#`、`$` 的标点符号。 * `Plus` * `Space` * `Backspace` * `Delete` * `Insert` -* `Return` (or `Enter` as alias) -* `Up`, `Down`, `Left` and `Right` -* `Home` and `End` -* `PageUp` and `PageDown` -* `Escape` (or `Esc` for short) -* `VolumeUp`, `VolumeDown` and `VolumeMute` -* `MediaNextTrack`, `MediaPreviousTrack`, `MediaStop` and `MediaPlayPause` +* `Return`(和 `Enter` 等同) +* `Up`、`Down`、`Left` 和 `Right` +* `Home` 和 `End` +* `PageUp` 和 `PageDown` +* `Escape`(缩写为 `Esc`) +* `VolumeUp`、`VolumeDown` 和 `VolumeMute` +* `MediaNextTrack`、`MediaPreviousTrack`、`MediaStop` 和 `MediaPlayPause` diff --git a/docs-translations/zh-CN/api/app.md b/docs-translations/zh-CN/api/app.md index 448a5a373e9..dd8d6bcfdf6 100644 --- a/docs-translations/zh-CN/api/app.md +++ b/docs-translations/zh-CN/api/app.md @@ -11,11 +11,11 @@ app.on('window-all-closed', function() { }); ``` -## 事件 +## 事件列表 `app` 对象会触发以下的事件: -### 事件: 'will-finish-launching' +### 事件:'will-finish-launching' 当应用程序完成基础的启动的时候被触发。在 Windows 和 Linux 中, `will-finish-launching` 事件与 `ready` 事件是相同的; 在 OS X 中, @@ -24,11 +24,11 @@ app.on('window-all-closed', function() { 在大多数的情况下,你应该只在 `ready` 事件处理器中完成所有的业务。 -### 事件: 'ready' +### 事件:'ready' 当 Electron 完成初始化时被触发。 -### 事件: 'window-all-closed' +### 事件:'window-all-closed' 当所有的窗口都被关闭时触发。 @@ -36,20 +36,20 @@ app.on('window-all-closed', function() { 或者开发者调用了 `app.quit()` ,Electron 将会先尝试关闭所有的窗口再触发 `will-quit` 事件, 在这种情况下 `window-all-closed` 不会被触发。 -### 事件: 'before-quit' +### 事件:'before-quit' 返回: -* `event` 事件 +* `event` Event 在应用程序开始关闭它的窗口的时候被触发。 调用 `event.preventDefault()` 将会阻止终止应用程序的默认行为。 -### 事件: 'will-quit' +### 事件:'will-quit' 返回: -* `event` 事件 +* `event` Event 当所有的窗口已经被关闭,应用即将退出时被触发。 调用 `event.preventDefault()` 将会阻止终止应用程序的默认行为。 @@ -57,20 +57,20 @@ app.on('window-all-closed', function() { 你可以在 `window-all-closed` 事件的描述中看到 `will-quit` 事件 和 `window-all-closed` 事件的区别。 -### 事件: 'quit' +### 事件:'quit' 返回: -* `event` 事件 -* `exitCode` 整数 +* `event` Event +* `exitCode` Integer 当应用程序正在退出时触发。 -### 事件: 'open-file' _OS X_ +### 事件:'open-file' _OS X_ 返回: -* `event` 事件 -* `path` 字符串 +* `event` Event +* `path` String 当用户想要在应用中打开一个文件时触发。`open-file` 事件常常在应用已经打开并且系统想要再次使用应用打开文件时被触发。 `open-file` 也会在一个文件被拖入 dock 且应用还没有运行的时候被触发。 @@ -78,73 +78,74 @@ app.on('window-all-closed', function() { 如果你想处理这个事件,你应该调用 `event.preventDefault()` 。 在 Windows系统中, 你需要通过解析 process.argv 来获取文件路径。 -### 事件: 'open-url' _OS X_ + +### 事件:'open-url' _OS X_ 返回: -* `event` 事件 -* `url` 字符串 +* `event` Event +* `url` String 当用户想要在应用中打开一个url的时候被触发。URL格式必须要提前标识才能被你的应用打开。 如果你想处理这个事件,你应该调用 `event.preventDefault()` 。 -### 事件: 'activate' _OS X_ +### 事件:'activate' _OS X_ 返回: -* `event` 事件 -* `hasVisibleWindows` 布尔值 +* `event` Event +* `hasVisibleWindows` Boolean 当应用被激活时触发,常用于点击应用的 dock 图标的时候。 -### 事件: 'browser-window-blur' +### 事件:'browser-window-blur' 返回: -* `event` 事件 -* `window` 浏览器窗口 +* `event` Event +* `window` BrowserWindow -当一个 [浏览器窗口](browser-window.md) 失去焦点的时候触发。 +当一个 [BrowserWindow](browser-window.md) 失去焦点的时候触发。 -### 事件: 'browser-window-focus' +### 事件:'browser-window-focus' 返回: -* `event` 事件 -* `window` 浏览器窗口 +* `event` Event +* `window` BrowserWindow -当一个 [浏览器窗口](browser-window.md) 获得焦点的时候触发。 +当一个 [BrowserWindow](browser-window.md) 获得焦点的时候触发。 -### 事件: 'browser-window-created' +### 事件:'browser-window-created' 返回: -* `event` 事件 -* `window` 浏览器窗口 +* `event` Event +* `window` BrowserWindow -当一个 [浏览器窗口](browser-window.md) 被创建的时候触发。 +当一个 [BrowserWindow](browser-window.md) 被创建的时候触发。 -### 事件: 'certificate-error' +### 事件:'certificate-error' 返回: -* `event` 事件 -* `webContents` [web组件](web-contents.md) -* `url` 字符串 -* `certificateList` 对象 - * `data` PEM 编码数据 - * `issuerName` 发行者的公有名称 -* `callback` 函数 +* `event` Event +* `webContents` [WebContents](web-contents.md) +* `url` String - URL 地址 +* `error` String - 错误码 +* `certificate` Object + * `data` Buffer - PEM 编码数据 + * `issuerName` String - 发行者的公有名称 +* `callback` Function -Emitted when failed to verify the `certificate` for `url`, to trust the -certificate you should prevent the default behavior with -`event.preventDefault()` and call `callback(true)`. +当对 `url` 验证 `certificate` 证书失败的时候触发,如果需要信任这个证书,你需要阻止默认行为 `event.preventDefault()` 并且 +调用 `callback(true)`。 ```javascript session.on('certificate-error', function(event, webContents, url, error, certificate, callback) { if (url == "https://github.com") { - // Verification logic. + // 验证逻辑。 event.preventDefault(); callback(true); } else { @@ -153,37 +154,37 @@ session.on('certificate-error', function(event, webContents, url, error, certifi }); ``` -### 事件: 'select-client-certificate' +### 事件:'select-client-certificate' 返回: -* `event` 事件 -* `webContents` [web组件](web-contents.md) -* `url` 字符串 -* `certificateList` 对象 - * `data` PEM 编码数据 - * `issuerName` 发行者的公有名称 -* `callback` 函数 +* `event` Event +* `webContents` [WebContents](web-contents.md) +* `url` String - URL 地址 +* `certificateList` [Object] + * `data` Buffer - PEM 编码数据 + * `issuerName` String - 发行者的公有名称 +* `callback` Function 当一个客户端认证被请求的时候被触发。 -The `url` corresponds to the navigation entry requesting the client certificate -and `callback` needs to be called with an entry filtered from the list. -Using `event.preventDefault()` prevents the application from using the first -certificate from the store. +`url` 指的是请求客户端认证的网页地址,调用 `callback` 时需要传入一个证书列表中的证书。 + +需要通过调用 `event.preventDefault()` 来防止应用自动使用第一个证书进行验证。如下所示: + ```javascript app.on('select-certificate', function(event, host, url, list, callback) { event.preventDefault(); callback(list[0]); }) ``` -### Event: 'login' +### 事件: 'login' -Returns: +返回: * `event` Event -* `webContents` [Web组件](web-contents.md) +* `webContents` [WebContents](web-contents.md) * `request` Object * `method` String * `url` URL @@ -196,11 +197,10 @@ Returns: * `realm` String * `callback` Function -当 `webContents` 要做验证时被触发。 +当 `webContents` 要做进行一次 HTTP 登陆验证时被触发。 -The default behavior is to cancel all authentications, to override this you -should prevent the default behavior with `event.preventDefault()` and call -`callback(username, password)` with the credentials. +默认情况下,Electron 会取消所有的验证行为,如果需要重写这个行为,你需要用 `event.preventDefault()` 来阻止默认行为,并且 +用 `callback(username, password)` 来进行验证。 ```javascript app.on('login', function(event, webContents, request, authInfo, callback) { @@ -208,22 +208,22 @@ app.on('login', function(event, webContents, request, authInfo, callback) { callback('username', 'secret'); }) ``` -### 事件: 'gpu-process-crashed' +### 事件:'gpu-process-crashed' -当GPU进程崩溃时触发。 +当 GPU 进程崩溃时触发。 -## 方法 +## 方法列表 `app` 对象拥有以下的方法: -**提示:** 有的方法只能用于特定的操作系统。 +**请注意** 有的方法只能用于特定的操作系统。 ### `app.quit()` -试图关掉所有的窗口。`before-quit` 事件将会被最先触发。如果所有的窗口都被成功关闭了, +试图关掉所有的窗口。`before-quit` 事件将会最先被触发。如果所有的窗口都被成功关闭了, `will-quit` 事件将会被触发,默认下应用将会被关闭。 -这个方法保证了所有的 `beforeunload` 和 `unload` 事件处理器被正确执行。会存在一个窗口被 `beforeunload` 事件处理器返回 `false` 取消退出的可能性。 +这个方法保证了所有的 `beforeunload` 和 `unload` 事件处理器被正确执行。假如一个窗口的 `beforeunload` 事件处理器返回 `false`,那么整个应用可能会取消退出。 ### `app.hide()` _OS X_ @@ -241,30 +241,29 @@ app.on('login', function(event, webContents, request, authInfo, callback) { 所有的窗口会被立刻关闭,不会询问用户。`before-quit` 和 `will-quit` 这2个事件不会被触发 - ### `app.getAppPath()` 返回当前应用所在的文件路径。 ### `app.getPath(name)` -* `name` 字符串 +* `name` String 返回一个与 `name` 参数相关的特殊文件夹或文件路径。当失败时抛出一个 `Error` 。 你可以通过名称请求以下的路径: -* `home` 用户的 home 文件夹。 -* `appData` 所有用户的应用数据文件夹,默认对应: +* `home` 用户的 home 文件夹(主目录) +* `appData` 当前用户的应用数据文件夹,默认对应: * `%APPDATA%` Windows 中 * `$XDG_CONFIG_HOME` or `~/.config` Linux 中 * `~/Library/Application Support` OS X 中 -* `userData` 储存你应用程序设置文件的文件夹,默认是 `appData` 文件夹附加应用的名称。 -* `temp` 临时文件夹。 +* `userData` 储存你应用程序设置文件的文件夹,默认是 `appData` 文件夹附加应用的名称 +* `temp` 临时文件夹 * `exe` 当前的可执行文件 -* `module` `libchromiumcontent` 库. -* `desktop` 当前用户的桌面文件夹。 -* `documents` "我的文件夹"的路径. +* `module` `libchromiumcontent` 库 +* `desktop` 当前用户的桌面文件夹 +* `documents` 用户文档目录的路径 * `downloads` 用户下载目录的路径. * `music` 用户音乐目录的路径. * `pictures` 用户图片目录的路径. @@ -272,14 +271,14 @@ app.on('login', function(event, webContents, request, authInfo, callback) { ### `app.setPath(name, path)` -* `name` 字符串 -* `path` 字符串 +* `name` String +* `path` String -重写 `path` 参数到一个特别的文件夹或者是一个和 `name` 参数有关系的文件。 +重写某个 `name` 的路径为 `path`,`path` 可以是一个文件夹或者一个文件,这个和 `name` 的类型有关。 如果这个路径指向的文件夹不存在,这个文件夹将会被这个方法创建。 -如果错误则抛出 `Error` 。 +如果错误则会抛出 `Error`。 -你只可以指向 `app.getPath` 中定义过 `name` 的路径。 +`name` 参数只能使用 `app.getPath` 中定义过 `name`。 默认情况下,网页的 cookie 和缓存都会储存在 `userData` 文件夹。 如果你想要改变这个位置,你需要在 `app` 模块中的 `ready` 事件被触发之前重写 `userData` 的路径。 @@ -293,22 +292,20 @@ app.on('login', function(event, webContents, request, authInfo, callback) { 返回当前应用程序的 `package.json` 文件中的名称。 -通常 `name` 字段是一个短的小写字符串,其命名规则按照 npm 中的模块命名规则。你应该单独列举一个 -`productName` 字段,用于表示你的应用程序的完整名称,这个名称将会被 Electron 优先采用。 +由于 npm 的命名规则,通常 `name` 字段是一个短的小写字符串。但是应用名的完整名称通常是首字母大写的,你应该单独使用一个 +`productName` 字段,用于表示你的应用程序的完整名称。Electron 会优先使用这个字段作为应用名。 ### `app.getLocale()` -返回当前应用程序的语言种类。 - - +返回当前应用程序的语言。 ### `app.addRecentDocument(path)` _OS X_ _Windows_ -* `path` 字符串 +* `path` String -为最近访问的文档列表中添加 `path` 。 +在最近访问的文档列表中添加 `path`。 -这个列表由操作系统进行管理。在 Windows 中您可以通过任务条进行访问,在 OS X 中你可以通过dock 菜单进行访问。 +这个列表由操作系统进行管理。在 Windows 中您可以通过任务条进行访问,在 OS X 中你可以通过 dock 菜单进行访问。 ### `app.clearRecentDocuments()` _OS X_ _Windows_ @@ -316,66 +313,55 @@ app.on('login', function(event, webContents, request, authInfo, callback) { ### `app.setUserTasks(tasks)` _Windows_ -* `tasks` 由 `Task` 对象构成的数组 +* `tasks` [Task] - 一个由 Task 对象构成的数组 将 `tasks` 添加到 Windows 中 JumpList 功能的 [Tasks][tasks] 分类中。 `tasks` 中的 `Task` 对象格式如下: -`Task` 对象 -* `program` 字符串 - 执行程序的路径,通常你应该说明当前程序的路径为 `process.execPath` 字段。 -* `arguments` 字符串 - 当 `program` 执行时的命令行参数。 -* `title` 字符串 - JumpList 中显示的标题。 -* `description` 字符串 - 对这个任务的描述。 -* `iconPath` 字符串 - JumpList 中显示的 icon 的绝对路径,可以是一个任意包含一个icon的资源文件。你通常可以通过指明 `process.execPath` 来显示程序中的icon。 -* `iconIndex` 整数 - icon文件中的icon目录。如果一个icon文件包括了两个或多个icon,就需要设置这个值以确定icon。如果一个文件仅包含一个icon,那么这个值为0。 +`Task` Object +* `program` String - 执行程序的路径,通常你应该说明当前程序的路径为 `process.execPath` 字段。 +* `arguments` String - 当 `program` 执行时的命令行参数。 +* `title` String - JumpList 中显示的标题。 +* `description` String - 对这个任务的描述。 +* `iconPath` String - JumpList 中显示的图标的绝对路径,可以是一个任意包含一个图标的资源文件。通常来说,你可以通过指明 `process.execPath` 来显示程序中的图标。 +* `iconIndex` Integer - 图标文件中的采用的图标位置。如果一个图标文件包括了多个图标,就需要设置这个值以确定使用的是哪一个图标。 +如果这个图标文件中只包含一个图标,那么这个值为 0。 ### `app.allowNTLMCredentialsForAllDomains(allow)` * `allow` Boolean -Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate -authentication - normally, Electron will only send NTLM/Kerberos credentials for -URLs that fall under "Local Intranet" sites (i.e. are in the same domain as you). -However, this detection often fails when corporate networks are badly configured, -so this lets you co-opt this behavior and enable it for all URLs. +动态设置是否总是为 HTTP NTLM 或 Negotiate 认证发送证书。通常来说,Electron 只会对本地网络(比如和你处在一个域中的计算机)发 +送 NTLM / Kerberos 证书。但是假如网络设置得不太好,可能这个自动探测会失效,所以你可以通过这个接口自定义 Electron 对所有 URL +的行为。 + ### `app.makeSingleInstance(callback)` * `callback` Function -This method makes your application a Single Instance Application - instead of -allowing multiple instances of your app to run, this will ensure that only a -single instance of your app is running, and other instances signal this -instance and exit. +这个方法可以让你的应用在同一时刻最多只会有一个实例,否则你的应用可以被运行多次并产生多个实例。你可以利用这个接口保证只有一个实例正 +常运行,其余的实例全部会被终止并退出。 -`callback` will be called with `callback(argv, workingDirectory)` when a second -instance has been executed. `argv` is an Array of the second instance's command -line arguments, and `workingDirectory` is its current working directory. Usually -applications respond to this by making their primary window focused and -non-minimized. +如果多个实例同时运行,那么第一个被运行的实例中 `callback` 会以 `callback(argv, workingDirectory)` 的形式被调用。其余的实例 +会被终止。 +`argv` 是一个包含了这个实例的命令行参数列表的数组,`workingDirectory` 是这个实例目前的运行目录。通常来说,我们会用通过将应用在 +主屏幕上激活,并且取消最小化,来提醒用户这个应用已经被打开了。 -The `callback` is guaranteed to be executed after the `ready` event of `app` -gets emitted. +在 `app` 的 `ready` 事件后,`callback` 才有可能被调用。 -This method returns `false` if your process is the primary instance of the -application and your app should continue loading. And returns `true` if your -process has sent its parameters to another instance, and you should immediately -quit. +如果当前实例为第一个实例,那么在这个方法将会返回 `false` 来保证它继续运行。否则将会返回 `true` 来让它立刻退出。 -On OS X the system enforces single instance automatically when users try to open -a second instance of your app in Finder, and the `open-file` and `open-url` -events will be emitted for that. However when users start your app in command -line the system's single instance machanism will be bypassed and you have to -use this method to ensure single instance. +在 OS X 中,如果用户通过 Finder、`open-file` 或者 `open-url` 打开应用,系统会强制确保只有一个实例在运行。但是如果用户是通过 +命令行打开,这个系统机制会被忽略,所以你仍然需要靠这个方法来保证应用为单实例运行的。 -An example of activating the window of primary instance when a second instance -starts: +下面是一个简单的例子。我们可以通过这个例子了解如何确保应用为单实例运行状态。 ```js var myWindow = null; var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { - // Someone tried to run a second instance, we should focus our window. + // 当另一个实例运行的时候,这里将会被调用,我们需要激活应用的窗口 if (myWindow) { if (myWindow.isMinimized()) myWindow.restore(); myWindow.focus(); @@ -383,104 +369,109 @@ var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) return true; }); +// 这个实例是多余的实例,需要退出 if (shouldQuit) { app.quit(); return; } -// Create myWindow, load the rest of the app, etc... +// 创建窗口、继续加载应用、应用逻辑等…… app.on('ready', function() { }); + ``` ### `app.setAppUserModelId(id)` _Windows_ * `id` String -改变 [Application User Model ID][app-user-model-id] 的 `id`. +改变当前应用的 [Application User Model ID][app-user-model-id] 为 `id`. ### `app.isAeroGlassEnabled()` _Windows_ -This method returns `true` if [DWM composition](https://msdn.microsoft.com/en-us/library/windows/desktop/aa969540.aspx) -(Aero Glass) is enabled, and `false` otherwise. You can use it to determine if -you should create a transparent window or not (transparent windows won't work -correctly when DWM composition is disabled). +如果 [DWM composition](https://msdn.microsoft.com/en-us/library/windows/desktop/aa969540.aspx)(Aero Glass) 启用 +了,那么这个方法会返回 `true`,否则是 `false`。你可以用这个方法来决定是否要开启透明窗口特效,因为如果用户没开启 DWM,那么透明窗 +口特效是无效的。 -Usage example: +举个例子: ```js let browserOptions = {width: 1000, height: 800}; -// Make the window transparent only if the platform supports it. +// 只有平台支持的时候才使用透明窗口 if (process.platform !== 'win32' || app.isAeroGlassEnabled()) { browserOptions.transparent = true; browserOptions.frame = false; } -// Create the window. +// 创建窗口 win = new BrowserWindow(browserOptions); -// Navigate. +// 转到某个网页 if (browserOptions.transparent) { win.loadURL('file://' + __dirname + '/index.html'); } else { - // No transparency, so we load a fallback that uses basic styles. + // 没有透明特效,我们应该用某个只包含基本样式的替代解决方案。 win.loadURL('file://' + __dirname + '/fallback.html'); } ``` ### `app.commandLine.appendSwitch(switch[, value])` -通过可选的参数 `value` 给 Chromium 命令行中添加一个开关。 -Append a switch (with optional `value`) to Chromium's command line. +通过可选的参数 `value` 给 Chromium 中添加一个命令行开关。 -**贴士:** 这不会影响 `process.argv` ,这个方法主要被开发者用于控制一些低层级的 Chromium 行为。 +**注意** 这个方法不会影响 `process.argv`,我们通常用这个方法控制一些底层 Chromium 行为。 ### `app.commandLine.appendArgument(value)` -给 Chromium 命令行中加入一个参数。这个参数是当前正在被引用的。 +给 Chromium 中直接添加一个命令行参数,这个参数 `value` 的引号和格式必须正确。 -**贴士:** 这不会影响 `process.argv`。 +**注意** 这个方法不会影响 `process.argv`。 ### `app.dock.bounce([type])` _OS X_ -* `type` 字符串 (可选的) - 可以是 `critical` 或 `informational`。默认下是 `informational` +* `type` String - 可选参数,可以是 `critical` 或 `informational`。默认为 `informational`。 -当输入 `critical` 时,dock 中的 icon 将会开始弹跳直到应用被激活或者这个请求被取消。 +当传入的是 `critical` 时,dock 中的应用将会开始弹跳,直到这个应用被激活或者这个请求被取消。 -当输入 `informational` 时,dock 中的 icon 只会弹跳一秒钟。 -然而,这个请求仍然会激活,直到应用被激活或者请求被取消。 +当传入的是 `informational` 时,dock 中的图标只会弹跳一秒钟。但是,这个请求仍然会激活,直到应用被激活或者请求被取消。 -返回一个表示这个请求的 ID。 +这个方法返回的返回值表示这个请求的 ID。 ### `app.dock.cancelBounce(id)` _OS X_ -* `id` 整数 +* `id` Integer 取消这个 `id` 对应的请求。 ### `app.dock.setBadge(text)` _OS X_ -* `text` 字符串 +* `text` String -设置 dock 中显示的字符。 +设置应用在 dock 中显示的字符串。 ### `app.dock.getBadge()` _OS X_ -返回 dock 中显示的字符。 +返回应用在 dock 中显示的字符串。 ### `app.dock.hide()` _OS X_ -隐藏 dock 中的 icon。 +隐藏应用在 dock 中的图标。 ### `app.dock.show()` _OS X_ -显示 dock 中的 icon。 +显示应用在 dock 中的图标。 ### `app.dock.setMenu(menu)` _OS X_ -* `menu` 菜单 +* `menu` [Menu](menu.md) 设置应用的 [dock 菜单][dock-menu]. +### `app.dock.setIcon(image)` _OS X_ + +* `image` [NativeImage](native-image.md) + +设置应用在 dock 中显示的图标。 + [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx diff --git a/docs-translations/zh-CN/api/auto-updater.md b/docs-translations/zh-CN/api/auto-updater.md new file mode 100644 index 00000000000..f027fbc6e50 --- /dev/null +++ b/docs-translations/zh-CN/api/auto-updater.md @@ -0,0 +1,88 @@ +# autoUpdater + +这个模块提供了一个到 `Squirrel` 自动更新框架的接口。 + +## 平台相关的提示 + +虽然 `autoUpdater` 模块提供了一套各平台通用的接口,但是在每个平台间依然会有一些微小的差异。 + +### OS X + +在 OS X 上,`autoUpdater` 模块依靠的是内置的 [Squirrel.Mac][squirrel-mac],这意味着你不需要依靠其他的设置就能使用。关于 +更新服务器的配置,你可以通过阅读 [Server Support][server-support] 这篇文章来了解。 + +### Windows + +在 Windows 上,你必须使用安装程序将你的应用装到用户的计算机上,所以比较推荐的方法是用 [grunt-electron-installer][installer] 这个模块来自动生成一个 Windows 安装向导。 + +Squirrel 自动生成的安装向导会生成一个带 [Application User Model ID][app-user-model-id] 的快捷方式。 +Application User Model ID 的格式是 `com.squirrel.PACKAGE_ID.YOUR_EXE_WITHOUT_DOT_EXE`, 比如 +像 `com.squirrel.slack.Slack` 和 `com.squirrel.code.Code` 这样的。你应该在自己的应用中使用 `app.setAppUserModelId` 方法设置相同的 API,不然 Windows 将不能正确地把你的应用固定在任务栏上。 + +服务器端的配置和 OS X 也是不一样的,你可以阅读 [Squirrel.Windows][squirrel-windows] 这个文档来获得详细信息。 + +### Linux + +Linux 下没有任何的自动更新支持,所以我们推荐用各个 Linux 发行版的包管理器来分发你的应用。 + +## 事件列表 + +`autoUpdater` 对象会触发以下的事件: + +### 事件:'error' + +返回: + +* `error` Error + +当更新发生错误的时候触发。 + +### 事件:'checking-for-update' + +当开始检查更新的时候触发。 + +### 事件:'update-available' + +当发现一个可用更新的时候触发,更新包下载会自动开始。 + +### 事件:'update-not-available' + +当没有可用更新的时候触发。 + +### 事件:'update-downloaded' + +返回: + +* `event` Event +* `releaseNotes` String - 新版本更新公告 +* `releaseName` String - 新的版本号 +* `releaseDate` Date - 新版本发布的日期 +* `updateURL` String - 更新地址 + +在更新下载完成的时候触发。 + +在 Windows 上只有 `releaseName` 是有效的。 + +## 方法列表 + +`autoUpdater` 对象有以下的方法: + +### `autoUpdater.setFeedURL(url)` + +* `url` String + +设置检查更新的 `url`,并且初始化自动更新。这个 `url` 一旦设置就无法更改。 + +### `autoUpdater.checkForUpdates()` + +向服务端查询现在是否有可用的更新。在调用这个方法之前,必须要先调用 `setFeedURL`。 + +### `autoUpdater.quitAndInstall()` + +在下载完成后,重启当前的应用并且安装更新。这个方法应该仅在 `update-downloaded` 事件触发后被调用。 + +[squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac +[server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support +[squirrel-windows]: https://github.com/Squirrel/Squirrel.Windows +[installer]: https://github.com/atom/grunt-electron-installer +[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx diff --git a/docs-translations/zh-CN/api/file-object.md b/docs-translations/zh-CN/api/file-object.md new file mode 100644 index 00000000000..ef320b36994 --- /dev/null +++ b/docs-translations/zh-CN/api/file-object.md @@ -0,0 +1,29 @@ +# `File`对象 + +为了让用户能够通过HTML5的file API直接操作本地文件,DOM的File接口提供了对本地文件的抽象。Electron在File接口中增加了一个path属性,它是文件在系统中的真实路径。 + +--- + +获取拖动到APP中文件的真实路径的例子: + +``` +
+ Drag your file here +
+ + +``` diff --git a/docs-translations/zh-CN/api/remote.md b/docs-translations/zh-CN/api/remote.md new file mode 100644 index 00000000000..a686abf2f61 --- /dev/null +++ b/docs-translations/zh-CN/api/remote.md @@ -0,0 +1,137 @@ +# remote + +`remote` 模块提供了一种在渲染进程(网页)和主进程之间进行进程间通讯(IPC)的简便途径。 + +Electron中, 与GUI相关的模块(如 `dialog`, `menu` 等)只存在于主进程,而不在渲染进程中 +。为了能从渲染进程中使用它们,需要用`ipc`模块来给主进程发送进程间消息。使用 `remote` +模块,可以调用主进程对象的方法,而无需显式地发送进程间消息,这类似于 Java 的 [RMI][rmi]。 +下面是从渲染进程创建一个浏览器窗口的例子: + +```javascript +const remote = require('electron').remote; +const BrowserWindow = remote.BrowserWindow; + +var win = new BrowserWindow({ width: 800, height: 600 }); +win.loadURL('https://github.com'); +``` + +**注意:** 反向操作(从主进程访问渲染进程),可以使用[webContents.executeJavascript](web-contents.md#webcontentsexecutejavascriptcode-usergesture). + +## 远程对象 + +`remote`模块返回的每个对象(包括函数)都代表了主进程中的一个对象(我们称之为远程对象或者远程函数)。 +当调用远程对象的方法、执行远程函数或者使用远程构造器(函数)创建新对象时,其实就是在发送同步的进程间消息。 + +在上面的例子中, `BrowserWindow` 和 `win` 都是远程对象,然而 +`new BrowserWindow` 并没有在渲染进程中创建 `BrowserWindow` 对象。 +而是在主进程中创建了 `BrowserWindow` 对象,并在渲染进程中返回了对应的远程对象,即 +`win` 对象。 + +请注意只有 [可枚举属性](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties) 才能通过 remote 进行访问. + +## 远程对象的生命周期 + +Electron 确保在渲染进程中的远程对象存在(换句话说,没有被垃圾收集),那主进程中的对应对象也不会被释放。 +当远程对象被垃圾收集之后,主进程中的对应对象才会被取消关联。 + +如果远程对象在渲染进程泄露了(即,存在某个表中但永远不会释放),那么主进程中的对应对象也一样会泄露, +所以你必须小心不要泄露了远程对象。If the remote object is leaked in the renderer process (e.g. stored in a map but +never freed), the corresponding object in the main process will also be leaked, +so you should be very careful not to leak remote objects. + +不过,主要的值类型如字符串和数字,是传递的副本。 + +## 给主进程传递回调函数 + +在主进程中的代码可以从渲染进程——`remote`模块——中接受回调函数,但是使用这个功能的时候必须非常非常小心。Code in the main process can accept callbacks from the renderer - for instance +the `remote` module - but you should be extremely careful when using this +feature. + +首先,为了避免死锁,传递给主进程的回调函数会进行异步调用。所以不能期望主进程来获得传递过去的回调函数的返回值。First, in order to avoid deadlocks, the callbacks passed to the main process +are called asynchronously. You should not expect the main process to +get the return value of the passed callbacks. + +比如,你不能主进程中给`Array.map`传递来自渲染进程的函数。 + +```javascript +// 主进程 mapNumbers.js +exports.withRendererCallback = function(mapper) { + return [1,2,3].map(mapper); +} + +exports.withLocalCallback = function() { + return exports.mapNumbers(function(x) { + return x + 1; + }); +} +``` + +```javascript +// 渲染进程 +var mapNumbers = require("remote").require("./mapNumbers"); + +var withRendererCb = mapNumbers.withRendererCallback(function(x) { + return x + 1; +}) + +var withLocalCb = mapNumbers.withLocalCallback() + +console.log(withRendererCb, withLocalCb) // [true, true, true], [2, 3, 4] +``` + +如你所见,渲染器回调函数的同步返回值没有按预期产生,与主进程中的一模一样的回调函数的返回值不同。 + +其次,传递给主进程的函数会持续到主进程对他们进行垃圾回收。 + +例如,下面的代码第一眼看上去毫无问题。给远程对象的`close`事件绑定了一个回调函数: + +```javascript +remote.getCurrentWindow().on('close', function() { + // blabla... +}); +``` + +但记住主进程会一直保持对这个回调函数的引用,除非明确的卸载它。如果不卸载,每次重新载入窗口都会再次绑定,这样每次重启就会泄露一个回调函数。 + +更严重的是,由于前面安装了回调函数的上下文已经被释放,所以当主进程的 `close` 事件触发的时候,会抛出异常。 + +为了避免这个问题,要确保对传递给主进程的渲染器的回调函数进行清理。可以清理事件处理器,或者明确告诉主进行取消来自已经退出的渲染器进程中的回调函数。 + +## 访问主进程中的内置模块 + +在主进程中的内置模块已经被添加为`remote`模块中的属性,所以可以直接像使用`electron`模块一样直接使用它们。 + +```javascript +const app = remote.app; +``` + +## 方法 + +`remote` 模块有以下方法: + +### `remote.require(module)` + +* `module` String + +返回在主进程中执行 `require(module)` 所返回的对象。 + +### `remote.getCurrentWindow()` + +返回该网页所属的 [`BrowserWindow`](browser-window.md) 对象。 + +### `remote.getCurrentWebContents()` + +返回该网页的 [`WebContents`](web-contents.md) 对象 + +### `remote.getGlobal(name)` + +* `name` String + +返回在主进程中名为 `name` 的全局变量(即 `global[name]`) 。 + +### `remote.process` + +返回主进程中的 `process` 对象。等同于 +`remote.getGlobal('process')` 但是有缓存。 + +[rmi]: http://en.wikipedia.org/wiki/Java_remote_method_invocation diff --git a/docs-translations/zh-CN/faq/electron-faq.md b/docs-translations/zh-CN/faq/electron-faq.md new file mode 100644 index 00000000000..3431c6018ab --- /dev/null +++ b/docs-translations/zh-CN/faq/electron-faq.md @@ -0,0 +1,139 @@ +# Electron 常见问题 + +## Electron 会在什么时候升级到最新版本的 Chrome? + +通常来说,在稳定版的 Chrome 发布后两周内,我们会更新 Electron 内的 Chrome 版本。 + +我们只会使用 stable 版本的 Chrome。但如果在 beta 或 dev 版本中有一个重要的更新,我们会把补丁应用到现版本的 Chrome 上。 + +## Electron 会在什么时候升级到最新版本的 Node.js? + +我们通常会在最新版的 Node.js 发布后一个月左右将 Electron 更新到这个版本的 Node.js。我们通过这种方式来避免新版本的 Node.js +带来的 bug(这种 bug 太常见了)。 + +Node.js 的新特性通常是由新版本的 V8 带来的。由于 Electron 使用的是 Chrome 浏览器中附带的 V8 引擎,所以 Electron 内往往已经 +有了部分新版本 Node.js 才有的崭新特性。 + +## 如何在两个网页间共享数据? + +在两个网页(渲染进程)间共享数据最简单的方法是使用浏览器中已经实现的 HTML5 API,比较好的方案是用 [Storage API][storage], +[`localStorage`][local-storage],[`sessionStorage`][session-storage] 或者 [IndexedDB][indexed-db]。 + +你还可以用 Electron 内的 IPC 机制实现。将数据存在主进程的某个全局变量中,然后在多个渲染进程中使用 `remote` 模块来访问它。 + +```javascript +// 在主进程中 +global.sharedObject = { + someProperty: 'default value' +}; +``` + +```javascript +// 在第一个页面中 +require('remote').getGlobal('sharedObject').someProperty = 'new value'; +``` + +```javascript +// 在第二个页面中 +console.log(require('remote').getGlobal('sharedObject').someProperty); +``` + +## 为什么应用的窗口、托盘在一段时间后不见了? + +这通常是因为用来存放窗口、托盘的变量被垃圾收集了。 + +你可以参考以下两篇文章来了解为什么会遇到这个问题。 + +* [内存管理][memory-management] +* [变量作用域][variable-scope] + +如果你只是要一个快速的修复方案,你可以用下面的方式改变变量的作用域,防止这个变量被垃圾收集。 + +从 + +```javascript +app.on('ready', function() { + var tray = new Tray('/path/to/icon.png'); +}) +``` + +改为 + +```javascript +var tray = null; +app.on('ready', function() { + tray = new Tray('/path/to/icon.png'); +}) +``` + +## 在 Electron 中,我为什么不能用 jQuery、RequireJS、Meteor、AngularJS? + +因为 Electron 在运行环境中引入了 Node.js,所以在 DOM 中有一些额外的变量,比如 `module`、`exports` 和 `require`。这导致 +了许多库不能正常运行,因为它们也需要将同名的变量加入运行环境中。 + +我们可以通过禁用 Node.js 来解决这个问题,用如下的方式: + +```javascript +// 在主进程中 +var mainWindow = new BrowserWindow({ + webPreferences: { + nodeIntegration: false + } +}); +``` + +假如你依然需要使用 Node.js 和 Electron 提供的 API,你需要在引入那些库之前将这些变量重命名,比如: + +```html + + + + +``` + +## 为什么 `require('electron').xxx` 的结果是 undefined? + +在使用 Electron 的提供的模块时,你可能会遇到和以下类似的错误: + +``` +> require('electron').webFrame.setZoomFactor(1.0); +Uncaught TypeError: Cannot read property 'setZoomLevel' of undefined +``` + +这是因为你在项目中或者在全局中安装了[npm 上获取的 `electron` 模块][electron-module],它把 Electron 的内置模块覆写了。 + +你可以通过以下方式输出 `electron` 模块的路径来确认你是否使用了正确的模块。 + +```javascript +console.log(require.resolve('electron')); +``` + +确认以下它是不是像下面这样的: + +``` +"/path/to/Electron.app/Contents/Resources/atom.asar/renderer/api/lib/exports/electron.js" +``` + +假如输出的路径类似于 `node_modules/electron/index.js`,那么你需要移除或者重命名 npm 上的 `electron` 模块。 + +```bash +npm uninstall electron +npm uninstall -g electron +``` + +如果你依然遇到了这个问题,你可能需要检查一下拼写或者是否在错误的进程中调用了这个模块。比如, +`require('electron').app` 只能在主进程中使用, 然而 `require('electron').webFrame` 只能在渲染进程中使用。 + +[memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management +[variable-scope]: https://msdn.microsoft.com/library/bzt2dkta(v=vs.94).aspx +[electron-module]: https://www.npmjs.com/package/electron +[storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage +[local-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage +[session-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage +[indexed-db]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API diff --git a/docs-translations/zh-CN/tutorial/debugging-main-process.md b/docs-translations/zh-CN/tutorial/debugging-main-process.md index 48f3579394e..f971a40892f 100644 --- a/docs-translations/zh-CN/tutorial/debugging-main-process.md +++ b/docs-translations/zh-CN/tutorial/debugging-main-process.md @@ -18,16 +18,31 @@ ## 使用 node-inspector 来调试 -__备注:__ Electron 使用 node v0.11.13 版本,目前对 node-inspector支持的不是特别好, +__备注:__ Electron 目前对 node-inspector支持的不是特别好, 如果你通过 node-inspector 的 console 来检查 `process` 对象,主进程就会崩溃。 -### 1. 开始 [node-inspector][node-inspector] 服务 +### 1. 确认你已经安装了 [node-gyp 所需工具](https://github.com/nodejs/node-gyp#installation) + +### 2. 安装 [node-inspector][node-inspector] ```bash -$ node-inspector +$ npm install node-inspector ``` -### 2. 打开 Electron 的调试模式 +### 3. 安装 `node-pre-gyp` 的一个修订版 + +```bash +$ npm install git+https://git@github.com/enlight/node-pre-gyp.git#detect-electron-runtime-in-find +``` + +### 4. 为 Electron 重新编译 `node-inspector` `v8` 模块(将 target 参数修改为你的 Electron 的版本号) + +```bash +$ node_modules/.bin/node-pre-gyp --target=0.36.2 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=0.36.2 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall +``` + +### 5. 打开 Electron 的调试模式 你也可以用调试参数来运行 Electron : @@ -41,7 +56,13 @@ $ electron --debug=5858 your/app $ electron --debug-brk=5858 your/app ``` -### 3. 加载调试器界面 +### 6. 使用 Electron 开启 [node-inspector][node-inspector] 服务 + +```bash +$ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin/inspector.js +``` + +### 7. 加载调试器界面 在 Chrome 中打开 http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 diff --git a/docs-translations/zh-CN/tutorial/quick-start.md b/docs-translations/zh-CN/tutorial/quick-start.md index 5e9c752d1d4..c4d668417e4 100644 --- a/docs-translations/zh-CN/tutorial/quick-start.md +++ b/docs-translations/zh-CN/tutorial/quick-start.md @@ -127,7 +127,7 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/ 在你完成了你的应用后,你可以按照[应用部署][4]指导发布一个版本,并且以已经打包好的形式运行应用。 - [1]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/ipc-renderer.md + [1]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/ipc-main-process.md [2]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/remote.md [3]: https://github.com/atom/electron/releases [4]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/tutorial/application-distribution.md diff --git a/docs/README.md b/docs/README.md index 9d2b36eb6c1..19ec51343d4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,6 +26,7 @@ an issue: * [DevTools Extension](tutorial/devtools-extension.md) * [Using Pepper Flash Plugin](tutorial/using-pepper-flash-plugin.md) * [Using Widevine CDM Plugin](tutorial/using-widevine-cdm-plugin.md) +* [Testing on Headless CI Systems (Travis, Jenkins)](tutorial/testing-on-headless-ci.md) ## Tutorials diff --git a/docs/api/app.md b/docs/api/app.md index adba230b0e2..bdf0a8a5587 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -156,7 +156,7 @@ certificate you should prevent the default behavior with `event.preventDefault()` and call `callback(true)`. ```javascript -session.on('certificate-error', function(event, webContents, url, error, certificate, callback) { +app.on('certificate-error', function(event, webContents, url, error, certificate, callback) { if (url == "https://github.com") { // Verification logic. event.preventDefault(); @@ -246,7 +246,7 @@ returning `false` in the `beforeunload` event handler. ### `app.hide()` _OS X_ -Hides all application windows without minimising them. +Hides all application windows without minimizing them. ### `app.show()` _OS X_ @@ -398,7 +398,7 @@ quit. On OS X the system enforces single instance automatically when users try to open a second instance of your app in Finder, and the `open-file` and `open-url` events will be emitted for that. However when users start your app in command -line the system's single instance machanism will be bypassed and you have to +line the system's single instance mechanism will be bypassed and you have to use this method to ensure single instance. An example of activating the window of primary instance when a second instance @@ -413,7 +413,6 @@ var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) if (myWindow.isMinimized()) myWindow.restore(); myWindow.focus(); } - return true; }); if (shouldQuit) { @@ -516,7 +515,7 @@ Shows the dock icon. ### `app.dock.setMenu(menu)` _OS X_ -* `menu` Menu +* `menu` [Menu](menu.md) Sets the application's [dock menu][dock-menu]. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index d38a5b7083d..6e1bc992539 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -457,7 +457,7 @@ Returns a boolean, whether the window is in fullscreen mode. * `aspectRatio` The aspect ratio we want to maintain for some portion of the content view. * `extraSize` Object (optional) - The extra size not to be included while -maintaining the aspect ratio. Properties: +maintaining the aspect ratio. * `width` Integer * `height` Integer @@ -477,13 +477,11 @@ height areas you have within the overall content view. ### `win.setBounds(options[, animate])` -* `options` Object, properties: - +* `options` Object * `x` Integer * `y` Integer * `width` Integer * `height` Integer - * `animate` Boolean (optional) _OS X_ Resizes and moves the window to `width`, `height`, `x`, `y`. @@ -716,7 +714,7 @@ Returns the pathname of the file the window represents. * `edited` Boolean Specifies whether the window’s document has been edited, and the icon in title -bar will become grey when set to `true`. +bar will become gray when set to `true`. ### `win.isDocumentEdited()` _OS X_ @@ -728,7 +726,7 @@ Whether the window's document has been edited. ### `win.capturePage([rect, ]callback)` -* `rect` Object (optional)- The area of page to be captured, properties: +* `rect` Object (optional) - The area of page to be captured * `x` Integer * `y` Integer * `width` Integer @@ -801,27 +799,7 @@ Returns whether the window has a shadow. On Windows and Linux always returns ### `win.setThumbarButtons(buttons)` _Windows 7+_ -`buttons` Array of `button` Objects: - -`button` Object, properties: - -* `icon` [NativeImage](native-image.md) - The icon showing in thumbnail - toolbar. -* `tooltip` String (optional) - The text of the button's tooltip. -* `flags` Array (optional) - Control specific states and behaviors - of the button. By default, it uses `enabled`. It can include following - Strings: - * `enabled` - The button is active and available to the user. - * `disabled` - The button is disabled. It is present, but has a visual - state indicating it will not respond to user action. - * `dismissonclick` - When the button is clicked, the taskbar button's - flyout closes immediately. - * `nobackground` - Do not draw a button border, use only the image. - * `hidden` - The button is not shown to the user. - * `noninteractive` - The button is enabled but not interactive; no - pressed button state is drawn. This value is intended for instances - where the button is used in a notification. -* `click` - Function +* `buttons` Array Add a thumbnail toolbar with a specified set of buttons to the thumbnail image of a window in a taskbar button layout. Returns a `Boolean` object indicates @@ -832,6 +810,29 @@ the limited room. Once you setup the thumbnail toolbar, the toolbar cannot be removed due to the platform's limitation. But you can call the API with an empty array to clean the buttons. +The `buttons` is an array of `Button` objects: + +* `Button` Object + * `icon` [NativeImage](native-image.md) - The icon showing in thumbnail + toolbar. + * `click` Function + * `tooltip` String (optional) - The text of the button's tooltip. + * `flags` Array (optional) - Control specific states and behaviors of the + button. By default, it is `['enabled']`. + +The `flags` is an array that can include following `String`s: + +* `enabled` - The button is active and available to the user. +* `disabled` - The button is disabled. It is present, but has a visual state + indicating it will not respond to user action. +* `dismissonclick` - When the button is clicked, the thumbnail window closes + immediately. +* `nobackground` - Do not draw a button border, use only the image. +* `hidden` - The button is not shown to the user. +* `noninteractive` - The button is enabled but not interactive; no pressed + button state is drawn. This value is intended for instances where the button + is used in a notification. + ### `win.showDefinitionForSelection()` _OS X_ Shows pop-up dictionary that searches the selected word on the page. diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index 7cb5b840bc1..7f95a1af26d 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -61,6 +61,19 @@ Returns the content in the clipboard as a [NativeImage](native-image.md). Writes `image` to the clipboard. +### `clipboard.readRtf([type])` + +* `type` String (optional) + +Returns the content in the clipboard as RTF. + +### `clipboard.writeRtf(text[, type])` + +* `text` String +* `type` String (optional) + +Writes the `text` into the clipboard in RTF. + ### `clipboard.clear([type])` * `type` String (optional) diff --git a/docs/api/content-tracing.md b/docs/api/content-tracing.md index aae5306523a..0b83c2759c6 100644 --- a/docs/api/content-tracing.md +++ b/docs/api/content-tracing.md @@ -155,7 +155,7 @@ called. * `eventName` String * `callback` Function -`callback` will will be called every time the given event occurs on any +`callback` will be called every time the given event occurs on any process. ### `contentTracing.cancelWatchEvent()` diff --git a/docs/api/crash-reporter.md b/docs/api/crash-reporter.md index 1b092826356..98465dffc97 100644 --- a/docs/api/crash-reporter.md +++ b/docs/api/crash-reporter.md @@ -22,19 +22,16 @@ The `crash-reporter` module has the following methods: ### `crashReporter.start(options)` -`options` Object, properties: - -* `productName` String, default: Electron. -* `companyName` String (**required**) -* `submitURL` String, (**required**) - * URL that crash reports will be sent to as POST. -* `autoSubmit` Boolean, default: `true`. - * Send the crash report without user interaction. -* `ignoreSystemCrashHandler` Boolean, default: `false`. -* `extra` Object - * An object you can define that will be sent along with the report. - * Only string properties are sent correctly. - * Nested objects are not supported. +* `options` Object + * `companyName` String + * `submitURL` String - URL that crash reports will be sent to as POST. + * `productName` String (optional) - Default is `Electron`. + * `autoSubmit` Boolean - Send the crash report without user interaction. + Default is `true`. + * `ignoreSystemCrashHandler` Boolean - Default is `false`. + * `extra` Object - An object you can define that will be sent along with the + report. Only string properties are sent correctly, Nested objects are not + supported. You are required to call this method before using other `crashReporter` APIs. diff --git a/docs/api/ipc-main.md b/docs/api/ipc-main.md index 337d86be217..84fbcfd5a72 100644 --- a/docs/api/ipc-main.md +++ b/docs/api/ipc-main.md @@ -9,7 +9,7 @@ module. ## Sending Messages It is also possible to send messages from the main process to the renderer -process, see [webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-) for more information. +process, see [webContents.send][web-contents-send] for more information. * When sending a message, the event name is the `channel`. * To reply a synchronous message, you need to set `event.returnValue`. @@ -48,37 +48,37 @@ ipcRenderer.send('asynchronous-message', 'ping'); The `ipcMain` module has the following method to listen for events: -### `ipcMain.on(channel, callback)` +### `ipcMain.on(channel, listener)` -* `channel` String - The event name. -* `callback` Function +* `channel` String +* `listener` Function -When the event occurs the `callback` is called with an `event` object and -arbitrary arguments. +Listens to `channel`, when a new message arrives `listener` would be called with +`listener(event, args...)`. -### `ipcMain.removeListener(channel, callback)` +### `ipcMain.once(channel, listener)` -* `channel` String - The event name. -* `callback` Function - The reference to the same function that you used for - `ipcMain.on(channel, callback)` +* `channel` String +* `listener` Function -Once done listening for messages, if you no longer want to activate this -callback and for whatever reason can't merely stop sending messages on the -channel, this function will remove the callback handler for the specified -channel. +Adds a one time `listener` function for the event. This `listener` is invoked +only the next time a message is sent to `channel`, after which it is removed. -### `ipcMain.removeAllListeners(channel)` +### `ipcMain.removeListener(channel, listener)` -* `channel` String - The event name. +* `channel` String +* `listener` Function -This removes *all* handlers to this ipc channel. +Removes the specified `listener` from the listener array for the specified +`channel`. -### `ipcMain.once(channel, callback)` +### `ipcMain.removeAllListeners([channel])` -Use this in place of `ipcMain.on()` to fire handlers meant to occur only once, -as in, they won't be activated after one call of `callback` +* `channel` String (optional) -## IPC Event +Removes all listeners, or those of the specified `channel`. + +## Event object The `event` object passed to the `callback` has the following methods: @@ -90,4 +90,6 @@ Set this to the value to be returned in a synchronous message. Returns the `webContents` that sent the message, you can call `event.sender.send` to reply to the asynchronous message, see -[webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-) for more information. +[webContents.send][web-contents-send] for more information. + +[web-contents-send]: web-contents.md#webcontentssendchannel-arg1-arg2- diff --git a/docs/api/ipc-renderer.md b/docs/api/ipc-renderer.md index 96936632563..f17843702b5 100644 --- a/docs/api/ipc-renderer.md +++ b/docs/api/ipc-renderer.md @@ -12,35 +12,35 @@ See [ipcMain](ipc-main.md) for code examples. The `ipcRenderer` module has the following method to listen for events: -### `ipcRenderer.on(channel, callback)` +### `ipcRenderer.on(channel, listener)` -* `channel` String - The event name. -* `callback` Function +* `channel` String +* `listener` Function -When the event occurs the `callback` is called with an `event` object and -arbitrary arguments. +Listens to `channel`, when a new message arrives `listener` would be called with +`listener(event, args...)`. -### `ipcRenderer.removeListener(channel, callback)` +### `ipcRenderer.once(channel, listener)` -* `channel` String - The event name. -* `callback` Function - The reference to the same function that you used for - `ipcRenderer.on(channel, callback)` +* `channel` String +* `listener` Function -Once done listening for messages, if you no longer want to activate this -callback and for whatever reason can't merely stop sending messages on the -channel, this function will remove the callback handler for the specified -channel. +Adds a one time `listener` function for the event. This `listener` is invoked +only the next time a message is sent to `channel`, after which it is removed. -### `ipcRenderer.removeAllListeners(channel)` +### `ipcRenderer.removeListener(channel, listener)` -* `channel` String - The event name. +* `channel` String +* `listener` Function -This removes *all* handlers to this ipc channel. +Removes the specified `listener` from the listener array for the specified +`channel`. -### `ipcRenderer.once(channel, callback)` +### `ipcRenderer.removeAllListeners([channel])` -Use this in place of `ipcRenderer.on()` to fire handlers meant to occur only once, -as in, they won't be activated after one call of `callback` +* `channel` String (optional) + +Removes all listeners, or those of the specified `channel`. ## Sending Messages @@ -48,30 +48,33 @@ The `ipcRenderer` module has the following methods for sending messages: ### `ipcRenderer.send(channel[, arg1][, arg2][, ...])` -* `channel` String - The event name. +* `channel` String * `arg` (optional) -Send an event to the main process asynchronously via a `channel`, you can also -send arbitrary arguments. The main process handles it by listening for the -`channel` event with `ipcMain`. +Send a message to the main process asynchronously via `channel`, you can also +send arbitrary arguments. Arguments will be serialized in JSON internally and +hence no functions or prototype chain will be included. + +The main process handles it by listening for `channel` with `ipcMain` module. ### `ipcRenderer.sendSync(channel[, arg1][, arg2][, ...])` -* `channel` String - The event name. +* `channel` String * `arg` (optional) -Send an event to the main process synchronously via a `channel`, you can also -send arbitrary arguments. +Send a message to the main process synchronously via `channel`, you can also +send arbitrary arguments. Arguments will be serialized in JSON internally and +hence no functions or prototype chain will be included. -The main process handles it by listening for the `channel` event with -`ipcMain` and replies by setting `event.returnValue`. +The main process handles it by listening for `channel` with `ipcMain` module, +and replies by setting `event.returnValue`. __Note:__ Sending a synchronous message will block the whole renderer process, unless you know what you are doing you should never use it. ### `ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])` -* `channel` String - The event name. +* `channel` String * `arg` (optional) Like `ipcRenderer.send` but the event will be sent to the `` element in diff --git a/docs/api/menu.md b/docs/api/menu.md index 49a499a3d4b..7d05f88aa2b 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -143,7 +143,7 @@ var template = [ ]; if (process.platform == 'darwin') { - var name = require('electron').app.getName(); + var name = require('electron').remote.app.getName(); template.unshift({ label: name, submenu: [ diff --git a/docs/api/shell.md b/docs/api/shell.md index e6678a95536..823dc481bb0 100644 --- a/docs/api/shell.md +++ b/docs/api/shell.md @@ -26,12 +26,16 @@ Show the given file in a file manager. If possible, select the file. Open the given file in the desktop's default manner. -### `shell.openExternal(url)` +### `shell.openExternal(url[, options])` * `url` String +* `options` Object (optional) _OS X_ + * `activate` Boolean - `true` to bring the opened application to the + foreground. The default is `true`. Open the given external protocol URL in the desktop's default manner. (For -example, mailto: URLs in the user's default mail agent.) +example, mailto: URLs in the user's default mail agent.) Returns true if an +application was available to open the URL, false otherwise. ### `shell.moveItemToTrash(fullPath)` diff --git a/docs/api/tray.md b/docs/api/tray.md index 08a43638be1..22ab9aea5b7 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -32,6 +32,13 @@ __Platform limitations:__ install `libappindicator1` to make the tray icon work. * App indicator will only be shown when it has a context menu. * When app indicator is used on Linux, the `click` event is ignored. +* On Linux in order for changes made to individual `MenuItem`s to take effect, + you have to call `setContextMenu` again. For example: + +```javascript +contextMenu.items[2].checked = false; +appIcon.setContextMenu(contextMenu); +``` If you want to keep exact same behaviors on all platforms, you should not rely on the `click` event and always attach a context menu to the tray icon. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index afdd08c4d00..25597d9b6c8 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -259,8 +259,8 @@ Returns: * `result` Object * `requestId` Integer * `finalUpdate` Boolean - Indicates if more responses are to follow. - * `matches` Integer (Optional) - Number of Matches. - * `selectionArea` Object (Optional) - Coordinates of first match region. + * `matches` Integer (optional) - Number of Matches. + * `selectionArea` Object (optional) - Coordinates of first match region. Emitted when a result is available for [`webContents.findInPage`](web-contents.md#webcontentsfindinpage) request. @@ -310,7 +310,7 @@ The `webContents` object has the following instance methods: ### `webContents.loadURL(url[, options])` * `url` URL -* `options` Object (optional), properties: +* `options` Object (optional) * `httpReferrer` String - A HTTP Referrer url. * `userAgent` String - A user agent originating the request. * `extraHeaders` String - Extra headers separated by "\n" @@ -425,10 +425,12 @@ Returns a `String` representing the user agent for this web page. Injects CSS into the current web page. -### `webContents.executeJavaScript(code[, userGesture])` +### `webContents.executeJavaScript(code[, userGesture, callback])` * `code` String * `userGesture` Boolean (optional) +* `callback` Function (optional) - Called after script has been executed. + * `result` Evaluates `code` in page. @@ -503,7 +505,7 @@ Inserts `text` to the focused element. ### `webContents.findInPage(text[, options])` * `text` String - Content to be searched, must not be empty. -* `options` Object (Optional) +* `options` Object (optional) * `forward` Boolean - Whether to search forward or backward, defaults to `true`. * `findNext` Boolean - Whether the operation is first request or a follow up, defaults to `false`. @@ -556,11 +558,10 @@ when the JS promise is rejected. ### `webContents.print([options])` -`options` Object (optional), properties: - -* `silent` Boolean - Don't ask user for print settings, defaults to `false` -* `printBackground` Boolean - Also prints the background color and image of - the web page, defaults to `false`. +* `options` Object (optional) + * `silent` Boolean - Don't ask user for print settings. Default is `false`. + * `printBackground` Boolean - Also prints the background color and image of + the web page. Default is `false`. Prints window's web page. When `silent` is set to `false`, Electron will pick up system's default printer and default settings for printing. @@ -574,31 +575,22 @@ size. ### `webContents.printToPDF(options, callback)` -`options` Object, properties: - -* `marginsType` Integer - Specify the type of margins to use - * 0 - default - * 1 - none - * 2 - minimum -* `pageSize` String - Specify page size of the generated PDF. - * `A5` - * `A4` - * `A3` - * `Legal` - * `Letter` - * `Tabloid` -* `printBackground` Boolean - Whether to print CSS backgrounds. -* `printSelectionOnly` Boolean - Whether to print selection only. -* `landscape` Boolean - `true` for landscape, `false` for portrait. - -`callback` Function - `function(error, data) {}` - -* `error` Error -* `data` Buffer - PDF file content. +* `options` Object + * `marginsType` Integer - Specifies the type of margins to use. Uses 0 for + default margin, 1 for no margin, and 2 for minimum margin. + * `pageSize` String - Specify page size of the generated PDF. Can be `A3`, + `A4`, `A5`, `Legal`, `Letter` and `Tabloid`. + * `printBackground` Boolean - Whether to print CSS backgrounds. + * `printSelectionOnly` Boolean - Whether to print selection only. + * `landscape` Boolean - `true` for landscape, `false` for portrait. +* `callback` Function Prints window's web page as PDF with Chromium's preview printing custom settings. +The `callback` will be called with `callback(error, data)` on completion. The +`data` is a `Buffer` that contains the generated PDF data. + By default, an empty `options` will be regarded as: ```javascript @@ -651,7 +643,7 @@ Removes the specified path from DevTools workspace. ### `webContents.openDevTools([options])` -* `options` Object (optional). Properties: +* `options` Object (optional) * `detach` Boolean - opens DevTools in a new window Opens the devtools. @@ -693,8 +685,11 @@ Opens the developer tools for the service worker context. * `arg` (optional) Send an asynchronous message to renderer process via `channel`, you can also -send arbitrary arguments. The renderer process can handle the message by -listening to the `channel` event with the `ipcRenderer` module. +send arbitrary arguments. Arguments will be serialized in JSON internally and +hence no functions or prototype chain will be included. + +The renderer process can handle the message by listening to `channel` with the +`ipcRenderer` module. An example of sending messages from the main process to the renderer process: @@ -776,8 +771,10 @@ For keyboard events, the `event` object also have following properties: * `keyCode` Char or String (**required**) - The character that will be sent as the keyboard event. Can be a single UTF-8 character, or the name of the key that generates the event. Accepted key names are `enter`, `backspace`, - `delete`, `tab`, `escape`, `control`, `alt`, `shift`, `end`, `home`, `insert`, - `left`, `up`, `right`, `down`, `pageUp`, `pageDown`, `printScreen` + `delete`, `tab`, `escape`, `control`, `alt`, `altgr` (Windows only), `shift`, + `end`, `home`, `insert`, `left`, `up`, `right`, `down`, `pageUp`, `pageDown`, + `printScreen`, `meta`, `cmd` (OSX only), `command` (OSX only), `option` + (OSX only) For mouse events, the `event` object also have following properties: @@ -849,6 +846,10 @@ win.webContents.on('did-finish-load', function() { Returns the [session](session.md) object used by this webContents. +### `webContents.hostWebContents` + +Returns the `WebContents` that might own this `WebContents`. + ### `webContents.devToolsWebContents` Get the `WebContents` of DevTools for this `WebContents`. diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index c410287553a..fa48ef60f3f 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -184,7 +184,7 @@ webview.addEventListener("dom-ready", function() { ### `.loadURL(url[, options])` * `url` URL -* `options` Object (optional), properties: +* `options` Object (optional) * `httpReferrer` String - A HTTP Referrer url. * `userAgent` String - A user agent originating the request. * `extraHeaders` String - Extra headers separated by "\n" @@ -279,10 +279,12 @@ Returns a `String` representing the user agent for guest page. Injects CSS into the guest page. -### `.executeJavaScript(code, userGesture)` +### `.executeJavaScript(code, userGesture, callback)` * `code` String * `userGesture` Boolean - Default `false`. +* `callback` Function (optional) - Called after script has been executed. + * `result` Evaluates `code` in page. If `userGesture` is set, it will create the user gesture context in the page. HTML APIs like `requestFullScreen`, which require @@ -382,7 +384,7 @@ Inserts `text` to the focused element. ### `.findInPage(text[, options])` * `text` String - Content to be searched, must not be empty. -* `options` Object (Optional) +* `options` Object (optional) * `forward` Boolean - Whether to search forward or backward, defaults to `true`. * `findNext` Boolean - Whether the operation is first request or a follow up, defaults to `false`. @@ -438,6 +440,10 @@ Sends an input `event` to the page. See [webContents.sendInputEvent](web-contents.md##webcontentssendinputeventevent) for detailed description of `event` object. +### `.getWebContents()` + +Returns the [WebContents](web-contents.md) associated with this `webview`. + ## DOM events The following DOM events are available to the `webview` tag: @@ -522,7 +528,7 @@ Returns: * `explicitSet` Boolean Fired when page title is set during navigation. `explicitSet` is false when -title is synthesised from file url. +title is synthesized from file url. ### Event: 'page-favicon-updated' @@ -567,8 +573,8 @@ Returns: * `result` Object * `requestId` Integer * `finalUpdate` Boolean - Indicates if more responses are to follow. - * `matches` Integer (Optional) - Number of Matches. - * `selectionArea` Object (Optional) - Coordinates of first match region. + * `matches` Integer (optional) - Number of Matches. + * `selectionArea` Object (optional) - Coordinates of first match region. Fired when a result is available for [`webview.findInPage`](web-view-tag.md#webviewtagfindinpage) request. diff --git a/docs/faq/electron-faq.md b/docs/faq/electron-faq.md index 643bde7409b..a58d31107b6 100644 --- a/docs/faq/electron-faq.md +++ b/docs/faq/electron-faq.md @@ -18,6 +18,34 @@ New features of Node.js are usually brought by V8 upgrades, since Electron is using the V8 shipped by Chrome browser, the shiny new JavaScript feature of a new Node.js version is usually already in Electron. +## How to share data between web pages? + +To share data between web pages (the renderer processes) the simplest way is to +use HTML5 APIs which are already available in browsers. Good candidates are +[Storage API][storage], [`localStorage`][local-storage], +[`sessionStorage`][session-storage], and [IndexedDB][indexed-db]. + +Or you can use the IPC system, which are specific to Electron, to store objects +in the main process as a global variable, and then to access them from the +renderers through the `remote` module: + +```javascript +// In the main process. +global.sharedObject = { + someProperty: 'default value' +}; +``` + +```javascript +// In page 1. +require('remote').getGlobal('sharedObject').someProperty = 'new value'; +``` + +```javascript +// In page 2. +console.log(require('remote').getGlobal('sharedObject').someProperty); +``` + ## My app's window/tray disappeared after a few minutes. This happens when the variable which is used to store the window/tray gets @@ -120,3 +148,7 @@ is only available in renderer processes. [memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management [variable-scope]: https://msdn.microsoft.com/library/bzt2dkta(v=vs.94).aspx [electron-module]: https://www.npmjs.com/package/electron +[storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage +[local-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage +[session-storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage +[indexed-db]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index 7d7eb44ecb6..a72d68b3cf3 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -271,6 +271,33 @@ var window = new BrowserWindow({...}); window.setProgressBar(0.5); ``` +## Icon Overlays in Taskbar (Windows) + +On Windows a taskbar button can use a small overlay to display application +status, as quoted from MSDN: + +> Icon overlays serve as a contextual notification of status, and are intended +> to negate the need for a separate notification area status icon to communicate +> that information to the user. For instance, the new mail status in Microsoft +> Outlook, currently shown in the notification area, can now be indicated +> through an overlay on the taskbar button. Again, you must decide during your +> development cycle which method is best for your application. Overlay icons are +> intended to supply important, long-standing status or notifications such as +> network status, messenger status, or new mail. The user should not be +> presented with constantly changing overlays or animations. + +__Overlay on taskbar button:__ + +![Overlay on taskbar button](https://i-msdn.sec.s-msft.com/dynimg/IC420441.png) + +To set the overlay icon for a window, you can use the +[BrowserWindow.setOverlayIcon][setoverlayicon] API: + +```javascript +var window = new BrowserWindow({...}); +window.setOverlayIcon('path/to/overlay.png', 'Description for overlay'); +``` + ## Represented File of Window (OS X) On OS X a window can set its represented file, so the file's icon can show in @@ -294,15 +321,16 @@ window.setRepresentedFilename('/etc/passwd'); window.setDocumentEdited(true); ``` -[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath -[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments -[setusertaskstasks]: ../api/app.md#appsetusertaskstasks -[setprogressbar]: ../api/browser-window.md#browserwindowsetprogressbarprogress -[setrepresentedfilename]: ../api/browser-window.md#browserwindowsetrepresentedfilenamefilename -[setdocumentedited]: ../api/browser-window.md#browserwindowsetdocumenteditededited +[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows +[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows +[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows +[setprogressbar]: ../api/browser-window.md#winsetprogressbarprogress +[setoverlayicon]: ../api/browser-window.md#winsetoverlayiconoverlay-description-windows-7 +[setrepresentedfilename]: ../api/browser-window.md#winsetrepresentedfilenamefilename-os-x +[setdocumentedited]: ../api/browser-window.md#winsetdocumenteditededited-os-x [app-registration]: http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx [unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher -[setthumbarbuttons]: ../api/browser-window.md#browserwindowsetthumbarbuttonsbuttons +[setthumbarbuttons]: ../api/browser-window.md#winsetthumbarbuttonsbuttons-windows-7 [tray-balloon]: ../api/tray.md#traydisplayballoonoptions-windows [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx [notification-spec]: https://developer.gnome.org/notification-spec/ diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index ebf907e070f..684c72c6669 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -34,8 +34,8 @@ The main process creates web pages by creating `BrowserWindow` instances. Each is also terminated. The main process manages all web pages and their corresponding renderer -processes. Each renderer process is isolated and only cares -about the web page running in it. +processes. Each renderer process is isolated and only cares about the web page +running in it. In web pages, calling native GUI related APIs is not allowed because managing native GUI resources in web pages is very dangerous and it is easy to leak @@ -43,9 +43,11 @@ resources. If you want to perform GUI operations in a web page, the renderer process of the web page must communicate with the main process to request that the main process perform those operations. -In Electron, we have provided the [ipc](../api/ipc-renderer.md) module for -communication between the main process and renderer process. There is also a -[remote](../api/remote.md) module for RPC style communication. +In Electron, we have several ways to communicate between the main process and +renderer processes. Like [`ipcRenderer`](../api/ipc-renderer.md) and +[`ipcMain`](../api/ipc-main.md) modules for sending messages, and the +[remote](../api/remote.md) module for RPC style communication. There is also +an FAQ entry on [how to share data between web pages][share-data]. ## Write your First Electron App @@ -205,3 +207,5 @@ $ cd electron-quick-start # Install dependencies and run the app $ npm install && npm start ``` + +[share-data]: ../faq/electron-faq.md#how-to-share-data-between-web-pages diff --git a/docs/tutorial/testing-on-headless-ci.md b/docs/tutorial/testing-on-headless-ci.md new file mode 100644 index 00000000000..6b0dacf788f --- /dev/null +++ b/docs/tutorial/testing-on-headless-ci.md @@ -0,0 +1,38 @@ +# Testing Electron with headless CI Systems (Travis CI, Jenkins) + +Being based on Chromium, Electron requires a display driver to function. If Chromium can't find a display driver, Electron will simply fail to launch - and therefore not executing any of your tests, regardless of how you are running them. Testing Electron-based apps on Travis, Circle, Jenkins or similar systems requires therefore a little bit of configuration. In essence, we need to use a virtual display driver. + +## Configuring the Virtual Display Server +First, install [Xvfb](https://en.wikipedia.org/wiki/Xvfb). It's a virtual framebuffer, implementing the X11 display server protocol - it performs all graphical operations in memory without showing any screen output, which is exactly what we need. + +Then, create a virtual xvfb screen and export an environment variable called DISPLAY that points to it. Chromium in Electron will automatically look for `$DISPLAY`, so no further configuration of your app is required. This step can be automated with Paul Betts's [xfvb-maybe](https://github.com/paulcbetts/xvfb-maybe): Prepend your test commands with `xfvb-maybe` and the little tool will automatically configure xfvb, if required by the current system. On Windows or Mac OS X, it will simply do nothing. + +``` +## On Windows or OS X, this just invokes electron-mocha +## On Linux, if we are in a headless environment, this will be equivalent +## to xvfb-run electron-mocha ./test/*.js +xvfb-maybe electron-mocha ./test/*.js +``` + +### Travis CI +On Travis, your `.travis.yml` should look roughly like this: + +``` +addons: + apt: + packages: + - xvfb + +install: + - export DISPLAY=':99.0' + - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & +``` + +### Jenkins +For Jenkins, a [Xfvb plugin is available](https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin). + +### Circle CI +Circle CI is awesome and has xvfb and `$DISPLAY` [already setup, so no further configuration is required](https://circleci.com/docs/environment#browsers). + +### AppVeyor +AppVeyor runs on Windows, supporting Selenium, Chromium, Electron and similar tools out of the box - no configuration is required. diff --git a/docs/tutorial/using-selenium-and-webdriver.md b/docs/tutorial/using-selenium-and-webdriver.md index 035dabdfe79..2d296548dd9 100644 --- a/docs/tutorial/using-selenium-and-webdriver.md +++ b/docs/tutorial/using-selenium-and-webdriver.md @@ -49,7 +49,7 @@ var driver = new webdriver.Builder() .withCapabilities({ chromeOptions: { // Here is the path to your Electron binary. - binary: '/Path-to-Your-App.app/Contents/MacOS/Atom', + binary: '/Path-to-Your-App.app/Contents/MacOS/Electron', } }) .forBrowser('electron') diff --git a/docs/tutorial/using-widevine-cdm-plugin.md b/docs/tutorial/using-widevine-cdm-plugin.md index 340dad343c8..630c18ab8e5 100644 --- a/docs/tutorial/using-widevine-cdm-plugin.md +++ b/docs/tutorial/using-widevine-cdm-plugin.md @@ -8,6 +8,10 @@ Electron doesn't ship with the Widevine CDM plugin for license reasons, to get it, you need to install the official Chrome browser first, which should match the architecture and Chrome version of the Electron build you use. +__Note:__ The major version of Chrome browser has to be the same with the Chrome +version used by Electron, otherwise the plugin will not work even though +`navigator.plugins` would show it has been loaded. + ### Windows & OS X Open `chrome://components/` in Chrome browser, find `WidevineCdm` and make diff --git a/filenames.gypi b/filenames.gypi index a5369570cad..abb11453210 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -41,6 +41,7 @@ 'atom/common/api/lib/clipboard.js', 'atom/common/api/lib/crash-reporter.js', 'atom/common/api/lib/deprecate.js', + 'atom/common/api/lib/deprecations.js', 'atom/common/api/lib/exports/electron.js', 'atom/common/api/lib/native-image.js', 'atom/common/api/lib/shell.js', diff --git a/package.json b/package.json index 3f0983bc441..504bdd6a4f7 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "electron", - "version": "0.36.7", + "version": "0.36.9", "devDependencies": { - "asar": "^0.9.0", - "eslint": "^1.10.3", + "asar": "^0.10.0", + "eslint": "^2.1.0", "request": "*" }, "optionalDependencies": { @@ -11,6 +11,8 @@ }, "private": true, "scripts": { + "bootstrap": "python ./script/bootstrap.py", + "build": "python ./script/build.py -c D", "lint": "python ./script/eslint.py && python ./script/cpplint.py", "preinstall": "node -e 'process.exit(0)'", "start": "python ./script/start.py", diff --git a/script/create-dist.py b/script/create-dist.py index a619e04d3ea..32d8f52aff3 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -38,6 +38,7 @@ TARGET_BINARIES = { 'libGLESv2.dll', 'msvcp120.dll', 'msvcr120.dll', + 'ffmpeg.dll', 'node.dll', 'pdf.dll', 'content_resources_200_percent.pak', @@ -51,6 +52,7 @@ TARGET_BINARIES = { PROJECT_NAME, # 'electron' 'content_shell.pak', 'icudtl.dat', + 'libffmpeg.so', 'libnode.so', 'natives_blob.bin', 'snapshot_blob.bin', @@ -97,6 +99,7 @@ def main(): create_dist_zip() create_chrome_binary_zip('chromedriver', get_chromedriver_version()) create_chrome_binary_zip('mksnapshot', ATOM_SHELL_VERSION) + create_ffmpeg_zip() create_symbols_zip() @@ -203,6 +206,24 @@ def create_chrome_binary_zip(binary, version): make_zip(zip_file, files, []) +def create_ffmpeg_zip(): + dist_name = 'ffmpeg-{0}-{1}-{2}.zip'.format( + ATOM_SHELL_VERSION, get_platform_key(), get_target_arch()) + zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name) + + if PLATFORM == 'darwin': + ffmpeg_name = 'libffmpeg.dylib' + elif PLATFORM == 'linux': + ffmpeg_name = 'libffmpeg.so' + elif PLATFORM == 'win32': + ffmpeg_name = 'ffmpeg.dll' + + shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'ffmpeg', ffmpeg_name), + DIST_DIR) + with scoped_cwd(DIST_DIR): + make_zip(zip_file, [ffmpeg_name, 'LICENSE', 'LICENSES.chromium.html'], []) + + def create_symbols_zip(): dist_name = '{0}-{1}-{2}-{3}-symbols.zip'.format(PROJECT_NAME, ATOM_SHELL_VERSION, diff --git a/script/eslint.py b/script/eslint.py index 8fa2c3a7e3a..7b912e0e1d6 100755 --- a/script/eslint.py +++ b/script/eslint.py @@ -4,6 +4,7 @@ import glob import os import sys +from lib.config import PLATFORM from lib.util import execute @@ -13,6 +14,10 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) def main(): os.chdir(SOURCE_ROOT) + # Skip eslint on our Windows build machine for now. + if PLATFORM == 'win32' and os.getenv('JANKY_SHA1'): + return + eslint = os.path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint') if sys.platform in ['win32', 'cygwin']: eslint += '.cmd' diff --git a/script/lib/config.py b/script/lib/config.py index 279fa7340a9..d4a055912e4 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -8,7 +8,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = 'ad63d8ba890bcaad2f1b7e6de148b7992f4d3af7' +LIBCHROMIUMCONTENT_COMMIT = 'ff714bf7ad79b0d278bcd20c3081a69b40be8bb8' PLATFORM = { 'cygwin': 'win32', diff --git a/script/upload.py b/script/upload.py index 3245d9caaa7..d23bc554c3c 100755 --- a/script/upload.py +++ b/script/upload.py @@ -35,9 +35,6 @@ DSYM_NAME = '{0}-{1}-{2}-{3}-dsym.zip'.format(PROJECT_NAME, ATOM_SHELL_VERSION, get_platform_key(), get_target_arch()) -MKSNAPSHOT_NAME = 'mksnapshot-{0}-{1}-{2}.zip'.format(ATOM_SHELL_VERSION, - get_platform_key(), - get_target_arch()) def main(): @@ -89,12 +86,19 @@ def main(): if PLATFORM == 'darwin': upload_atom_shell(github, release, os.path.join(DIST_DIR, DSYM_NAME)) + # Upload free version of ffmpeg. + ffmpeg = 'ffmpeg-{0}-{1}-{2}.zip'.format( + ATOM_SHELL_VERSION, get_platform_key(), get_target_arch()) + upload_atom_shell(github, release, os.path.join(DIST_DIR, ffmpeg)) + # Upload chromedriver and mksnapshot for minor version update. if parse_version(args.version)[2] == '0': chromedriver = 'chromedriver-{0}-{1}-{2}.zip'.format( get_chromedriver_version(), get_platform_key(), get_target_arch()) upload_atom_shell(github, release, os.path.join(DIST_DIR, chromedriver)) - upload_atom_shell(github, release, os.path.join(DIST_DIR, MKSNAPSHOT_NAME)) + mksnapshot = 'mksnapshot-{0}-{1}-{2}.zip'.format( + ATOM_SHELL_VERSION, get_platform_key(), get_target_arch()) + upload_atom_shell(github, release, os.path.join(DIST_DIR, mksnapshot)) if PLATFORM == 'win32' and not tag_exists: # Upload node headers. diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 0019ec59d5f..cbf8223401c 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -1,14 +1,10 @@ -var BrowserWindow, ChildProcess, app, assert, path, ref, remote; +const assert = require('assert'); +const ChildProcess = require('child_process'); +const path = require('path'); +const remote = require('electron').remote; -assert = require('assert'); - -ChildProcess = require('child_process'); - -path = require('path'); - -remote = require('electron').remote; - -ref = remote.require('electron'), app = ref.app, BrowserWindow = ref.BrowserWindow; +const app = remote.require('electron').app; +const BrowserWindow = remote.require('electron').BrowserWindow; describe('electron module', function() { it ('can prevent exposing internal modules to require', function(done) { @@ -27,100 +23,109 @@ describe('electron module', function() { describe('app module', function() { describe('app.getVersion()', function() { - return it('returns the version field of package.json', function() { - return assert.equal(app.getVersion(), '0.1.0'); + it('returns the version field of package.json', function() { + assert.equal(app.getVersion(), '0.1.0'); }); }); + describe('app.setVersion(version)', function() { - return it('overrides the version', function() { + it('overrides the version', function() { assert.equal(app.getVersion(), '0.1.0'); app.setVersion('test-version'); assert.equal(app.getVersion(), 'test-version'); - return app.setVersion('0.1.0'); + app.setVersion('0.1.0'); }); }); + describe('app.getName()', function() { - return it('returns the name field of package.json', function() { - return assert.equal(app.getName(), 'Electron Test'); + it('returns the name field of package.json', function() { + assert.equal(app.getName(), 'Electron Test'); }); }); + describe('app.setName(name)', function() { - return it('overrides the name', function() { + it('overrides the name', function() { assert.equal(app.getName(), 'Electron Test'); app.setName('test-name'); assert.equal(app.getName(), 'test-name'); - return app.setName('Electron Test'); + app.setName('Electron Test'); }); }); + describe('app.getLocale()', function() { - return it('should not be empty', function() { - return assert.notEqual(app.getLocale(), ''); + it('should not be empty', function() { + assert.notEqual(app.getLocale(), ''); }); }); + describe('app.exit(exitCode)', function() { - var appProcess; - appProcess = null; + var appProcess = null; + afterEach(function() { - return appProcess != null ? appProcess.kill() : void 0; + appProcess != null ? appProcess.kill() : void 0; }); - return it('emits a process exit event with the code', function(done) { - var appPath, electronPath, output; - appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app'); - electronPath = remote.getGlobal('process').execPath; + + it('emits a process exit event with the code', function(done) { + var appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app'); + var electronPath = remote.getGlobal('process').execPath; + var output = ''; appProcess = ChildProcess.spawn(electronPath, [appPath]); - output = ''; appProcess.stdout.on('data', function(data) { - return output += data; + output += data; }); - return appProcess.on('close', function(code) { + appProcess.on('close', function(code) { if (process.platform !== 'win32') { assert.notEqual(output.indexOf('Exit event with code: 123'), -1); } assert.equal(code, 123); - return done(); + done(); }); }); }); - return describe('BrowserWindow events', function() { - var w; - w = null; + + describe('BrowserWindow events', function() { + var w = null; + afterEach(function() { if (w != null) { w.destroy(); } - return w = null; + w = null; }); + it('should emit browser-window-focus event when window is focused', function(done) { app.once('browser-window-focus', function(e, window) { assert.equal(w.id, window.id); - return done(); + done(); }); w = new BrowserWindow({ show: false }); - return w.emit('focus'); + w.emit('focus'); }); + it('should emit browser-window-blur event when window is blured', function(done) { app.once('browser-window-blur', function(e, window) { assert.equal(w.id, window.id); - return done(); + done(); }); w = new BrowserWindow({ show: false }); - return w.emit('blur'); + w.emit('blur'); }); - return it('should emit browser-window-created event when window is created', function(done) { + + it('should emit browser-window-created event when window is created', function(done) { app.once('browser-window-created', function(e, window) { - return setImmediate(function() { + setImmediate(function() { assert.equal(w.id, window.id); - return done(); + done(); }); }); w = new BrowserWindow({ show: false }); - return w.emit('blur'); + w.emit('blur'); }); }); }); diff --git a/spec/api-auto-updater-spec.js b/spec/api-auto-updater-spec.js new file mode 100644 index 00000000000..efd3afe3882 --- /dev/null +++ b/spec/api-auto-updater-spec.js @@ -0,0 +1,38 @@ +const assert = require('assert'); +const autoUpdater = require('electron').remote.autoUpdater; +const ipcRenderer = require('electron').ipcRenderer; + +// Skip autoUpdater tests in MAS build. +if (process.mas) + return; + +describe('autoUpdater module', function() { + describe('checkForUpdates', function() { + it('emits an error on Windows when called the feed URL is not set', function (done) { + if (process.platform !== 'win32') { + return done(); + } + + ipcRenderer.once('auto-updater-error', function(event, message) { + assert.equal(message, 'Update URL is not set'); + done(); + }); + autoUpdater.setFeedURL(''); + autoUpdater.checkForUpdates(); + }); + }); + + describe('setFeedURL', function() { + it('emits an error on Mac OS X when the application is unsigned', function (done) { + if (process.platform !== 'darwin') { + return done(); + } + + ipcRenderer.once('auto-updater-error', function(event, message) { + assert.equal(message, 'Could not get code signature for running application'); + done(); + }); + autoUpdater.setFeedURL(''); + }); + }); +}); diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index fc8d3523c8f..25e92382cca 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -8,173 +8,193 @@ const os = require('os'); const remote = require('electron').remote; const screen = require('electron').screen; +const app = remote.require('electron').app; const ipcMain = remote.require('electron').ipcMain; +const ipcRenderer = require('electron').ipcRenderer; const BrowserWindow = remote.require('electron').BrowserWindow; const isCI = remote.getGlobal('isCi'); describe('browser-window module', function() { - var fixtures, w; - fixtures = path.resolve(__dirname, 'fixtures'); - w = null; + var fixtures = path.resolve(__dirname, 'fixtures'); + var w = null; + beforeEach(function() { if (w != null) { w.destroy(); } - return w = new BrowserWindow({ + w = new BrowserWindow({ show: false, width: 400, height: 400 }); }); + afterEach(function() { if (w != null) { w.destroy(); } - return w = null; + w = null; }); + describe('BrowserWindow.close()', function() { it('should emit unload handler', function(done) { w.webContents.on('did-finish-load', function() { - return w.close(); + w.close(); }); w.on('closed', function() { - var content, test; - test = path.join(fixtures, 'api', 'unload'); - content = fs.readFileSync(test); + var test = path.join(fixtures, 'api', 'unload'); + var content = fs.readFileSync(test); fs.unlinkSync(test); assert.equal(String(content), 'unload'); - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'unload.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'unload.html')); }); - return it('should emit beforeunload handler', function(done) { + + it('should emit beforeunload handler', function(done) { w.on('onbeforeunload', function() { - return done(); + done(); }); w.webContents.on('did-finish-load', function() { - return w.close(); + w.close(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false.html')); }); }); + describe('window.close()', function() { it('should emit unload handler', function(done) { w.on('closed', function() { - var content, test; - test = path.join(fixtures, 'api', 'close'); - content = fs.readFileSync(test); + var test = path.join(fixtures, 'api', 'close'); + var content = fs.readFileSync(test); fs.unlinkSync(test); assert.equal(String(content), 'close'); - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'close.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'close.html')); }); - return it('should emit beforeunload handler', function(done) { + + it('should emit beforeunload handler', function(done) { w.on('onbeforeunload', function() { - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')); }); }); + describe('BrowserWindow.destroy()', function() { - return it('prevents users to access methods of webContents', function() { - var webContents; - webContents = w.webContents; + it('prevents users to access methods of webContents', function() { + var webContents = w.webContents; w.destroy(); - return assert.throws((function() { - return webContents.getId(); + assert.throws((function() { + webContents.getId(); }), /Object has been destroyed/); }); }); + describe('BrowserWindow.loadURL(url)', function() { it('should emit did-start-loading event', function(done) { w.webContents.on('did-start-loading', function() { - return done(); + done(); }); - return w.loadURL('about:blank'); + w.loadURL('about:blank'); }); - return it('should emit did-fail-load event', function(done) { - w.webContents.on('did-fail-load', function() { - return done(); + + it('should emit did-fail-load event for files that do not exist', function(done) { + w.webContents.on('did-fail-load', function(event, code) { + assert.equal(code, -6); + done(); }); - return w.loadURL('file://a.txt'); + w.loadURL('file://a.txt'); + }); + + it('should emit did-fail-load event for invalid URL', function(done) { + w.webContents.on('did-fail-load', function(event, code, desc) { + assert.equal(desc, 'ERR_INVALID_URL'); + assert.equal(code, -300); + done(); + }); + w.loadURL('http://example:port'); }); }); + describe('BrowserWindow.show()', function() { - return it('should focus on window', function() { + it('should focus on window', function() { if (isCI) { return; } + w.show(); - return assert(w.isFocused()); + assert(w.isFocused()); }); }); + describe('BrowserWindow.showInactive()', function() { - return it('should not focus on window', function() { + it('should not focus on window', function() { w.showInactive(); - return assert(!w.isFocused()); + assert(!w.isFocused()); }); }); + describe('BrowserWindow.focus()', function() { - return it('does not make the window become visible', function() { + it('does not make the window become visible', function() { assert.equal(w.isVisible(), false); w.focus(); - return assert.equal(w.isVisible(), false); + assert.equal(w.isVisible(), false); }); }); + describe('BrowserWindow.capturePage(rect, callback)', function() { - return it('calls the callback with a Buffer', function(done) { - return w.capturePage({ + it('calls the callback with a Buffer', function(done) { + w.capturePage({ x: 0, y: 0, width: 100, height: 100 }, function(image) { assert.equal(image.isEmpty(), true); - return done(); + done(); }); }); }); + describe('BrowserWindow.setSize(width, height)', function() { - return it('sets the window size', function(done) { - var size; - size = [300, 400]; + it('sets the window size', function(done) { + var size = [300, 400]; w.once('resize', function() { - var newSize; - newSize = w.getSize(); + var newSize = w.getSize(); assert.equal(newSize[0], size[0]); assert.equal(newSize[1], size[1]); - return done(); + done(); }); - return w.setSize(size[0], size[1]); + w.setSize(size[0], size[1]); }); }); + describe('BrowserWindow.setPosition(x, y)', function() { - return it('sets the window position', function(done) { - var pos; - pos = [10, 10]; + it('sets the window position', function(done) { + var pos = [10, 10]; w.once('move', function() { - var newPos; - newPos = w.getPosition(); + var newPos = w.getPosition(); assert.equal(newPos[0], pos[0]); assert.equal(newPos[1], pos[1]); - return done(); + done(); }); - return w.setPosition(pos[0], pos[1]); + w.setPosition(pos[0], pos[1]); }); }); + describe('BrowserWindow.setContentSize(width, height)', function() { it('sets the content size', function() { - var after, size; - size = [400, 400]; + var size = [400, 400]; w.setContentSize(size[0], size[1]); - after = w.getContentSize(); + var after = w.getContentSize(); assert.equal(after[0], size[0]); - return assert.equal(after[1], size[1]); + assert.equal(after[1], size[1]); }); - return it('works for framless window', function() { - var after, size; + + it('works for framless window', function() { w.destroy(); w = new BrowserWindow({ show: false, @@ -182,21 +202,22 @@ describe('browser-window module', function() { width: 400, height: 400 }); - size = [400, 400]; + var size = [400, 400]; w.setContentSize(size[0], size[1]); - after = w.getContentSize(); + var after = w.getContentSize(); assert.equal(after[0], size[0]); - return assert.equal(after[1], size[1]); + assert.equal(after[1], size[1]); }); }); + describe('BrowserWindow.fromId(id)', function() { - return it('returns the window with id', function() { - return assert.equal(w.id, BrowserWindow.fromId(w.id).id); + it('returns the window with id', function() { + assert.equal(w.id, BrowserWindow.fromId(w.id).id); }); }); + describe('"useContentSize" option', function() { it('make window created with content size when used', function() { - var contentSize; w.destroy(); w = new BrowserWindow({ show: false, @@ -204,18 +225,18 @@ describe('browser-window module', function() { height: 400, useContentSize: true }); - contentSize = w.getContentSize(); + var contentSize = w.getContentSize(); assert.equal(contentSize[0], 400); - return assert.equal(contentSize[1], 400); + assert.equal(contentSize[1], 400); }); + it('make window created with window size when not used', function() { - var size; - size = w.getSize(); + var size = w.getSize(); assert.equal(size[0], 400); - return assert.equal(size[1], 400); + assert.equal(size[1], 400); }); - return it('works for framless window', function() { - var contentSize, size; + + it('works for framless window', function() { w.destroy(); w = new BrowserWindow({ show: false, @@ -224,14 +245,15 @@ describe('browser-window module', function() { height: 400, useContentSize: true }); - contentSize = w.getContentSize(); + var contentSize = w.getContentSize(); assert.equal(contentSize[0], 400); assert.equal(contentSize[1], 400); - size = w.getSize(); + var size = w.getSize(); assert.equal(size[0], 400); - return assert.equal(size[1], 400); + assert.equal(size[1], 400); }); }); + describe('"title-bar-style" option', function() { if (process.platform !== 'darwin') { return; @@ -239,8 +261,8 @@ describe('browser-window module', function() { if (parseInt(os.release().split('.')[0]) < 14) { return; } + it('creates browser window with hidden title bar', function() { - var contentSize; w.destroy(); w = new BrowserWindow({ show: false, @@ -248,11 +270,11 @@ describe('browser-window module', function() { height: 400, titleBarStyle: 'hidden' }); - contentSize = w.getContentSize(); - return assert.equal(contentSize[1], 400); + var contentSize = w.getContentSize(); + assert.equal(contentSize[1], 400); }); - return it('creates browser window with hidden inset title bar', function() { - var contentSize; + + it('creates browser window with hidden inset title bar', function() { w.destroy(); w = new BrowserWindow({ show: false, @@ -260,53 +282,55 @@ describe('browser-window module', function() { height: 400, titleBarStyle: 'hidden-inset' }); - contentSize = w.getContentSize(); - return assert.equal(contentSize[1], 400); + var contentSize = w.getContentSize(); + assert.equal(contentSize[1], 400); }); }); + describe('"enableLargerThanScreen" option', function() { if (process.platform === 'linux') { return; } + beforeEach(function() { w.destroy(); - return w = new BrowserWindow({ + w = new BrowserWindow({ show: true, width: 400, height: 400, enableLargerThanScreen: true }); }); + it('can move the window out of screen', function() { - var after; w.setPosition(-10, -10); - after = w.getPosition(); + var after = w.getPosition(); assert.equal(after[0], -10); - return assert.equal(after[1], -10); + assert.equal(after[1], -10); }); - return it('can set the window larger than screen', function() { - var after, size; - size = screen.getPrimaryDisplay().size; + + it('can set the window larger than screen', function() { + var size = screen.getPrimaryDisplay().size; size.width += 100; size.height += 100; w.setSize(size.width, size.height); - after = w.getSize(); + var after = w.getSize(); assert.equal(after[0], size.width); - return assert.equal(after[1], size.height); + assert.equal(after[1], size.height); }); }); describe('"web-preferences" option', function() { afterEach(function() { - return ipcMain.removeAllListeners('answer'); + ipcMain.removeAllListeners('answer'); }); + describe('"preload" option', function() { - return it('loads the script before other scripts in window', function(done) { - var preload; - preload = path.join(fixtures, 'module', 'set-global.js'); + it('loads the script before other scripts in window', function(done) { + var preload = path.join(fixtures, 'module', 'set-global.js'); ipcMain.once('answer', function(event, test) { assert.equal(test, 'preload'); - return done(); + done(); }); w.destroy(); w = new BrowserWindow({ @@ -315,16 +339,16 @@ describe('browser-window module', function() { preload: preload } }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'preload.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'preload.html')); }); }); - return describe('"node-integration" option', function() { - return it('disables node integration when specified to false', function(done) { - var preload; - preload = path.join(fixtures, 'module', 'send-later.js'); + + describe('"node-integration" option', function() { + it('disables node integration when specified to false', function(done) { + var preload = path.join(fixtures, 'module', 'send-later.js'); ipcMain.once('answer', function(event, test) { assert.equal(test, 'undefined'); - return done(); + done(); }); w.destroy(); w = new BrowserWindow({ @@ -334,107 +358,129 @@ describe('browser-window module', function() { nodeIntegration: false } }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'blank.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'blank.html')); }); }); }); + describe('beforeunload handler', function() { it('returning true would not prevent close', function(done) { w.on('closed', function() { - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-true.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-true.html')); }); + it('returning non-empty string would not prevent close', function(done) { w.on('closed', function() { - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-string.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-string.html')); }); + it('returning false would prevent close', function(done) { w.on('onbeforeunload', function() { - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')); }); - return it('returning empty string would prevent close', function(done) { + + it('returning empty string would prevent close', function(done) { w.on('onbeforeunload', function() { - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-empty-string.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-empty-string.html')); }); }); + describe('new-window event', function() { if (isCI && process.platform === 'darwin') { return; } + it('emits when window.open is called', function(done) { w.webContents.once('new-window', function(e, url, frameName) { e.preventDefault(); assert.equal(url, 'http://host/'); assert.equal(frameName, 'host'); - return done(); + done(); }); - return w.loadURL("file://" + fixtures + "/pages/window-open.html"); + w.loadURL("file://" + fixtures + "/pages/window-open.html"); }); - return it('emits when link with target is called', function(done) { + + it('emits when link with target is called', function(done) { this.timeout(10000); w.webContents.once('new-window', function(e, url, frameName) { e.preventDefault(); assert.equal(url, 'http://host/'); assert.equal(frameName, 'target'); - return done(); + done(); }); - return w.loadURL("file://" + fixtures + "/pages/target-name.html"); + w.loadURL("file://" + fixtures + "/pages/target-name.html"); }); }); + describe('maximize event', function() { if (isCI) { return; } - return it('emits when window is maximized', function(done) { + + it('emits when window is maximized', function(done) { this.timeout(10000); w.once('maximize', function() { - return done(); + done(); }); w.show(); - return w.maximize(); + w.maximize(); }); }); + describe('unmaximize event', function() { if (isCI) { return; } - return it('emits when window is unmaximized', function(done) { + + it('emits when window is unmaximized', function(done) { this.timeout(10000); w.once('unmaximize', function() { - return done(); + done(); }); w.show(); w.maximize(); - return w.unmaximize(); + w.unmaximize(); }); }); + describe('minimize event', function() { if (isCI) { return; } - return it('emits when window is minimized', function(done) { + + it('emits when window is minimized', function(done) { this.timeout(10000); w.once('minimize', function() { - return done(); + done(); }); w.show(); - return w.minimize(); + w.minimize(); }); }); - xdescribe('beginFrameSubscription method', function() { - return it('subscribes frame updates', function(done) { + + describe('beginFrameSubscription method', function() { + this.timeout(20000); + + it('subscribes frame updates', function(done) { + let called = false; w.loadURL("file://" + fixtures + "/api/blank.html"); - return w.webContents.beginFrameSubscription(function(data) { + w.webContents.beginFrameSubscription(function(data) { + // This callback might be called twice. + if (called) + return; + called = true; + assert.notEqual(data.length, 0); w.webContents.endFrameSubscription(); - return done(); + done(); }); }); }); @@ -472,13 +518,12 @@ describe('browser-window module', function() { }); describe('BrowserWindow options argument is optional', function() { - return it('should create a window with default size (800x600)', function() { - var size; + it('should create a window with default size (800x600)', function() { w.destroy(); w = new BrowserWindow(); - size = w.getSize(); + var size = w.getSize(); assert.equal(size[0], 800); - return assert.equal(size[1], 600); + assert.equal(size[1], 600); }); }); @@ -618,4 +663,50 @@ describe('browser-window module', function() { }); }); }); + + describe('window.webContents.send(channel, args...)', function() { + it('throws an error when the channel is missing', function() { + assert.throws(function() { + w.webContents.send(); + }, 'Missing required channel argument'); + + assert.throws(function() { + w.webContents.send(null); + }, 'Missing required channel argument'); + }); + }); + + describe('dev tool extensions', function () { + it('serializes the registered extensions on quit', function () { + var extensionName = 'foo'; + var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName); + var serializedPath = path.join(app.getPath('userData'), 'DevTools Extensions'); + + BrowserWindow.addDevToolsExtension(extensionPath); + app.emit('will-quit'); + assert.deepEqual(JSON.parse(fs.readFileSync(serializedPath)), [extensionPath]); + + BrowserWindow.removeDevToolsExtension(extensionName); + app.emit('will-quit'); + assert.equal(fs.existsSync(serializedPath), false); + }); + }); + + describe('window.webContents.executeJavaScript', function() { + var expected = 'hello, world!'; + var code = '(() => \"' + expected + '\")()'; + + it('doesnt throw when no calback is provided', function() { + const result = ipcRenderer.sendSync('executeJavaScript', code, false); + assert.equal(result, 'success'); + }); + + it('returns result when calback is provided', function(done) { + ipcRenderer.send('executeJavaScript', code, true); + ipcRenderer.once('executeJavaScript-response', function(event, result) { + assert.equal(result, expected); + done(); + }); + }); + }); }); diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 6154181f092..0b4a9f9521c 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -1,55 +1,63 @@ -var assert, clipboard, nativeImage, path, ref; +const assert = require('assert'); +const path = require('path'); -assert = require('assert'); - -path = require('path'); - -ref = require('electron'), clipboard = ref.clipboard, nativeImage = ref.nativeImage; +const clipboard = require('electron').clipboard; +const nativeImage = require('electron').nativeImage; describe('clipboard module', function() { - var fixtures; - fixtures = path.resolve(__dirname, 'fixtures'); + var fixtures = path.resolve(__dirname, 'fixtures'); + describe('clipboard.readImage()', function() { - return it('returns NativeImage intance', function() { - var i, p; - p = path.join(fixtures, 'assets', 'logo.png'); - i = nativeImage.createFromPath(p); + it('returns NativeImage intance', function() { + var p = path.join(fixtures, 'assets', 'logo.png'); + var i = nativeImage.createFromPath(p); clipboard.writeImage(p); - return assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()); + assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()); }); }); + describe('clipboard.readText()', function() { - return it('returns unicode string correctly', function() { - var text; - text = '千江有水千江月,万里无云万里天'; + it('returns unicode string correctly', function() { + var text = '千江有水千江月,万里无云万里天'; clipboard.writeText(text); - return assert.equal(clipboard.readText(), text); + assert.equal(clipboard.readText(), text); }); }); + describe('clipboard.readHtml()', function() { - return it('returns markup correctly', function() { - var markup, text; - text = 'Hi'; - markup = process.platform === 'darwin' ? 'Hi' : process.platform === 'linux' ? 'Hi' : 'Hi'; + it('returns markup correctly', function() { + var text = 'Hi'; + var markup = process.platform === 'darwin' ? 'Hi' : process.platform === 'linux' ? 'Hi' : 'Hi'; clipboard.writeHtml(text); - return assert.equal(clipboard.readHtml(), markup); + assert.equal(clipboard.readHtml(), markup); }); }); - return describe('clipboard.write()', function() { - return it('returns data correctly', function() { - var i, markup, p, text; - text = 'test'; - p = path.join(fixtures, 'assets', 'logo.png'); - i = nativeImage.createFromPath(p); - markup = process.platform === 'darwin' ? 'Hi' : process.platform === 'linux' ? 'Hi' : 'Hi'; + + describe('clipboard.readRtf', function() { + it('returns rtf text correctly', function() { + var rtf = "{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}"; + clipboard.writeRtf(rtf); + assert.equal(clipboard.readRtf(), rtf); + }); + }); + + describe('clipboard.write()', function() { + it('returns data correctly', function() { + var text = 'test'; + var rtf = '{\\rtf1\\utf8 text}'; + var p = path.join(fixtures, 'assets', 'logo.png'); + var i = nativeImage.createFromPath(p); + var markup = process.platform === 'darwin' ? 'Hi' : process.platform === 'linux' ? 'Hi' : 'Hi'; clipboard.write({ text: "test", html: 'Hi', + rtf: '{\\rtf1\\utf8 text}', image: p }); assert.equal(clipboard.readText(), text); assert.equal(clipboard.readHtml(), markup); - return assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()); + assert.equal(clipboard.readRtf(), rtf); + assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()); }); }); }); diff --git a/spec/api-crash-reporter-spec.js b/spec/api-crash-reporter-spec.js index b3c4c311589..68dc1375fc3 100644 --- a/spec/api-crash-reporter-spec.js +++ b/spec/api-crash-reporter-spec.js @@ -1,47 +1,45 @@ -var BrowserWindow, app, assert, crashReporter, http, multiparty, path, ref, remote, url; +const assert = require('assert'); +const http = require('http'); +const multiparty = require('multiparty'); +const path = require('path'); +const url = require('url'); -assert = require('assert'); - -path = require('path'); - -http = require('http'); - -url = require('url'); - -multiparty = require('multiparty'); - -remote = require('electron').remote; - -ref = remote.require('electron'), app = ref.app, crashReporter = ref.crashReporter, BrowserWindow = ref.BrowserWindow; +const remote = require('electron').remote; +const app = remote.require('electron').app; +const crashReporter = remote.require('electron').crashReporter; +const BrowserWindow = remote.require('electron').BrowserWindow; describe('crash-reporter module', function() { - var fixtures, isCI, w; - fixtures = path.resolve(__dirname, 'fixtures'); - w = null; + var fixtures = path.resolve(__dirname, 'fixtures'); + var w = null; + beforeEach(function() { - return w = new BrowserWindow({ + w = new BrowserWindow({ show: false }); }); + afterEach(function() { - return w.destroy(); + w.destroy(); }); + if (process.mas) { return; } - isCI = remote.getGlobal('isCi'); + + var isCI = remote.getGlobal('isCi'); if (isCI) { return; } + it('should send minidump when renderer crashes', function(done) { - var called, port, server; this.timeout(120000); - called = false; - server = http.createServer(function(req, res) { - var form; + + var called = false; + var server = http.createServer(function(req, res) { server.close(); - form = new multiparty.Form(); - return form.parse(req, function(error, fields) { + var form = new multiparty.Form(); + form.parse(req, function(error, fields) { if (called) { return; } @@ -56,14 +54,14 @@ describe('crash-reporter module', function() { assert.equal(fields['_companyName'], 'Umbrella Corporation'); assert.equal(fields['_version'], app.getVersion()); res.end('abc-123-def'); - return done(); + done(); }); }); - port = remote.process.port; - return server.listen(port, '127.0.0.1', function() { + var port = remote.process.port; + server.listen(port, '127.0.0.1', function() { port = server.address().port; remote.process.port = port; - url = url.format({ + const crashUrl = url.format({ protocol: 'file', pathname: path.join(fixtures, 'api', 'crash.html'), search: "?port=" + port @@ -74,18 +72,19 @@ describe('crash-reporter module', function() { submitURL: "http://127.0.0.1:" + port }); } - return w.loadURL(url); + w.loadURL(crashUrl); }); }); - return describe(".start(options)", function() { - return it('requires that the companyName and submitURL options be specified', function() { + + describe(".start(options)", function() { + it('requires that the companyName and submitURL options be specified', function() { assert.throws(function() { - return crashReporter.start({ + crashReporter.start({ companyName: 'Missing submitURL' }); }); - return assert.throws(function() { - return crashReporter.start({ + assert.throws(function() { + crashReporter.start({ submitURL: 'Missing companyName' }); }); diff --git a/spec/api-debugger-spec.js b/spec/api-debugger-spec.js index 7a16ca7263c..56b642e76e4 100644 --- a/spec/api-debugger-spec.js +++ b/spec/api-debugger-spec.js @@ -1,17 +1,11 @@ -var assert, path, remote, BrowserWindow; - -assert = require('assert'); - -path = require('path'); - -remote = require('electron').remote; - -BrowserWindow = remote.BrowserWindow; +const assert = require('assert'); +const path = require('path'); +const BrowserWindow = require('electron').remote.BrowserWindow; describe('debugger module', function() { - var fixtures, w; - fixtures = path.resolve(__dirname, 'fixtures'); - w = null; + var fixtures = path.resolve(__dirname, 'fixtures'); + var w = null; + beforeEach(function() { if (w != null) { w.destroy(); @@ -22,6 +16,7 @@ describe('debugger module', function() { height: 400 }); }); + afterEach(function() { if (w != null) { w.destroy(); @@ -100,7 +95,9 @@ describe('debugger module', function() { }); it('fires message event', function(done) { - var url = 'file://' + path.join(fixtures, 'pages', 'a.html'); + var url = process.platform != 'win32' ? + 'file://' + path.join(fixtures, 'pages', 'a.html') : + 'file:///' + path.join(fixtures, 'pages', 'a.html').replace(/\\/g, '/'); w.webContents.loadURL(url); try { w.webContents.debugger.attach(); diff --git a/spec/api-deprecations-spec.js b/spec/api-deprecations-spec.js new file mode 100644 index 00000000000..2f010059059 --- /dev/null +++ b/spec/api-deprecations-spec.js @@ -0,0 +1,27 @@ +const assert = require('assert'); +const deprecations = require('electron').deprecations; + +describe('deprecations', function() { + beforeEach(function() { + deprecations.setHandler(null); + process.throwDeprecation = true; + }); + + it('allows a deprecation handler function to be specified', function() { + var messages = []; + + deprecations.setHandler(function (message) { + messages.push(message); + }); + + require('electron').webFrame.registerUrlSchemeAsSecure('some-scheme'); + + assert.deepEqual(messages, ['registerUrlSchemeAsSecure is deprecated. Use registerURLSchemeAsSecure instead.']); + }); + + it('throws an exception if no deprecation handler is specified', function() { + assert.throws(function() { + require('electron').webFrame.registerUrlSchemeAsPrivileged('some-scheme'); + }, "registerUrlSchemeAsPrivileged is deprecated. Use registerURLSchemeAsPrivileged instead."); + }); +}); diff --git a/spec/api-ipc-spec.js b/spec/api-ipc-spec.js index 66f392a577c..1ac717cd9d3 100644 --- a/spec/api-ipc-spec.js +++ b/spec/api-ipc-spec.js @@ -1,145 +1,200 @@ -var BrowserWindow, assert, comparePaths, ipcMain, ipcRenderer, path, ref, ref1, remote; +'use strict'; -assert = require('assert'); +const assert = require('assert'); +const path = require('path'); -path = require('path'); +const ipcRenderer = require('electron').ipcRenderer; +const remote = require('electron').remote; -ref = require('electron'), ipcRenderer = ref.ipcRenderer, remote = ref.remote; +const ipcMain = remote.require('electron').ipcMain; +const BrowserWindow = remote.require('electron').BrowserWindow; -ref1 = remote.require('electron'), ipcMain = ref1.ipcMain, BrowserWindow = ref1.BrowserWindow; - -comparePaths = function(path1, path2) { +const comparePaths = function(path1, path2) { if (process.platform === 'win32') { path1 = path1.toLowerCase(); path2 = path2.toLowerCase(); } - return assert.equal(path1, path2); + assert.equal(path1, path2); }; describe('ipc module', function() { - var fixtures; - fixtures = path.join(__dirname, 'fixtures'); + var fixtures = path.join(__dirname, 'fixtures'); + describe('remote.require', function() { it('should returns same object for the same module', function() { - var dialog1, dialog2; - dialog1 = remote.require('electron'); - dialog2 = remote.require('electron'); - return assert.equal(dialog1, dialog2); + var dialog1 = remote.require('electron'); + var dialog2 = remote.require('electron'); + assert.equal(dialog1, dialog2); }); + it('should work when object contains id property', function() { - var a; - a = remote.require(path.join(fixtures, 'module', 'id.js')); - return assert.equal(a.id, 1127); + var a = remote.require(path.join(fixtures, 'module', 'id.js')); + assert.equal(a.id, 1127); }); - return it('should search module from the user app', function() { + + it('should search module from the user app', function() { comparePaths(path.normalize(remote.process.mainModule.filename), path.resolve(__dirname, 'static', 'main.js')); - return comparePaths(path.normalize(remote.process.mainModule.paths[0]), path.resolve(__dirname, 'static', 'node_modules')); + comparePaths(path.normalize(remote.process.mainModule.paths[0]), path.resolve(__dirname, 'static', 'node_modules')); }); }); + describe('remote.createFunctionWithReturnValue', function() { - return it('should be called in browser synchronously', function() { - var buf, call, result; - buf = new Buffer('test'); - call = remote.require(path.join(fixtures, 'module', 'call.js')); - result = call.call(remote.createFunctionWithReturnValue(buf)); - return assert.equal(result.constructor.name, 'Buffer'); + it('should be called in browser synchronously', function() { + var buf = new Buffer('test'); + var call = remote.require(path.join(fixtures, 'module', 'call.js')); + var result = call.call(remote.createFunctionWithReturnValue(buf)); + assert.equal(result.constructor.name, 'Buffer'); }); }); + describe('remote object in renderer', function() { it('can change its properties', function() { - var property, property2; - property = remote.require(path.join(fixtures, 'module', 'property.js')); + var property = remote.require(path.join(fixtures, 'module', 'property.js')); assert.equal(property.property, 1127); property.property = 1007; assert.equal(property.property, 1007); - property2 = remote.require(path.join(fixtures, 'module', 'property.js')); + var property2 = remote.require(path.join(fixtures, 'module', 'property.js')); assert.equal(property2.property, 1007); - return property.property = 1127; + property.property = 1127; }); - return it('can construct an object from its member', function() { - var call, obj; - call = remote.require(path.join(fixtures, 'module', 'call.js')); - obj = new call.constructor; - return assert.equal(obj.test, 'test'); + + it('can construct an object from its member', function() { + var call = remote.require(path.join(fixtures, 'module', 'call.js')); + var obj = new call.constructor; + assert.equal(obj.test, 'test'); }); }); + describe('remote value in browser', function() { - var print; - print = path.join(fixtures, 'module', 'print_name.js'); + var print = path.join(fixtures, 'module', 'print_name.js'); + it('keeps its constructor name for objects', function() { - var buf, print_name; - buf = new Buffer('test'); - print_name = remote.require(print); - return assert.equal(print_name.print(buf), 'Buffer'); + var buf = new Buffer('test'); + var print_name = remote.require(print); + assert.equal(print_name.print(buf), 'Buffer'); }); - return it('supports instanceof Date', function() { - var now, print_name; - now = new Date(); - print_name = remote.require(print); + + it('supports instanceof Date', function() { + var now = new Date(); + var print_name = remote.require(print); assert.equal(print_name.print(now), 'Date'); - return assert.deepEqual(print_name.echo(now), now); + assert.deepEqual(print_name.echo(now), now); }); }); + describe('remote promise', function() { - return it('can be used as promise in each side', function(done) { - var promise; - promise = remote.require(path.join(fixtures, 'module', 'promise.js')); - return promise.twicePromise(Promise.resolve(1234)).then(function(value) { + it('can be used as promise in each side', function(done) { + var promise = remote.require(path.join(fixtures, 'module', 'promise.js')); + promise.twicePromise(Promise.resolve(1234)).then(function(value) { assert.equal(value, 2468); - return done(); + done(); }); }); }); + + describe('remote webContents', function() { + it('can return same object with different getters', function() { + var contents1 = remote.getCurrentWindow().webContents; + var contents2 = remote.getCurrentWebContents(); + assert(contents1 == contents2); + }); + }); + + describe('remote class', function() { + let cl = remote.require(path.join(fixtures, 'module', 'class.js')); + let base = cl.base; + let derived = cl.derived; + + it('can get methods', function() { + assert.equal(base.method(), 'method'); + }); + + it('can get properties', function() { + assert.equal(base.readonly, 'readonly'); + }); + + it('can change properties', function() { + assert.equal(base.value, 'old'); + base.value = 'new'; + assert.equal(base.value, 'new'); + base.value = 'old'; + }); + + it('has unenumerable methods', function() { + assert(!base.hasOwnProperty('method')); + assert(Object.getPrototypeOf(base).hasOwnProperty('method')); + }); + + it('keeps prototype chain in derived class', function() { + assert.equal(derived.method(), 'method'); + assert.equal(derived.readonly, 'readonly'); + assert(!derived.hasOwnProperty('method')); + let proto = Object.getPrototypeOf(derived); + assert(!proto.hasOwnProperty('method')); + assert(Object.getPrototypeOf(proto).hasOwnProperty('method')); + }); + }); + describe('ipc.sender.send', function() { - return it('should work when sending an object containing id property', function(done) { - var obj; - obj = { + it('should work when sending an object containing id property', function(done) { + var obj = { id: 1, name: 'ly' }; ipcRenderer.once('message', function(event, message) { assert.deepEqual(message, obj); - return done(); + done(); }); - return ipcRenderer.send('message', obj); + ipcRenderer.send('message', obj); + }); + + it('can send instance of Date', function(done) { + const currentDate = new Date(); + ipcRenderer.once('message', function(event, value) { + assert.equal(value, currentDate.toISOString()); + done(); + }); + ipcRenderer.send('message', currentDate); }); }); + describe('ipc.sendSync', function() { it('can be replied by setting event.returnValue', function() { - var msg; - msg = ipcRenderer.sendSync('echo', 'test'); - return assert.equal(msg, 'test'); + var msg = ipcRenderer.sendSync('echo', 'test'); + assert.equal(msg, 'test'); }); - return it('does not crash when reply is not sent and browser is destroyed', function(done) { - var w; + + it('does not crash when reply is not sent and browser is destroyed', function(done) { this.timeout(10000); - w = new BrowserWindow({ + + var w = new BrowserWindow({ show: false }); ipcMain.once('send-sync-message', function(event) { event.returnValue = null; w.destroy(); - return done(); + done(); }); - return w.loadURL('file://' + path.join(fixtures, 'api', 'send-sync-message.html')); + w.loadURL('file://' + path.join(fixtures, 'api', 'send-sync-message.html')); }); }); - return describe('remote listeners', function() { - var w; - w = null; + + describe('remote listeners', function() { + var w = null; + afterEach(function() { - return w.destroy(); + w.destroy(); }); - return it('can be added and removed correctly', function() { - var listener; + + it('can be added and removed correctly', function() { w = new BrowserWindow({ show: false }); - listener = function() {}; + var listener = function() {}; w.on('test', listener); assert.equal(w.listenerCount('test'), 1); w.removeListener('test', listener); - return assert.equal(w.listenerCount('test'), 0); + assert.equal(w.listenerCount('test'), 0); }); }); }); diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index 5771358337e..b27840ec4f2 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -1,27 +1,26 @@ -var Menu, MenuItem, assert, ipcRenderer, ref, ref1, remote; +const assert = require('assert'); -assert = require('assert'); +const remote = require('electron').remote; +const ipcRenderer = require('electron').ipcRenderer; -ref = require('electron'), remote = ref.remote, ipcRenderer = ref.ipcRenderer; - -ref1 = remote.require('electron'), Menu = ref1.Menu, MenuItem = ref1.MenuItem; +const Menu = remote.require('electron').Menu; +const MenuItem = remote.require('electron').MenuItem; describe('menu module', function() { describe('Menu.buildFromTemplate', function() { it('should be able to attach extra fields', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { label: 'text', extra: 'field' } ]); - return assert.equal(menu.items[0].extra, 'field'); + assert.equal(menu.items[0].extra, 'field'); }); + it('does not modify the specified template', function() { - var template; - template = ipcRenderer.sendSync('eval', "var template = [{label: 'text', submenu: [{label: 'sub'}]}];\nrequire('electron').Menu.buildFromTemplate(template);\ntemplate;"); - return assert.deepStrictEqual(template, [ + var template = ipcRenderer.sendSync('eval', "var template = [{label: 'text', submenu: [{label: 'sub'}]}];\nrequire('electron').Menu.buildFromTemplate(template);\ntemplate;"); + assert.deepStrictEqual(template, [ { label: 'text', submenu: [ @@ -32,10 +31,10 @@ describe('menu module', function() { } ]); }); - return describe('Menu.buildFromTemplate should reorder based on item position specifiers', function() { + + describe('Menu.buildFromTemplate should reorder based on item position specifiers', function() { it('should position before existing item', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { label: '2', id: '2' @@ -50,11 +49,11 @@ describe('menu module', function() { ]); assert.equal(menu.items[0].label, '1'); assert.equal(menu.items[1].label, '2'); - return assert.equal(menu.items[2].label, '3'); + assert.equal(menu.items[2].label, '3'); }); + it('should position after existing item', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { label: '1', id: '1' @@ -69,11 +68,11 @@ describe('menu module', function() { ]); assert.equal(menu.items[0].label, '1'); assert.equal(menu.items[1].label, '2'); - return assert.equal(menu.items[2].label, '3'); + assert.equal(menu.items[2].label, '3'); }); + it('should position at endof existing separator groups', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { type: 'separator', id: 'numbers' @@ -113,11 +112,11 @@ describe('menu module', function() { assert.equal(menu.items[4].id, 'letters'); assert.equal(menu.items[5].label, 'a'); assert.equal(menu.items[6].label, 'b'); - return assert.equal(menu.items[7].label, 'c'); + assert.equal(menu.items[7].label, 'c'); }); + it('should create separator group if endof does not reference existing separator group', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { label: 'a', id: 'a', @@ -151,11 +150,11 @@ describe('menu module', function() { assert.equal(menu.items[4].id, 'numbers'); assert.equal(menu.items[5].label, '1'); assert.equal(menu.items[6].label, '2'); - return assert.equal(menu.items[7].label, '3'); + assert.equal(menu.items[7].label, '3'); }); - return it('should continue inserting items at next index when no specifier is present', function() { - var menu; - menu = Menu.buildFromTemplate([ + + it('should continue inserting items at next index when no specifier is present', function() { + var menu = Menu.buildFromTemplate([ { label: '4', id: '4' @@ -178,14 +177,14 @@ describe('menu module', function() { assert.equal(menu.items[1].label, '2'); assert.equal(menu.items[2].label, '3'); assert.equal(menu.items[3].label, '4'); - return assert.equal(menu.items[4].label, '5'); + assert.equal(menu.items[4].label, '5'); }); }); }); + describe('Menu.insert', function() { - return it('should store item in @items by its index', function() { - var item, menu; - menu = Menu.buildFromTemplate([ + it('should store item in @items by its index', function() { + var menu = Menu.buildFromTemplate([ { label: '1' }, { @@ -194,36 +193,36 @@ describe('menu module', function() { label: '3' } ]); - item = new MenuItem({ + var item = new MenuItem({ label: 'inserted' }); menu.insert(1, item); assert.equal(menu.items[0].label, '1'); assert.equal(menu.items[1].label, 'inserted'); assert.equal(menu.items[2].label, '2'); - return assert.equal(menu.items[3].label, '3'); + assert.equal(menu.items[3].label, '3'); }); }); + describe('MenuItem.click', function() { - return it('should be called with the item object passed', function(done) { - var menu; - menu = Menu.buildFromTemplate([ + it('should be called with the item object passed', function(done) { + var menu = Menu.buildFromTemplate([ { label: 'text', click: function(item) { assert.equal(item.constructor.name, 'MenuItem'); assert.equal(item.label, 'text'); - return done(); + done(); } } ]); - return menu.delegate.executeCommand(menu.items[0].commandId); + menu.delegate.executeCommand(menu.items[0].commandId); }); }); - return describe('MenuItem with checked property', function() { + + describe('MenuItem with checked property', function() { it('clicking an checkbox item should flip the checked property', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { label: 'text', type: 'checkbox' @@ -231,11 +230,11 @@ describe('menu module', function() { ]); assert.equal(menu.items[0].checked, false); menu.delegate.executeCommand(menu.items[0].commandId); - return assert.equal(menu.items[0].checked, true); + assert.equal(menu.items[0].checked, true); }); + it('clicking an radio item should always make checked property true', function() { - var menu; - menu = Menu.buildFromTemplate([ + var menu = Menu.buildFromTemplate([ { label: 'text', type: 'radio' @@ -244,8 +243,9 @@ describe('menu module', function() { menu.delegate.executeCommand(menu.items[0].commandId); assert.equal(menu.items[0].checked, true); menu.delegate.executeCommand(menu.items[0].commandId); - return assert.equal(menu.items[0].checked, true); + assert.equal(menu.items[0].checked, true); }); + it('at least have one item checked in each group', function() { var i, j, k, menu, template; template = []; @@ -267,10 +267,11 @@ describe('menu module', function() { menu = Menu.buildFromTemplate(template); menu.delegate.menuWillShow(); assert.equal(menu.items[0].checked, true); - return assert.equal(menu.items[12].checked, true); + assert.equal(menu.items[12].checked, true); }); + it('should assign groupId automatically', function() { - var groupId, i, j, k, l, m, menu, results, template; + var groupId, i, j, k, l, m, menu, template; template = []; for (i = j = 0; j <= 10; i = ++j) { template.push({ @@ -292,14 +293,13 @@ describe('menu module', function() { for (i = l = 0; l <= 10; i = ++l) { assert.equal(menu.items[i].groupId, groupId); } - results = []; for (i = m = 12; m <= 20; i = ++m) { - results.push(assert.equal(menu.items[i].groupId, groupId + 1)); + assert.equal(menu.items[i].groupId, groupId + 1); } - return results; }); - return it("setting 'checked' should flip other items' 'checked' property", function() { - var i, j, k, l, m, menu, n, o, p, q, results, template; + + it("setting 'checked' should flip other items' 'checked' property", function() { + var i, j, k, l, m, menu, n, o, p, q, template; template = []; for (i = j = 0; j <= 10; i = ++j) { template.push({ @@ -339,11 +339,9 @@ describe('menu module', function() { assert.equal(menu.items[i].checked, false); } assert.equal(menu.items[12].checked, true); - results = []; for (i = q = 13; q <= 20; i = ++q) { - results.push(assert.equal(menu.items[i].checked, false)); + assert.equal(menu.items[i].checked, false); } - return results; }); }); }); diff --git a/spec/api-protocol-spec.js b/spec/api-protocol-spec.js index 284033b967d..215868bfdc8 100644 --- a/spec/api-protocol-spec.js +++ b/spec/api-protocol-spec.js @@ -6,66 +6,68 @@ const remote = require('electron').remote; const protocol = remote.require('electron').protocol; describe('protocol module', function() { - var postData, protocolName, text; - protocolName = 'sp'; - text = 'valar morghulis'; - postData = { + var protocolName = 'sp'; + var text = 'valar morghulis'; + var postData = { name: 'post test', type: 'string' }; + afterEach(function(done) { - return protocol.unregisterProtocol(protocolName, function() { - return protocol.uninterceptProtocol('http', function() { - return done(); + protocol.unregisterProtocol(protocolName, function() { + protocol.uninterceptProtocol('http', function() { + done(); }); }); }); + describe('protocol.register(Any)Protocol', function() { - var emptyHandler; - emptyHandler = function(request, callback) { - return callback(); + var emptyHandler = function(request, callback) { + callback(); }; + it('throws error when scheme is already registered', function(done) { - return protocol.registerStringProtocol(protocolName, emptyHandler, function(error) { + protocol.registerStringProtocol(protocolName, emptyHandler, function(error) { assert.equal(error, null); - return protocol.registerBufferProtocol(protocolName, emptyHandler, function(error) { + protocol.registerBufferProtocol(protocolName, emptyHandler, function(error) { assert.notEqual(error, null); - return done(); + done(); }); }); }); + it('does not crash when handler is called twice', function(done) { - var doubleHandler; - doubleHandler = function(request, callback) { + var doubleHandler = function(request, callback) { try { callback(text); - return callback(); + callback(); } catch (error) { // Ignore error } }; - return protocol.registerStringProtocol(protocolName, doubleHandler, function(error) { + protocol.registerStringProtocol(protocolName, doubleHandler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('sends error when callback is called with nothing', function(done) { - return protocol.registerBufferProtocol(protocolName, emptyHandler, function(error) { + protocol.registerBufferProtocol(protocolName, emptyHandler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { return done('request succeeded but it should not'); @@ -77,236 +79,238 @@ describe('protocol module', function() { }); }); }); - return it('does not crash when callback is called in next tick', function(done) { - var handler; - handler = function(request, callback) { - return setImmediate(function() { - return callback(text); + + it('does not crash when callback is called in next tick', function(done) { + var handler = function(request, callback) { + setImmediate(function() { + callback(text); }); }; - return protocol.registerStringProtocol(protocolName, handler, function(error) { + protocol.registerStringProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); }); + describe('protocol.unregisterProtocol', function() { - return it('returns error when scheme does not exist', function(done) { - return protocol.unregisterProtocol('not-exist', function(error) { + it('returns error when scheme does not exist', function(done) { + protocol.unregisterProtocol('not-exist', function(error) { assert.notEqual(error, null); - return done(); + done(); }); }); }); + describe('protocol.registerStringProtocol', function() { it('sends string as response', function(done) { - var handler; - handler = function(request, callback) { - return callback(text); + var handler = function(request, callback) { + callback(text); }; - return protocol.registerStringProtocol(protocolName, handler, function(error) { + protocol.registerStringProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('sets Access-Control-Allow-Origin', function(done) { - var handler; - handler = function(request, callback) { - return callback(text); + var handler = function(request, callback) { + callback(text); }; - return protocol.registerStringProtocol(protocolName, handler, function(error) { + protocol.registerStringProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data, status, request) { assert.equal(data, text); assert.equal(request.getResponseHeader('Access-Control-Allow-Origin'), '*'); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('sends object as response', function(done) { - var handler; - handler = function(request, callback) { - return callback({ + var handler = function(request, callback) { + callback({ data: text, mimeType: 'text/html' }); }; - return protocol.registerStringProtocol(protocolName, handler, function(error) { + protocol.registerStringProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); - return it('fails when sending object other than string', function(done) { - var handler; - handler = function(request, callback) { - return callback(new Date); + + it('fails when sending object other than string', function(done) { + var handler = function(request, callback) { + callback(new Date); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); }); + describe('protocol.registerBufferProtocol', function() { - var buffer; - buffer = new Buffer(text); + var buffer = new Buffer(text); + it('sends Buffer as response', function(done) { - var handler; - handler = function(request, callback) { - return callback(buffer); + var handler = function(request, callback) { + callback(buffer); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('sets Access-Control-Allow-Origin', function(done) { - var handler; - handler = function(request, callback) { - return callback(buffer); + var handler = function(request, callback) { + callback(buffer); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data, status, request) { assert.equal(data, text); assert.equal(request.getResponseHeader('Access-Control-Allow-Origin'), '*'); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('sends object as response', function(done) { - var handler; - handler = function(request, callback) { - return callback({ + var handler = function(request, callback) { + callback({ data: buffer, mimeType: 'text/html' }); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); - return it('fails when sending string', function(done) { - var handler; - handler = function(request, callback) { - return callback(text); + + it('fails when sending string', function(done) { + var handler = function(request, callback) { + callback(text); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); }); + describe('protocol.registerFileProtocol', function() { - var fileContent, filePath, normalContent, normalPath; - filePath = path.join(__dirname, 'fixtures', 'asar', 'a.asar', 'file1'); - fileContent = require('fs').readFileSync(filePath); - normalPath = path.join(__dirname, 'fixtures', 'pages', 'a.html'); - normalContent = require('fs').readFileSync(normalPath); + var filePath = path.join(__dirname, 'fixtures', 'asar', 'a.asar', 'file1'); + var fileContent = require('fs').readFileSync(filePath); + var normalPath = path.join(__dirname, 'fixtures', 'pages', 'a.html'); + var normalContent = require('fs').readFileSync(normalPath); + it('sends file path as response', function(done) { - var handler; - handler = function(request, callback) { - return callback(filePath); + var handler = function(request, callback) { + callback(filePath); }; - return protocol.registerFileProtocol(protocolName, handler, function(error) { + protocol.registerFileProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, String(fileContent)); @@ -318,452 +322,452 @@ describe('protocol module', function() { }); }); }); + it('sets Access-Control-Allow-Origin', function(done) { - var handler; - handler = function(request, callback) { - return callback(filePath); + var handler = function(request, callback) { + callback(filePath); }; - return protocol.registerFileProtocol(protocolName, handler, function(error) { + protocol.registerFileProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data, status, request) { assert.equal(data, String(fileContent)); assert.equal(request.getResponseHeader('Access-Control-Allow-Origin'), '*'); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); it('sends object as response', function(done) { - var handler; - handler = function(request, callback) { - return callback({ + var handler = function(request, callback) { + callback({ path: filePath }); }; - return protocol.registerFileProtocol(protocolName, handler, function(error) { + protocol.registerFileProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, String(fileContent)); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('can send normal file', function(done) { - var handler; - handler = function(request, callback) { - return callback(normalPath); + var handler = function(request, callback) { + callback(normalPath); }; - return protocol.registerFileProtocol(protocolName, handler, function(error) { + + protocol.registerFileProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, String(normalContent)); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('fails when sending unexist-file', function(done) { - var fakeFilePath, handler; - fakeFilePath = path.join(__dirname, 'fixtures', 'asar', 'a.asar', 'not-exist'); - handler = function(request, callback) { - return callback(fakeFilePath); + var fakeFilePath = path.join(__dirname, 'fixtures', 'asar', 'a.asar', 'not-exist'); + var handler = function(request, callback) { + callback(fakeFilePath); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); - return it('fails when sending unsupported content', function(done) { - var handler; - handler = function(request, callback) { - return callback(new Date); + + it('fails when sending unsupported content', function(done) { + var handler = function(request, callback) { + callback(new Date); }; - return protocol.registerBufferProtocol(protocolName, handler, function(error) { + protocol.registerBufferProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); }); + describe('protocol.registerHttpProtocol', function() { it('sends url as response', function(done) { - var server; - server = http.createServer(function(req, res) { + var server = http.createServer(function(req, res) { assert.notEqual(req.headers.accept, ''); res.end(text); - return server.close(); + server.close(); }); - return server.listen(0, '127.0.0.1', function() { - var handler, port, url; - port = server.address().port; - url = "http://127.0.0.1:" + port; - handler = function(request, callback) { - return callback({ + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; + var url = "http://127.0.0.1:" + port; + var handler = function(request, callback) { + callback({ url: url }); }; - return protocol.registerHttpProtocol(protocolName, handler, function(error) { + protocol.registerHttpProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); }); + it('fails when sending invalid url', function(done) { - var handler; - handler = function(request, callback) { - return callback({ + var handler = function(request, callback) { + callback({ url: 'url' }); }; - return protocol.registerHttpProtocol(protocolName, handler, function(error) { + protocol.registerHttpProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); - return it('fails when sending unsupported content', function(done) { - var handler; - handler = function(request, callback) { - return callback(new Date); + + it('fails when sending unsupported content', function(done) { + var handler = function(request, callback) { + callback(new Date); }; - return protocol.registerHttpProtocol(protocolName, handler, function(error) { + protocol.registerHttpProtocol(protocolName, handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: protocolName + "://fake-host", success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); }); + describe('protocol.isProtocolHandled', function() { it('returns true for file:', function(done) { - return protocol.isProtocolHandled('file', function(result) { + protocol.isProtocolHandled('file', function(result) { assert.equal(result, true); - return done(); + done(); }); }); + it('returns true for http:', function(done) { - return protocol.isProtocolHandled('http', function(result) { + protocol.isProtocolHandled('http', function(result) { assert.equal(result, true); - return done(); + done(); }); }); + it('returns true for https:', function(done) { - return protocol.isProtocolHandled('https', function(result) { + protocol.isProtocolHandled('https', function(result) { assert.equal(result, true); - return done(); + done(); }); }); + it('returns false when scheme is not registred', function(done) { - return protocol.isProtocolHandled('no-exist', function(result) { + protocol.isProtocolHandled('no-exist', function(result) { assert.equal(result, false); - return done(); + done(); }); }); + it('returns true for custom protocol', function(done) { - var emptyHandler; - emptyHandler = function(request, callback) { - return callback(); + var emptyHandler = function(request, callback) { + callback(); }; - return protocol.registerStringProtocol(protocolName, emptyHandler, function(error) { + protocol.registerStringProtocol(protocolName, emptyHandler, function(error) { assert.equal(error, null); - return protocol.isProtocolHandled(protocolName, function(result) { + protocol.isProtocolHandled(protocolName, function(result) { assert.equal(result, true); - return done(); + done(); }); }); }); - return it('returns true for intercepted protocol', function(done) { - var emptyHandler; - emptyHandler = function(request, callback) { - return callback(); + + it('returns true for intercepted protocol', function(done) { + var emptyHandler = function(request, callback) { + callback(); }; - return protocol.interceptStringProtocol('http', emptyHandler, function(error) { + protocol.interceptStringProtocol('http', emptyHandler, function(error) { assert.equal(error, null); - return protocol.isProtocolHandled('http', function(result) { + protocol.isProtocolHandled('http', function(result) { assert.equal(result, true); - return done(); + done(); }); }); }); }); + describe('protocol.intercept(Any)Protocol', function() { - var emptyHandler; - emptyHandler = function(request, callback) { - return callback(); + var emptyHandler = function(request, callback) { + callback(); }; + it('throws error when scheme is already intercepted', function(done) { - return protocol.interceptStringProtocol('http', emptyHandler, function(error) { + protocol.interceptStringProtocol('http', emptyHandler, function(error) { assert.equal(error, null); - return protocol.interceptBufferProtocol('http', emptyHandler, function(error) { + protocol.interceptBufferProtocol('http', emptyHandler, function(error) { assert.notEqual(error, null); - return done(); + done(); }); }); }); + it('does not crash when handler is called twice', function(done) { - var doubleHandler; - doubleHandler = function(request, callback) { + var doubleHandler = function(request, callback) { try { callback(text); - return callback(); + callback(); } catch (error) { // Ignore error } }; - return protocol.interceptStringProtocol('http', doubleHandler, function(error) { + protocol.interceptStringProtocol('http', doubleHandler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: 'http://fake-host', success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); - return it('sends error when callback is called with nothing', function(done) { + + it('sends error when callback is called with nothing', function(done) { if (process.env.TRAVIS === 'true') { return done(); } - return protocol.interceptBufferProtocol('http', emptyHandler, function(error) { + protocol.interceptBufferProtocol('http', emptyHandler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: 'http://fake-host', success: function() { - return done('request succeeded but it should not'); + done('request succeeded but it should not'); }, error: function(xhr, errorType) { assert.equal(errorType, 'error'); - return done(); + done(); } }); }); }); }); + describe('protocol.interceptStringProtocol', function() { it('can intercept http protocol', function(done) { - var handler; - handler = function(request, callback) { - return callback(text); + var handler = function(request, callback) { + callback(text); }; - return protocol.interceptStringProtocol('http', handler, function(error) { + protocol.interceptStringProtocol('http', handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: 'http://fake-host', success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); + it('can set content-type', function(done) { - var handler; - handler = function(request, callback) { - return callback({ + var handler = function(request, callback) { + callback({ mimeType: 'application/json', data: '{"value": 1}' }); }; - return protocol.interceptStringProtocol('http', handler, function(error) { + protocol.interceptStringProtocol('http', handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: 'http://fake-host', success: function(data) { assert.equal(typeof data, 'object'); assert.equal(data.value, 1); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); - return it('can receive post data', function(done) { - var handler; - handler = function(request, callback) { - var uploadData; - uploadData = request.uploadData[0].bytes.toString(); - return callback({ + + it('can receive post data', function(done) { + var handler = function(request, callback) { + var uploadData = request.uploadData[0].bytes.toString(); + callback({ data: uploadData }); }; - return protocol.interceptStringProtocol('http', handler, function(error) { + protocol.interceptStringProtocol('http', handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: "http://fake-host", type: "POST", data: postData, success: function(data) { assert.deepEqual(qs.parse(data), postData); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); }); + describe('protocol.interceptBufferProtocol', function() { it('can intercept http protocol', function(done) { - var handler; - handler = function(request, callback) { - return callback(new Buffer(text)); + var handler = function(request, callback) { + callback(new Buffer(text)); }; - return protocol.interceptBufferProtocol('http', handler, function(error) { + protocol.interceptBufferProtocol('http', handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: 'http://fake-host', success: function(data) { assert.equal(data, text); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); - return it('can receive post data', function(done) { - var handler; - handler = function(request, callback) { - var uploadData; - uploadData = request.uploadData[0].bytes; - return callback(uploadData); + + it('can receive post data', function(done) { + var handler = function(request, callback) { + var uploadData = request.uploadData[0].bytes; + callback(uploadData); }; - return protocol.interceptBufferProtocol('http', handler, function(error) { + protocol.interceptBufferProtocol('http', handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: "http://fake-host", type: "POST", data: postData, success: function(data) { assert.equal(data, $.param(postData)); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); }); + describe('protocol.interceptHttpProtocol', function() { - return it('can send POST request', function(done) { - var server; - server = http.createServer(function(req, res) { - var body; - body = ''; + it('can send POST request', function(done) { + var server = http.createServer(function(req, res) { + var body = ''; req.on('data', function(chunk) { - return body += chunk; + body += chunk; }); req.on('end', function() { - return res.end(body); + res.end(body); }); - return server.close(); + server.close(); }); - return server.listen(0, '127.0.0.1', function() { - var handler, port, url; - port = server.address().port; - url = "http://127.0.0.1:" + port; - handler = function(request, callback) { - var data; - data = { + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; + var url = "http://127.0.0.1:" + port; + var handler = function(request, callback) { + var data = { url: url, method: 'POST', uploadData: { @@ -772,39 +776,41 @@ describe('protocol module', function() { }, session: null }; - return callback(data); + callback(data); }; - return protocol.interceptHttpProtocol('http', handler, function(error) { + protocol.interceptHttpProtocol('http', handler, function(error) { if (error) { return done(error); } - return $.ajax({ + $.ajax({ url: "http://fake-host", type: "POST", data: postData, success: function(data) { assert.deepEqual(qs.parse(data), postData); - return done(); + done(); }, error: function(xhr, errorType, error) { - return done(error); + done(error); } }); }); }); }); }); - return describe('protocol.uninterceptProtocol', function() { + + describe('protocol.uninterceptProtocol', function() { it('returns error when scheme does not exist', function(done) { - return protocol.uninterceptProtocol('not-exist', function(error) { + protocol.uninterceptProtocol('not-exist', function(error) { assert.notEqual(error, null); - return done(); + done(); }); }); - return it('returns error when scheme is not intercepted', function(done) { - return protocol.uninterceptProtocol('http', function(error) { + + it('returns error when scheme is not intercepted', function(done) { + protocol.uninterceptProtocol('http', function(error) { assert.notEqual(error, null); - return done(); + done(); }); }); }); diff --git a/spec/api-screen-spec.js b/spec/api-screen-spec.js index b393d4b99ee..34828e863e8 100644 --- a/spec/api-screen-spec.js +++ b/spec/api-screen-spec.js @@ -1,25 +1,21 @@ -var assert, screen; - -assert = require('assert'); - -screen = require('electron').screen; +const assert = require('assert'); +const screen = require('electron').screen; describe('screen module', function() { describe('screen.getCursorScreenPoint()', function() { - return it('returns a point object', function() { - var point; - point = screen.getCursorScreenPoint(); + it('returns a point object', function() { + var point = screen.getCursorScreenPoint(); assert.equal(typeof point.x, 'number'); - return assert.equal(typeof point.y, 'number'); + assert.equal(typeof point.y, 'number'); }); }); - return describe('screen.getPrimaryDisplay()', function() { - return it('returns a display object', function() { - var display; - display = screen.getPrimaryDisplay(); + + describe('screen.getPrimaryDisplay()', function() { + it('returns a display object', function() { + var display = screen.getPrimaryDisplay(); assert.equal(typeof display.scaleFactor, 'number'); assert(display.size.width > 0); - return assert(display.size.height > 0); + assert(display.size.height > 0); }); }); }); diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index fe27f932cee..9fedcc29e77 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -11,36 +11,36 @@ const session = remote.session; const BrowserWindow = remote.BrowserWindow; describe('session module', function() { - var fixtures, url, w; this.timeout(10000); - fixtures = path.resolve(__dirname, 'fixtures'); - w = null; - url = "http://127.0.0.1"; + + var fixtures = path.resolve(__dirname, 'fixtures'); + var w = null; + var url = "http://127.0.0.1"; + beforeEach(function() { - return w = new BrowserWindow({ + w = new BrowserWindow({ show: false, width: 400, height: 400 }); }); + afterEach(function() { - return w.destroy(); + w.destroy(); }); describe('session.cookies', function() { it('should get cookies', function(done) { - var server; - server = http.createServer(function(req, res) { + var server = http.createServer(function(req, res) { res.setHeader('Set-Cookie', ['0=0']); res.end('finished'); - return server.close(); + server.close(); }); - return server.listen(0, '127.0.0.1', function() { - var port; - port = server.address().port; + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; w.loadURL(url + ":" + port); - return w.webContents.on('did-finish-load', function() { - return w.webContents.session.cookies.get({ + w.webContents.on('did-finish-load', function() { + w.webContents.session.cookies.get({ url: url }, function(error, list) { var cookie, i, len; @@ -57,13 +57,14 @@ describe('session module', function() { } } } - return done('Can not find cookie'); + done('Can not find cookie'); }); }); }); }); + it('should over-write the existent cookie', function(done) { - return session.defaultSession.cookies.set({ + session.defaultSession.cookies.set({ url: url, name: '1', value: '1' @@ -71,7 +72,7 @@ describe('session module', function() { if (error) { return done(error); } - return session.defaultSession.cookies.get({ + session.defaultSession.cookies.get({ url: url }, function(error, list) { var cookie, i, len; @@ -88,12 +89,13 @@ describe('session module', function() { } } } - return done('Can not find cookie'); + done('Can not find cookie'); }); }); }); + it('should remove cookies', function(done) { - return session.defaultSession.cookies.set({ + session.defaultSession.cookies.set({ url: url, name: '2', value: '2' @@ -101,8 +103,8 @@ describe('session module', function() { if (error) { return done(error); } - return session.defaultSession.cookies.remove(url, '2', function() { - return session.defaultSession.cookies.get({ + session.defaultSession.cookies.remove(url, '2', function() { + session.defaultSession.cookies.get({ url: url }, function(error, list) { var cookie, i, len; @@ -115,7 +117,7 @@ describe('session module', function() { return done('Cookie not deleted'); } } - return done(); + done(); }); }); }); @@ -124,22 +126,21 @@ describe('session module', function() { describe('session.clearStorageData(options)', function() { fixtures = path.resolve(__dirname, 'fixtures'); - return it('clears localstorage data', function(done) { + it('clears localstorage data', function(done) { ipcMain.on('count', function(event, count) { ipcMain.removeAllListeners('count'); assert(!count); - return done(); + done(); }); w.loadURL('file://' + path.join(fixtures, 'api', 'localstorage.html')); - return w.webContents.on('did-finish-load', function() { - var options; - options = { + w.webContents.on('did-finish-load', function() { + var options = { origin: "file://", storages: ['localstorage'], quotas: ['persistent'] }; - return w.webContents.session.clearStorageData(options, function() { - return w.webContents.send('getcount'); + w.webContents.session.clearStorageData(options, function() { + w.webContents.send('getcount'); }); }); }); @@ -155,8 +156,9 @@ describe('session module', function() { height: 400 }); }); + afterEach(function() { - return w.destroy(); + w.destroy(); }); it('can cancel default download behavior', function(done) { @@ -188,21 +190,20 @@ describe('session module', function() { }); }); - return describe('DownloadItem', function() { - var assertDownload, contentDisposition, downloadFilePath, downloadServer, mockPDF; - mockPDF = new Buffer(1024 * 1024 * 5); - contentDisposition = 'inline; filename="mock.pdf"'; - downloadFilePath = path.join(fixtures, 'mock.pdf'); - downloadServer = http.createServer(function(req, res) { + describe('DownloadItem', function() { + var mockPDF = new Buffer(1024 * 1024 * 5); + var contentDisposition = 'inline; filename="mock.pdf"'; + var downloadFilePath = path.join(fixtures, 'mock.pdf'); + var downloadServer = http.createServer(function(req, res) { res.writeHead(200, { 'Content-Length': mockPDF.length, 'Content-Type': 'application/pdf', 'Content-Disposition': contentDisposition }); res.end(mockPDF); - return downloadServer.close(); + downloadServer.close(); }); - assertDownload = function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port) { + var assertDownload = function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port) { assert.equal(state, 'completed'); assert.equal(filename, 'mock.pdf'); assert.equal(url, "http://127.0.0.1:" + port + "/"); @@ -211,52 +212,52 @@ describe('session module', function() { assert.equal(totalBytes, mockPDF.length); assert.equal(disposition, contentDisposition); assert(fs.existsSync(downloadFilePath)); - return fs.unlinkSync(downloadFilePath); + fs.unlinkSync(downloadFilePath); }; + it('can download using BrowserWindow.loadURL', function(done) { - return downloadServer.listen(0, '127.0.0.1', function() { - var port; - port = downloadServer.address().port; + downloadServer.listen(0, '127.0.0.1', function() { + var port = downloadServer.address().port; ipcRenderer.sendSync('set-download-option', false, false); w.loadURL(url + ":" + port); - return ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) { + ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) { assertDownload(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port); - return done(); + done(); }); }); }); + it('can download using WebView.downloadURL', function(done) { - return downloadServer.listen(0, '127.0.0.1', function() { - var port, webview; - port = downloadServer.address().port; + downloadServer.listen(0, '127.0.0.1', function() { + var port = downloadServer.address().port; ipcRenderer.sendSync('set-download-option', false, false); - webview = new WebView; + var webview = new WebView; webview.src = "file://" + fixtures + "/api/blank.html"; webview.addEventListener('did-finish-load', function() { - return webview.downloadURL(url + ":" + port + "/"); + webview.downloadURL(url + ":" + port + "/"); }); ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) { assertDownload(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port); document.body.removeChild(webview); - return done(); + done(); }); - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + it('can cancel download', function(done) { - return downloadServer.listen(0, '127.0.0.1', function() { - var port; - port = downloadServer.address().port; + downloadServer.listen(0, '127.0.0.1', function() { + var port = downloadServer.address().port; ipcRenderer.sendSync('set-download-option', true, false); w.loadURL(url + ":" + port + "/"); - return ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) { + ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) { assert.equal(state, 'cancelled'); assert.equal(filename, 'mock.pdf'); assert.equal(mimeType, 'application/pdf'); assert.equal(receivedBytes, 0); assert.equal(totalBytes, mockPDF.length); assert.equal(disposition, contentDisposition); - return done(); + done(); }); }); }); diff --git a/spec/api-web-frame-spec.js b/spec/api-web-frame-spec.js index 3d287a6acba..15f31aa8f09 100644 --- a/spec/api-web-frame-spec.js +++ b/spec/api-web-frame-spec.js @@ -1,24 +1,18 @@ -var assert, path, webFrame; - -assert = require('assert'); - -path = require('path'); - -webFrame = require('electron').webFrame; +const assert = require('assert'); +const path = require('path'); +const webFrame = require('electron').webFrame; describe('webFrame module', function() { - var fixtures; - fixtures = path.resolve(__dirname, 'fixtures'); - return describe('webFrame.registerURLSchemeAsPrivileged', function() { - return it('supports fetch api', function(done) { - var url; + var fixtures = path.resolve(__dirname, 'fixtures'); + describe('webFrame.registerURLSchemeAsPrivileged', function() { + it('supports fetch api', function(done) { webFrame.registerURLSchemeAsPrivileged('file'); - url = "file://" + fixtures + "/assets/logo.png"; - return fetch(url).then(function(response) { + var url = "file://" + fixtures + "/assets/logo.png"; + fetch(url).then(function(response) { assert(response.ok); - return done(); - })["catch"](function(err) { - return done('unexpected error : ' + err); + done(); + }).catch(function(err) { + done('unexpected error : ' + err); }); }); }); diff --git a/spec/api-web-request-spec.js b/spec/api-web-request-spec.js index 91b84c77e1b..77f160a2004 100644 --- a/spec/api-web-request-spec.js +++ b/spec/api-web-request-spec.js @@ -5,78 +5,80 @@ const remote = require('electron').remote; const session = remote.session; describe('webRequest module', function() { - var defaultURL, server, ses; - ses = session.defaultSession; - server = http.createServer(function(req, res) { - var content; + var ses = session.defaultSession; + var server = http.createServer(function(req, res) { res.setHeader('Custom', ['Header']); - content = req.url; + var content = req.url; if (req.headers.accept === '*/*;test/header') { content += 'header/received'; } - return res.end(content); + res.end(content); }); - defaultURL = null; + var defaultURL = null; + before(function(done) { - return server.listen(0, '127.0.0.1', function() { - var port; - port = server.address().port; + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; defaultURL = "http://127.0.0.1:" + port + "/"; - return done(); + done(); }); }); + after(function() { - return server.close(); + server.close(); }); + describe('webRequest.onBeforeRequest', function() { afterEach(function() { - return ses.webRequest.onBeforeRequest(null); + ses.webRequest.onBeforeRequest(null); }); + it('can cancel the request', function(done) { ses.webRequest.onBeforeRequest(function(details, callback) { - return callback({ + callback({ cancel: true }); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function() { - return done('unexpected success'); + done('unexpected success'); }, error: function() { - return done(); + done(); } }); }); + it('can filter URLs', function(done) { - var filter; - filter = { + var filter = { urls: [defaultURL + "filter/*"] }; ses.webRequest.onBeforeRequest(filter, function(details, callback) { - return callback({ + callback({ cancel: true }); }); - return $.ajax({ + $.ajax({ url: defaultURL + "nofilter/test", success: function(data) { assert.equal(data, '/nofilter/test'); - return $.ajax({ + $.ajax({ url: defaultURL + "filter/test", success: function() { - return done('unexpected success'); + done('unexpected success'); }, error: function() { - return done(); + done(); } }); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); + it('receives details object', function(done) { ses.webRequest.onBeforeRequest(function(details, callback) { assert.equal(typeof details.id, 'number'); @@ -85,36 +87,36 @@ describe('webRequest module', function() { assert.equal(details.method, 'GET'); assert.equal(details.resourceType, 'xhr'); assert(!details.uploadData); - return callback({}); + callback({}); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); + it('receives post data in details object', function(done) { var postData = { name: 'post test', type: 'string' }; ses.webRequest.onBeforeRequest(function(details, callback) { - var data; assert.equal(details.url, defaultURL); assert.equal(details.method, 'POST'); assert.equal(details.uploadData.length, 1); - data = qs.parse(details.uploadData[0].bytes.toString()); + var data = qs.parse(details.uploadData[0].bytes.toString()); assert.deepEqual(data, postData); - return callback({ + callback({ cancel: true }); }); - return $.ajax({ + $.ajax({ url: defaultURL, type: 'POST', data: postData, @@ -125,270 +127,285 @@ describe('webRequest module', function() { } }); }); - return it('can redirect the request', function(done) { + + it('can redirect the request', function(done) { ses.webRequest.onBeforeRequest(function(details, callback) { if (details.url === defaultURL) { - return callback({ + callback({ redirectURL: defaultURL + "redirect" }); } else { - return callback({}); + callback({}); } }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/redirect'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); + describe('webRequest.onBeforeSendHeaders', function() { afterEach(function() { - return ses.webRequest.onBeforeSendHeaders(null); + ses.webRequest.onBeforeSendHeaders(null); }); + it('receives details object', function(done) { ses.webRequest.onBeforeSendHeaders(function(details, callback) { assert.equal(typeof details.requestHeaders, 'object'); - return callback({}); + callback({}); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); + it('can change the request headers', function(done) { ses.webRequest.onBeforeSendHeaders(function(details, callback) { - var requestHeaders; - requestHeaders = details.requestHeaders; + var requestHeaders = details.requestHeaders; requestHeaders.Accept = '*/*;test/header'; - return callback({ + callback({ requestHeaders: requestHeaders }); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/header/received'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); - return it('resets the whole headers', function(done) { - var requestHeaders; - requestHeaders = { + + it('resets the whole headers', function(done) { + var requestHeaders = { Test: 'header' }; ses.webRequest.onBeforeSendHeaders(function(details, callback) { - return callback({ + callback({ requestHeaders: requestHeaders }); }); ses.webRequest.onSendHeaders(function(details) { assert.deepEqual(details.requestHeaders, requestHeaders); - return done(); + done(); }); - return $.ajax({ + $.ajax({ url: defaultURL, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); + describe('webRequest.onSendHeaders', function() { afterEach(function() { - return ses.webRequest.onSendHeaders(null); + ses.webRequest.onSendHeaders(null); }); - return it('receives details object', function(done) { + + it('receives details object', function(done) { ses.webRequest.onSendHeaders(function(details) { - return assert.equal(typeof details.requestHeaders, 'object'); + assert.equal(typeof details.requestHeaders, 'object'); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); + describe('webRequest.onHeadersReceived', function() { afterEach(function() { - return ses.webRequest.onHeadersReceived(null); + ses.webRequest.onHeadersReceived(null); }); + it('receives details object', function(done) { ses.webRequest.onHeadersReceived(function(details, callback) { assert.equal(details.statusLine, 'HTTP/1.1 200 OK'); assert.equal(details.statusCode, 200); assert.equal(details.responseHeaders['Custom'], 'Header'); - return callback({}); + callback({}); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); + it('can change the response header', function(done) { ses.webRequest.onHeadersReceived(function(details, callback) { - var responseHeaders; - responseHeaders = details.responseHeaders; + var responseHeaders = details.responseHeaders; responseHeaders['Custom'] = ['Changed']; - return callback({ + callback({ responseHeaders: responseHeaders }); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data, status, xhr) { assert.equal(xhr.getResponseHeader('Custom'), 'Changed'); assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); - return it('does not change header by default', function(done) { + + it('does not change header by default', function(done) { ses.webRequest.onHeadersReceived(function(details, callback) { - return callback({}); + callback({}); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data, status, xhr) { assert.equal(xhr.getResponseHeader('Custom'), 'Header'); assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); + describe('webRequest.onResponseStarted', function() { afterEach(function() { - return ses.webRequest.onResponseStarted(null); + ses.webRequest.onResponseStarted(null); }); - return it('receives details object', function(done) { + + it('receives details object', function(done) { ses.webRequest.onResponseStarted(function(details) { assert.equal(typeof details.fromCache, 'boolean'); assert.equal(details.statusLine, 'HTTP/1.1 200 OK'); assert.equal(details.statusCode, 200); - return assert.equal(details.responseHeaders['Custom'], 'Header'); + assert.equal(details.responseHeaders['Custom'], 'Header'); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data, status, xhr) { assert.equal(xhr.getResponseHeader('Custom'), 'Header'); assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); + describe('webRequest.onBeforeRedirect', function() { afterEach(function() { ses.webRequest.onBeforeRedirect(null); - return ses.webRequest.onBeforeRequest(null); + ses.webRequest.onBeforeRequest(null); }); - return it('receives details object', function(done) { - var redirectURL; - redirectURL = defaultURL + "redirect"; + + it('receives details object', function(done) { + var redirectURL = defaultURL + "redirect"; ses.webRequest.onBeforeRequest(function(details, callback) { if (details.url === defaultURL) { - return callback({ + callback({ redirectURL: redirectURL }); } else { - return callback({}); + callback({}); } }); ses.webRequest.onBeforeRedirect(function(details) { assert.equal(typeof details.fromCache, 'boolean'); assert.equal(details.statusLine, 'HTTP/1.1 307 Internal Redirect'); assert.equal(details.statusCode, 307); - return assert.equal(details.redirectURL, redirectURL); + assert.equal(details.redirectURL, redirectURL); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/redirect'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); + describe('webRequest.onCompleted', function() { afterEach(function() { - return ses.webRequest.onCompleted(null); + ses.webRequest.onCompleted(null); }); - return it('receives details object', function(done) { + + it('receives details object', function(done) { ses.webRequest.onCompleted(function(details) { assert.equal(typeof details.fromCache, 'boolean'); assert.equal(details.statusLine, 'HTTP/1.1 200 OK'); - return assert.equal(details.statusCode, 200); + assert.equal(details.statusCode, 200); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function(data) { assert.equal(data, '/'); - return done(); + done(); }, error: function(xhr, errorType) { - return done(errorType); + done(errorType); } }); }); }); - return describe('webRequest.onErrorOccurred', function() { + + describe('webRequest.onErrorOccurred', function() { afterEach(function() { ses.webRequest.onErrorOccurred(null); - return ses.webRequest.onBeforeRequest(null); + ses.webRequest.onBeforeRequest(null); }); - return it('receives details object', function(done) { + + it('receives details object', function(done) { ses.webRequest.onBeforeRequest(function(details, callback) { - return callback({ + callback({ cancel: true }); }); ses.webRequest.onErrorOccurred(function(details) { assert.equal(details.error, 'net::ERR_BLOCKED_BY_CLIENT'); - return done(); + done(); }); - return $.ajax({ + $.ajax({ url: defaultURL, success: function() { - return done('unexpected success'); + done('unexpected success'); } }); }); diff --git a/spec/asar-spec.js b/spec/asar-spec.js index 81d881f7eb4..060074390f1 100644 --- a/spec/asar-spec.js +++ b/spec/asar-spec.js @@ -10,8 +10,8 @@ const ipcMain = remote.require('electron').ipcMain; const BrowserWindow = remote.require('electron').BrowserWindow; describe('asar package', function() { - var fixtures; - fixtures = path.join(__dirname, 'fixtures'); + var fixtures = path.join(__dirname, 'fixtures'); + describe('node api', function() { describe('fs.readFileSync', function() { it('does not leak fd', function() { @@ -21,124 +21,124 @@ describe('asar package', function() { readCalls++; } }); + it('reads a normal file', function() { - var file1, file2, file3; - file1 = path.join(fixtures, 'asar', 'a.asar', 'file1'); + var file1 = path.join(fixtures, 'asar', 'a.asar', 'file1'); assert.equal(fs.readFileSync(file1).toString().trim(), 'file1'); - file2 = path.join(fixtures, 'asar', 'a.asar', 'file2'); + var file2 = path.join(fixtures, 'asar', 'a.asar', 'file2'); assert.equal(fs.readFileSync(file2).toString().trim(), 'file2'); - file3 = path.join(fixtures, 'asar', 'a.asar', 'file3'); - return assert.equal(fs.readFileSync(file3).toString().trim(), 'file3'); + var file3 = path.join(fixtures, 'asar', 'a.asar', 'file3'); + assert.equal(fs.readFileSync(file3).toString().trim(), 'file3'); }); + it('reads from a empty file', function() { - var buffer, file; - file = path.join(fixtures, 'asar', 'empty.asar', 'file1'); - buffer = fs.readFileSync(file); + var file = path.join(fixtures, 'asar', 'empty.asar', 'file1'); + var buffer = fs.readFileSync(file); assert.equal(buffer.length, 0); - return assert.equal(buffer.toString(), ''); + assert.equal(buffer.toString(), ''); }); + it('reads a linked file', function() { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link1'); - return assert.equal(fs.readFileSync(p).toString().trim(), 'file1'); + var p = path.join(fixtures, 'asar', 'a.asar', 'link1'); + assert.equal(fs.readFileSync(p).toString().trim(), 'file1'); }); + it('reads a file from linked directory', function() { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'file1'); + var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'file1'); assert.equal(fs.readFileSync(p).toString().trim(), 'file1'); p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); - return assert.equal(fs.readFileSync(p).toString().trim(), 'file1'); + assert.equal(fs.readFileSync(p).toString().trim(), 'file1'); }); + it('throws ENOENT error when can not find file', function() { - var p, throws; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - throws = function() { - return fs.readFileSync(p); + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + var throws = function() { + fs.readFileSync(p); }; - return assert.throws(throws, /ENOENT/); + assert.throws(throws, /ENOENT/); }); + it('passes ENOENT error to callback when can not find file', function() { - var async, p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - async = false; + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + var async = false; fs.readFile(p, function(e) { assert(async); - return assert(/ENOENT/.test(e)); + assert(/ENOENT/.test(e)); }); - return async = true; + async = true; }); - return it('reads a normal file with unpacked files', function() { - var p; - p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt'); - return assert.equal(fs.readFileSync(p).toString().trim(), 'a'); + + it('reads a normal file with unpacked files', function() { + var p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt'); + assert.equal(fs.readFileSync(p).toString().trim(), 'a'); }); }); + describe('fs.readFile', function() { it('reads a normal file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'file1'); - return fs.readFile(p, function(err, content) { + var p = path.join(fixtures, 'asar', 'a.asar', 'file1'); + fs.readFile(p, function(err, content) { assert.equal(err, null); assert.equal(String(content).trim(), 'file1'); - return done(); + done(); }); }); + it('reads from a empty file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'empty.asar', 'file1'); - return fs.readFile(p, function(err, content) { + var p = path.join(fixtures, 'asar', 'empty.asar', 'file1'); + fs.readFile(p, function(err, content) { assert.equal(err, null); assert.equal(String(content), ''); - return done(); + done(); }); }); + it('reads a linked file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link1'); - return fs.readFile(p, function(err, content) { + var p = path.join(fixtures, 'asar', 'a.asar', 'link1'); + fs.readFile(p, function(err, content) { assert.equal(err, null); assert.equal(String(content).trim(), 'file1'); - return done(); + done(); }); }); + it('reads a file from linked directory', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); - return fs.readFile(p, function(err, content) { + var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); + fs.readFile(p, function(err, content) { assert.equal(err, null); assert.equal(String(content).trim(), 'file1'); - return done(); + done(); }); }); - return it('throws ENOENT error when can not find file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - return fs.readFile(p, function(err) { + + it('throws ENOENT error when can not find file', function(done) { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + fs.readFile(p, function(err) { assert.equal(err.code, 'ENOENT'); - return done(); + done(); }); }); }); + describe('fs.lstatSync', function() { it('handles path with trailing slash correctly', function() { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); + var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); fs.lstatSync(p); - return fs.lstatSync(p + '/'); + fs.lstatSync(p + '/'); }); + it('returns information of root', function() { - var p, stats; - p = path.join(fixtures, 'asar', 'a.asar'); - stats = fs.lstatSync(p); + var p = path.join(fixtures, 'asar', 'a.asar'); + var stats = fs.lstatSync(p); assert.equal(stats.isFile(), false); assert.equal(stats.isDirectory(), true); assert.equal(stats.isSymbolicLink(), false); - return assert.equal(stats.size, 0); + assert.equal(stats.size, 0); }); + it('returns information of a normal file', function() { - var file, j, len, p, ref2, results, stats; + var file, j, len, p, ref2, stats; ref2 = ['file1', 'file2', 'file3', path.join('dir1', 'file1'), path.join('link2', 'file1')]; - results = []; for (j = 0, len = ref2.length; j < len; j++) { file = ref2[j]; p = path.join(fixtures, 'asar', 'a.asar', file); @@ -146,14 +146,13 @@ describe('asar package', function() { assert.equal(stats.isFile(), true); assert.equal(stats.isDirectory(), false); assert.equal(stats.isSymbolicLink(), false); - results.push(assert.equal(stats.size, 6)); + assert.equal(stats.size, 6); } - return results; }); + it('returns information of a normal directory', function() { - var file, j, len, p, ref2, results, stats; + var file, j, len, p, ref2, stats; ref2 = ['dir1', 'dir2', 'dir3']; - results = []; for (j = 0, len = ref2.length; j < len; j++) { file = ref2[j]; p = path.join(fixtures, 'asar', 'a.asar', file); @@ -161,14 +160,13 @@ describe('asar package', function() { assert.equal(stats.isFile(), false); assert.equal(stats.isDirectory(), true); assert.equal(stats.isSymbolicLink(), false); - results.push(assert.equal(stats.size, 0)); + assert.equal(stats.size, 0); } - return results; }); + it('returns information of a linked file', function() { - var file, j, len, p, ref2, results, stats; + var file, j, len, p, ref2, stats; ref2 = ['link1', path.join('dir1', 'link1'), path.join('link2', 'link2')]; - results = []; for (j = 0, len = ref2.length; j < len; j++) { file = ref2[j]; p = path.join(fixtures, 'asar', 'a.asar', file); @@ -176,14 +174,13 @@ describe('asar package', function() { assert.equal(stats.isFile(), false); assert.equal(stats.isDirectory(), false); assert.equal(stats.isSymbolicLink(), true); - results.push(assert.equal(stats.size, 0)); + assert.equal(stats.size, 0); } - return results; }); + it('returns information of a linked directory', function() { - var file, j, len, p, ref2, results, stats; + var file, j, len, p, ref2, stats; ref2 = ['link2', path.join('dir1', 'link2'), path.join('link2', 'link2')]; - results = []; for (j = 0, len = ref2.length; j < len; j++) { file = ref2[j]; p = path.join(fixtures, 'asar', 'a.asar', file); @@ -191,31 +188,30 @@ describe('asar package', function() { assert.equal(stats.isFile(), false); assert.equal(stats.isDirectory(), false); assert.equal(stats.isSymbolicLink(), true); - results.push(assert.equal(stats.size, 0)); + assert.equal(stats.size, 0); } - return results; }); - return it('throws ENOENT error when can not find file', function() { - var file, j, len, p, ref2, results, throws; + + it('throws ENOENT error when can not find file', function() { + var file, j, len, p, ref2, throws; ref2 = ['file4', 'file5', path.join('dir1', 'file4')]; - results = []; for (j = 0, len = ref2.length; j < len; j++) { file = ref2[j]; p = path.join(fixtures, 'asar', 'a.asar', file); throws = function() { - return fs.lstatSync(p); + fs.lstatSync(p); }; - results.push(assert.throws(throws, /ENOENT/)); + assert.throws(throws, /ENOENT/); } - return results; }); }); + describe('fs.lstat', function() { it('handles path with trailing slash correctly', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); - return fs.lstat(p + '/', done); + var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1'); + fs.lstat(p + '/', done); }); + it('returns information of root', function(done) { var p = path.join(fixtures, 'asar', 'a.asar'); fs.lstat(p, function(err, stats) { @@ -224,9 +220,10 @@ describe('asar package', function() { assert.equal(stats.isDirectory(), true); assert.equal(stats.isSymbolicLink(), false); assert.equal(stats.size, 0); - return done(); + done(); }); }); + it('returns information of a normal file', function(done) { var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'file1'); fs.lstat(p, function(err, stats) { @@ -235,9 +232,10 @@ describe('asar package', function() { assert.equal(stats.isDirectory(), false); assert.equal(stats.isSymbolicLink(), false); assert.equal(stats.size, 6); - return done(); + done(); }); }); + it('returns information of a normal directory', function(done) { var p = path.join(fixtures, 'asar', 'a.asar', 'dir1'); fs.lstat(p, function(err, stats) { @@ -246,9 +244,10 @@ describe('asar package', function() { assert.equal(stats.isDirectory(), true); assert.equal(stats.isSymbolicLink(), false); assert.equal(stats.size, 0); - return done(); + done(); }); }); + it('returns information of a linked file', function(done) { var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link1'); fs.lstat(p, function(err, stats) { @@ -257,9 +256,10 @@ describe('asar package', function() { assert.equal(stats.isDirectory(), false); assert.equal(stats.isSymbolicLink(), true); assert.equal(stats.size, 0); - return done(); + done(); }); }); + it('returns information of a linked directory', function(done) { var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2'); fs.lstat(p, function(err, stats) { @@ -268,167 +268,168 @@ describe('asar package', function() { assert.equal(stats.isDirectory(), false); assert.equal(stats.isSymbolicLink(), true); assert.equal(stats.size, 0); - return done(); + done(); }); }); - return it('throws ENOENT error when can not find file', function(done) { + + it('throws ENOENT error when can not find file', function(done) { var p = path.join(fixtures, 'asar', 'a.asar', 'file4'); fs.lstat(p, function(err) { assert.equal(err.code, 'ENOENT'); - return done(); + done(); }); }); }); + describe('fs.realpathSync', function() { it('returns real path root', function() { - var p, parent, r; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = 'a.asar'; - r = fs.realpathSync(path.join(parent, p)); - return assert.equal(r, path.join(parent, p)); + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = 'a.asar'; + var r = fs.realpathSync(path.join(parent, p)); + assert.equal(r, path.join(parent, p)); }); + it('returns real path of a normal file', function() { - var p, parent, r; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'file1'); - r = fs.realpathSync(path.join(parent, p)); - return assert.equal(r, path.join(parent, p)); + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'file1'); + var r = fs.realpathSync(path.join(parent, p)); + assert.equal(r, path.join(parent, p)); }); + it('returns real path of a normal directory', function() { - var p, parent, r; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'dir1'); - r = fs.realpathSync(path.join(parent, p)); - return assert.equal(r, path.join(parent, p)); + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'dir1'); + var r = fs.realpathSync(path.join(parent, p)); + assert.equal(r, path.join(parent, p)); }); + it('returns real path of a linked file', function() { - var p, parent, r; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'link2', 'link1'); - r = fs.realpathSync(path.join(parent, p)); - return assert.equal(r, path.join(parent, 'a.asar', 'file1')); + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'link2', 'link1'); + var r = fs.realpathSync(path.join(parent, p)); + assert.equal(r, path.join(parent, 'a.asar', 'file1')); }); + it('returns real path of a linked directory', function() { - var p, parent, r; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'link2', 'link2'); - r = fs.realpathSync(path.join(parent, p)); - return assert.equal(r, path.join(parent, 'a.asar', 'dir1')); + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'link2', 'link2'); + var r = fs.realpathSync(path.join(parent, p)); + assert.equal(r, path.join(parent, 'a.asar', 'dir1')); }); - return it('throws ENOENT error when can not find file', function() { - var p, parent, throws; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'not-exist'); - throws = function() { - return fs.realpathSync(path.join(parent, p)); + + it('throws ENOENT error when can not find file', function() { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'not-exist'); + var throws = function() { + fs.realpathSync(path.join(parent, p)); }; - return assert.throws(throws, /ENOENT/); + assert.throws(throws, /ENOENT/); }); }); + describe('fs.realpath', function() { it('returns real path root', function(done) { - var p, parent; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = 'a.asar'; - return fs.realpath(path.join(parent, p), function(err, r) { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = 'a.asar'; + fs.realpath(path.join(parent, p), function(err, r) { assert.equal(err, null); assert.equal(r, path.join(parent, p)); - return done(); + done(); }); }); + it('returns real path of a normal file', function(done) { - var p, parent; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'file1'); - return fs.realpath(path.join(parent, p), function(err, r) { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'file1'); + fs.realpath(path.join(parent, p), function(err, r) { assert.equal(err, null); assert.equal(r, path.join(parent, p)); - return done(); + done(); }); }); + it('returns real path of a normal directory', function(done) { - var p, parent; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'dir1'); - return fs.realpath(path.join(parent, p), function(err, r) { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'dir1'); + fs.realpath(path.join(parent, p), function(err, r) { assert.equal(err, null); assert.equal(r, path.join(parent, p)); - return done(); + done(); }); }); + it('returns real path of a linked file', function(done) { - var p, parent; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'link2', 'link1'); - return fs.realpath(path.join(parent, p), function(err, r) { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'link2', 'link1'); + fs.realpath(path.join(parent, p), function(err, r) { assert.equal(err, null); assert.equal(r, path.join(parent, 'a.asar', 'file1')); - return done(); + done(); }); }); + it('returns real path of a linked directory', function(done) { - var p, parent; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'link2', 'link2'); - return fs.realpath(path.join(parent, p), function(err, r) { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'link2', 'link2'); + fs.realpath(path.join(parent, p), function(err, r) { assert.equal(err, null); assert.equal(r, path.join(parent, 'a.asar', 'dir1')); - return done(); + done(); }); }); - return it('throws ENOENT error when can not find file', function(done) { - var p, parent; - parent = fs.realpathSync(path.join(fixtures, 'asar')); - p = path.join('a.asar', 'not-exist'); - return fs.realpath(path.join(parent, p), function(err) { + it('throws ENOENT error when can not find file', function(done) { + var parent = fs.realpathSync(path.join(fixtures, 'asar')); + var p = path.join('a.asar', 'not-exist'); + fs.realpath(path.join(parent, p), function(err) { assert.equal(err.code, 'ENOENT'); - return done(); + done(); }); }); }); describe('fs.readdirSync', function() { it('reads dirs from root', function() { - var dirs, p; - p = path.join(fixtures, 'asar', 'a.asar'); - dirs = fs.readdirSync(p); - return assert.deepEqual(dirs, ['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + var p = path.join(fixtures, 'asar', 'a.asar'); + var dirs = fs.readdirSync(p); + assert.deepEqual(dirs, ['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); }); + it('reads dirs from a normal dir', function() { - var dirs, p; - p = path.join(fixtures, 'asar', 'a.asar', 'dir1'); - dirs = fs.readdirSync(p); - return assert.deepEqual(dirs, ['file1', 'file2', 'file3', 'link1', 'link2']); + var p = path.join(fixtures, 'asar', 'a.asar', 'dir1'); + var dirs = fs.readdirSync(p); + assert.deepEqual(dirs, ['file1', 'file2', 'file3', 'link1', 'link2']); }); + it('reads dirs from a linked dir', function() { - var dirs, p; - p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2'); - dirs = fs.readdirSync(p); - return assert.deepEqual(dirs, ['file1', 'file2', 'file3', 'link1', 'link2']); + var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2'); + var dirs = fs.readdirSync(p); + assert.deepEqual(dirs, ['file1', 'file2', 'file3', 'link1', 'link2']); }); - return it('throws ENOENT error when can not find file', function() { - var p, throws; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - throws = function() { - return fs.readdirSync(p); + + it('throws ENOENT error when can not find file', function() { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + var throws = function() { + fs.readdirSync(p); }; - return assert.throws(throws, /ENOENT/); + assert.throws(throws, /ENOENT/); }); }); + describe('fs.readdir', function() { it('reads dirs from root', function(done) { var p = path.join(fixtures, 'asar', 'a.asar'); fs.readdir(p, function(err, dirs) { assert.equal(err, null); assert.deepEqual(dirs, ['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); - return done(); + done(); }); }); + it('reads dirs from a normal dir', function(done) { var p = path.join(fixtures, 'asar', 'a.asar', 'dir1'); fs.readdir(p, function(err, dirs) { assert.equal(err, null); assert.deepEqual(dirs, ['file1', 'file2', 'file3', 'link1', 'link2']); - return done(); + done(); }); }); it('reads dirs from a linked dir', function(done) { @@ -436,23 +437,23 @@ describe('asar package', function() { fs.readdir(p, function(err, dirs) { assert.equal(err, null); assert.deepEqual(dirs, ['file1', 'file2', 'file3', 'link1', 'link2']); - return done(); + done(); }); }); - return it('throws ENOENT error when can not find file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - return fs.readdir(p, function(err) { + + it('throws ENOENT error when can not find file', function(done) { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + fs.readdir(p, function(err) { assert.equal(err.code, 'ENOENT'); - return done(); + done(); }); }); }); + describe('fs.openSync', function() { it('opens a normal/linked/under-linked-directory file', function() { - var buffer, fd, file, j, len, p, ref2, results; + var buffer, fd, file, j, len, p, ref2; ref2 = ['file1', 'link1', path.join('link2', 'file1')]; - results = []; for (j = 0, len = ref2.length; j < len; j++) { file = ref2[j]; p = path.join(fixtures, 'asar', 'a.asar', file); @@ -460,84 +461,82 @@ describe('asar package', function() { buffer = new Buffer(6); fs.readSync(fd, buffer, 0, 6, 0); assert.equal(String(buffer).trim(), 'file1'); - results.push(fs.closeSync(fd)); + fs.closeSync(fd); } - return results; }); - return it('throws ENOENT error when can not find file', function() { - var p, throws; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - throws = function() { - return fs.openSync(p); + + it('throws ENOENT error when can not find file', function() { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + var throws = function() { + fs.openSync(p); }; - return assert.throws(throws, /ENOENT/); + assert.throws(throws, /ENOENT/); }); }); + describe('fs.open', function() { it('opens a normal file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'file1'); - return fs.open(p, 'r', function(err, fd) { - var buffer; + var p = path.join(fixtures, 'asar', 'a.asar', 'file1'); + fs.open(p, 'r', function(err, fd) { assert.equal(err, null); - buffer = new Buffer(6); - return fs.read(fd, buffer, 0, 6, 0, function(err) { + var buffer = new Buffer(6); + fs.read(fd, buffer, 0, 6, 0, function(err) { assert.equal(err, null); assert.equal(String(buffer).trim(), 'file1'); - return fs.close(fd, done); + fs.close(fd, done); }); }); }); - return it('throws ENOENT error when can not find file', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - return fs.open(p, 'r', function(err) { + + it('throws ENOENT error when can not find file', function(done) { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + fs.open(p, 'r', function(err) { assert.equal(err.code, 'ENOENT'); - return done(); + done(); }); }); }); + describe('fs.mkdir', function() { - return it('throws error when calling inside asar archive', function(done) { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - return fs.mkdir(p, function(err) { + it('throws error when calling inside asar archive', function(done) { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + fs.mkdir(p, function(err) { assert.equal(err.code, 'ENOTDIR'); - return done(); + done(); }); }); }); + describe('fs.mkdirSync', function() { - return it('throws error when calling inside asar archive', function() { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - return assert.throws((function() { - return fs.mkdirSync(p); + it('throws error when calling inside asar archive', function() { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + assert.throws((function() { + fs.mkdirSync(p); }), new RegExp('ENOTDIR')); }); }); + describe('child_process.fork', function() { - child_process = require('child_process'); it('opens a normal js file', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js')); + var child = child_process.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js')); child.on('message', function(msg) { assert.equal(msg, 'message'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); - return it('supports asar in the forked js', function(done) { - var child, file; - file = path.join(fixtures, 'asar', 'a.asar', 'file1'); - child = child_process.fork(path.join(fixtures, 'module', 'asar.js')); + + it('supports asar in the forked js', function(done) { + var file = path.join(fixtures, 'asar', 'a.asar', 'file1'); + var child = child_process.fork(path.join(fixtures, 'module', 'asar.js')); child.on('message', function(content) { assert.equal(content, fs.readFileSync(file).toString()); - return done(); + done(); }); - return child.send(file); + child.send(file); }); }); + describe('child_process.execFile', function() { var echo, execFile, execFileSync, ref2; if (process.platform !== 'darwin') { @@ -545,244 +544,249 @@ describe('asar package', function() { } ref2 = require('child_process'), execFile = ref2.execFile, execFileSync = ref2.execFileSync; echo = path.join(fixtures, 'asar', 'echo.asar', 'echo'); + it('executes binaries', function(done) { execFile(echo, ['test'], function(error, stdout) { assert.equal(error, null); assert.equal(stdout, 'test\n'); - return done(); + done(); }); }); - return xit('execFileSync executes binaries', function() { - var output; - output = execFileSync(echo, ['test']); - return assert.equal(String(output), 'test\n'); + + xit('execFileSync executes binaries', function() { + var output = execFileSync(echo, ['test']); + assert.equal(String(output), 'test\n'); }); }); + describe('internalModuleReadFile', function() { - var internalModuleReadFile; - internalModuleReadFile = process.binding('fs').internalModuleReadFile; + var internalModuleReadFile = process.binding('fs').internalModuleReadFile; + it('read a normal file', function() { - var file1, file2, file3; - file1 = path.join(fixtures, 'asar', 'a.asar', 'file1'); + var file1 = path.join(fixtures, 'asar', 'a.asar', 'file1'); assert.equal(internalModuleReadFile(file1).toString().trim(), 'file1'); - file2 = path.join(fixtures, 'asar', 'a.asar', 'file2'); + var file2 = path.join(fixtures, 'asar', 'a.asar', 'file2'); assert.equal(internalModuleReadFile(file2).toString().trim(), 'file2'); - file3 = path.join(fixtures, 'asar', 'a.asar', 'file3'); - return assert.equal(internalModuleReadFile(file3).toString().trim(), 'file3'); + var file3 = path.join(fixtures, 'asar', 'a.asar', 'file3'); + assert.equal(internalModuleReadFile(file3).toString().trim(), 'file3'); }); - return it('reads a normal file with unpacked files', function() { - var p; - p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt'); - return assert.equal(internalModuleReadFile(p).toString().trim(), 'a'); + + it('reads a normal file with unpacked files', function() { + var p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt'); + assert.equal(internalModuleReadFile(p).toString().trim(), 'a'); }); }); - return describe('process.noAsar', function() { - var errorName; - errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR'; + + describe('process.noAsar', function() { + var errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR'; + beforeEach(function() { - return process.noAsar = true; + process.noAsar = true; }); + afterEach(function() { - return process.noAsar = false; + process.noAsar = false; }); + it('disables asar support in sync API', function() { - var dir, file; - file = path.join(fixtures, 'asar', 'a.asar', 'file1'); - dir = path.join(fixtures, 'asar', 'a.asar', 'dir1'); + var file = path.join(fixtures, 'asar', 'a.asar', 'file1'); + var dir = path.join(fixtures, 'asar', 'a.asar', 'dir1'); assert.throws((function() { - return fs.readFileSync(file); + fs.readFileSync(file); }), new RegExp(errorName)); assert.throws((function() { - return fs.lstatSync(file); + fs.lstatSync(file); }), new RegExp(errorName)); assert.throws((function() { - return fs.realpathSync(file); + fs.realpathSync(file); }), new RegExp(errorName)); - return assert.throws((function() { - return fs.readdirSync(dir); + assert.throws((function() { + fs.readdirSync(dir); }), new RegExp(errorName)); }); + it('disables asar support in async API', function(done) { - var dir, file; - file = path.join(fixtures, 'asar', 'a.asar', 'file1'); - dir = path.join(fixtures, 'asar', 'a.asar', 'dir1'); - return fs.readFile(file, function(error) { + var file = path.join(fixtures, 'asar', 'a.asar', 'file1'); + var dir = path.join(fixtures, 'asar', 'a.asar', 'dir1'); + fs.readFile(file, function(error) { assert.equal(error.code, errorName); - return fs.lstat(file, function(error) { + fs.lstat(file, function(error) { assert.equal(error.code, errorName); - return fs.realpath(file, function(error) { + fs.realpath(file, function(error) { assert.equal(error.code, errorName); - return fs.readdir(dir, function(error) { + fs.readdir(dir, function(error) { assert.equal(error.code, errorName); - return done(); + done(); }); }); }); }); }); - return it('treats *.asar as normal file', function() { - var asar, content1, content2, originalFs; - originalFs = require('original-fs'); - asar = path.join(fixtures, 'asar', 'a.asar'); - content1 = fs.readFileSync(asar); - content2 = originalFs.readFileSync(asar); + + it('treats *.asar as normal file', function() { + var originalFs = require('original-fs'); + var asar = path.join(fixtures, 'asar', 'a.asar'); + var content1 = fs.readFileSync(asar); + var content2 = originalFs.readFileSync(asar); assert.equal(content1.compare(content2), 0); - return assert.throws((function() { - return fs.readdirSync(asar); + assert.throws((function() { + fs.readdirSync(asar); }), /ENOTDIR/); }); }); }); + describe('asar protocol', function() { - var url; - url = require('url'); + var url = require('url'); + it('can request a file in package', function(done) { - var p; - p = path.resolve(fixtures, 'asar', 'a.asar', 'file1'); - return $.get("file://" + p, function(data) { + var p = path.resolve(fixtures, 'asar', 'a.asar', 'file1'); + $.get("file://" + p, function(data) { assert.equal(data.trim(), 'file1'); - return done(); + done(); }); }); + it('can request a file in package with unpacked files', function(done) { - var p; - p = path.resolve(fixtures, 'asar', 'unpack.asar', 'a.txt'); - return $.get("file://" + p, function(data) { + var p = path.resolve(fixtures, 'asar', 'unpack.asar', 'a.txt'); + $.get("file://" + p, function(data) { assert.equal(data.trim(), 'a'); - return done(); + done(); }); }); + it('can request a linked file in package', function(done) { - var p; - p = path.resolve(fixtures, 'asar', 'a.asar', 'link2', 'link1'); - return $.get("file://" + p, function(data) { + var p = path.resolve(fixtures, 'asar', 'a.asar', 'link2', 'link1'); + $.get("file://" + p, function(data) { assert.equal(data.trim(), 'file1'); - return done(); + done(); }); }); + it('can request a file in filesystem', function(done) { - var p; - p = path.resolve(fixtures, 'asar', 'file'); - return $.get("file://" + p, function(data) { + var p = path.resolve(fixtures, 'asar', 'file'); + $.get("file://" + p, function(data) { assert.equal(data.trim(), 'file'); - return done(); + done(); }); }); + it('gets 404 when file is not found', function(done) { - var p; - p = path.resolve(fixtures, 'asar', 'a.asar', 'no-exist'); - return $.ajax({ + var p = path.resolve(fixtures, 'asar', 'a.asar', 'no-exist'); + $.ajax({ url: "file://" + p, error: function(err) { assert.equal(err.status, 404); - return done(); + done(); } }); }); + it('sets __dirname correctly', function(done) { - var p, u, w; after(function() { w.destroy(); - return ipcMain.removeAllListeners('dirname'); + ipcMain.removeAllListeners('dirname'); }); - w = new BrowserWindow({ + + var w = new BrowserWindow({ show: false, width: 400, height: 400 }); - p = path.resolve(fixtures, 'asar', 'web.asar', 'index.html'); - u = url.format({ + var p = path.resolve(fixtures, 'asar', 'web.asar', 'index.html'); + var u = url.format({ protocol: 'file', slashed: true, pathname: p }); ipcMain.once('dirname', function(event, dirname) { assert.equal(dirname, path.dirname(p)); - return done(); + done(); }); - return w.loadURL(u); + w.loadURL(u); }); - return it('loads script tag in html', function(done) { - var p, u, w; + + it('loads script tag in html', function(done) { after(function() { w.destroy(); - return ipcMain.removeAllListeners('ping'); + ipcMain.removeAllListeners('ping'); }); - w = new BrowserWindow({ + + var w = new BrowserWindow({ show: false, width: 400, height: 400 }); - p = path.resolve(fixtures, 'asar', 'script.asar', 'index.html'); - u = url.format({ + var p = path.resolve(fixtures, 'asar', 'script.asar', 'index.html'); + var u = url.format({ protocol: 'file', slashed: true, pathname: p }); w.loadURL(u); - return ipcMain.once('ping', function(event, message) { + ipcMain.once('ping', function(event, message) { assert.equal(message, 'pong'); - return done(); + done(); }); }); }); + describe('original-fs module', function() { - var originalFs; - originalFs = require('original-fs'); + var originalFs = require('original-fs'); + it('treats .asar as file', function() { - var file, stats; - file = path.join(fixtures, 'asar', 'a.asar'); - stats = originalFs.statSync(file); - return assert(stats.isFile()); + var file = path.join(fixtures, 'asar', 'a.asar'); + var stats = originalFs.statSync(file); + assert(stats.isFile()); }); - return it('is available in forked scripts', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'module', 'original-fs.js')); + + it('is available in forked scripts', function(done) { + var child = child_process.fork(path.join(fixtures, 'module', 'original-fs.js')); child.on('message', function(msg) { assert.equal(msg, 'object'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); }); + describe('graceful-fs module', function() { - var gfs; - gfs = require('graceful-fs'); + var gfs = require('graceful-fs'); + it('recognize asar archvies', function() { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'link1'); - return assert.equal(gfs.readFileSync(p).toString().trim(), 'file1'); + var p = path.join(fixtures, 'asar', 'a.asar', 'link1'); + assert.equal(gfs.readFileSync(p).toString().trim(), 'file1'); }); - return it('does not touch global fs object', function() { - return assert.notEqual(fs.readdir, gfs.readdir); + it('does not touch global fs object', function() { + assert.notEqual(fs.readdir, gfs.readdir); }); }); + describe('mkdirp module', function() { - var mkdirp; - mkdirp = require('mkdirp'); - return it('throws error when calling inside asar archive', function() { - var p; - p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); - return assert.throws((function() { - return mkdirp.sync(p); + var mkdirp = require('mkdirp'); + + it('throws error when calling inside asar archive', function() { + var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist'); + assert.throws((function() { + mkdirp.sync(p); }), new RegExp('ENOTDIR')); }); }); - return describe('native-image', function() { + + describe('native-image', function() { it('reads image from asar archive', function() { - var logo, p; - p = path.join(fixtures, 'asar', 'logo.asar', 'logo.png'); - logo = nativeImage.createFromPath(p); - return assert.deepEqual(logo.getSize(), { + var p = path.join(fixtures, 'asar', 'logo.asar', 'logo.png'); + var logo = nativeImage.createFromPath(p); + assert.deepEqual(logo.getSize(), { width: 55, height: 55 }); }); - return it('reads image from asar archive with unpacked files', function() { - var logo, p; - p = path.join(fixtures, 'asar', 'unpack.asar', 'atom.png'); - logo = nativeImage.createFromPath(p); - return assert.deepEqual(logo.getSize(), { + + it('reads image from asar archive with unpacked files', function() { + var p = path.join(fixtures, 'asar', 'unpack.asar', 'atom.png'); + var logo = nativeImage.createFromPath(p); + assert.deepEqual(logo.getSize(), { width: 1024, height: 1024 }); diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 56fa21c89a7..8477aea776a 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -8,130 +8,156 @@ const BrowserWindow = remote.require('electron').BrowserWindow; const session = remote.require('electron').session; describe('chromium feature', function() { - var fixtures, listener; - fixtures = path.resolve(__dirname, 'fixtures'); - listener = null; + var fixtures = path.resolve(__dirname, 'fixtures'); + var listener = null; + afterEach(function() { if (listener != null) { window.removeEventListener('message', listener); } - return listener = null; + listener = null; }); + xdescribe('heap snapshot', function() { - return it('does not crash', function() { - return process.atomBinding('v8_util').takeHeapSnapshot(); + it('does not crash', function() { + process.atomBinding('v8_util').takeHeapSnapshot(); }); }); + describe('sending request of http protocol urls', function() { - return it('does not crash', function(done) { - var server; + it('does not crash', function(done) { this.timeout(5000); - server = http.createServer(function(req, res) { + + var server = http.createServer(function(req, res) { res.end(); server.close(); - return done(); + done(); }); - return server.listen(0, '127.0.0.1', function() { - var port; - port = server.address().port; - return $.get("http://127.0.0.1:" + port); + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; + $.get("http://127.0.0.1:" + port); }); }); }); + describe('document.hidden', function() { - var url, w; - url = "file://" + fixtures + "/pages/document-hidden.html"; - w = null; + var url = "file://" + fixtures + "/pages/document-hidden.html"; + var w = null; + afterEach(function() { - return w != null ? w.destroy() : void 0; + w != null ? w.destroy() : void 0; }); + it('is set correctly when window is not shown', function(done) { w = new BrowserWindow({ show: false }); w.webContents.on('ipc-message', function(event, args) { assert.deepEqual(args, ['hidden', true]); - return done(); + done(); }); - return w.loadURL(url); + w.loadURL(url); }); - return it('is set correctly when window is inactive', function(done) { + + it('is set correctly when window is inactive', function(done) { w = new BrowserWindow({ show: false }); w.webContents.on('ipc-message', function(event, args) { assert.deepEqual(args, ['hidden', false]); - return done(); + done(); }); w.showInactive(); - return w.loadURL(url); + w.loadURL(url); }); }); + xdescribe('navigator.webkitGetUserMedia', function() { - return it('calls its callbacks', function(done) { + it('calls its callbacks', function(done) { this.timeout(5000); - return navigator.webkitGetUserMedia({ + + navigator.webkitGetUserMedia({ audio: true, video: false }, function() { - return done(); + done(); }, function() { - return done(); + done(); }); }); }); - describe('navigator.language', function() { - return it('should not be empty', function() { - return assert.notEqual(navigator.language, ''); + + describe('navigator.mediaDevices', function() { + if (process.env.TRAVIS === 'true') { + return; + } + + it('can return labels of enumerated devices', function(done) { + navigator.mediaDevices.enumerateDevices().then((devices) => { + const result = devices.some((device) => !!device.label); + if (result) + done(); + }); }); }); - describe('navigator.serviceWorker', function() { - var url, w; - url = "file://" + fixtures + "/pages/service-worker/index.html"; - w = null; - afterEach(function() { - return w != null ? w.destroy() : void 0; + + describe('navigator.language', function() { + it('should not be empty', function() { + assert.notEqual(navigator.language, ''); }); - return it('should register for file scheme', function(done) { + }); + + describe('navigator.serviceWorker', function() { + var url = "file://" + fixtures + "/pages/service-worker/index.html"; + var w = null; + + afterEach(function() { + w != null ? w.destroy() : void 0; + }); + + it('should register for file scheme', function(done) { w = new BrowserWindow({ show: false }); w.webContents.on('ipc-message', function(event, args) { if (args[0] === 'reload') { - return w.webContents.reload(); + w.webContents.reload(); } else if (args[0] === 'error') { - return done('unexpected error : ' + args[1]); + done('unexpected error : ' + args[1]); } else if (args[0] === 'response') { assert.equal(args[1], 'Hello from serviceWorker!'); - return session.defaultSession.clearStorageData({ + session.defaultSession.clearStorageData({ storages: ['serviceworkers'] }, function() { - return done(); + done(); }); } }); - return w.loadURL(url); + w.loadURL(url); }); }); + describe('window.open', function() { this.timeout(20000); + it('returns a BrowserWindowProxy object', function() { - var b; - b = window.open('about:blank', '', 'show=no'); + var b = window.open('about:blank', '', 'show=no'); assert.equal(b.closed, false); assert.equal(b.constructor.name, 'BrowserWindowProxy'); - return b.close(); + b.close(); }); + it('accepts "node-integration" as feature', function(done) { var b; listener = function(event) { assert.equal(event.data, 'undefined'); b.close(); - return done(); + done(); }; window.addEventListener('message', listener); - return b = window.open("file://" + fixtures + "/pages/window-opener-node.html", '', 'nodeIntegration=no,show=no'); + b = window.open("file://" + fixtures + "/pages/window-opener-node.html", '', 'nodeIntegration=no,show=no'); }); + it('inherit options of parent window', function(done) { var b; listener = function(event) { @@ -139,12 +165,13 @@ describe('chromium feature', function() { ref1 = remote.getCurrentWindow().getSize(), width = ref1[0], height = ref1[1]; assert.equal(event.data, "size: " + width + " " + height); b.close(); - return done(); + done(); }; window.addEventListener('message', listener); - return b = window.open("file://" + fixtures + "/pages/window-open-size.html", '', 'show=no'); + b = window.open("file://" + fixtures + "/pages/window-open-size.html", '', 'show=no'); }); - return it('does not override child options', function(done) { + + it('does not override child options', function(done) { var b, size; size = { width: 350, @@ -153,156 +180,167 @@ describe('chromium feature', function() { listener = function(event) { assert.equal(event.data, "size: " + size.width + " " + size.height); b.close(); - return done(); + done(); }; window.addEventListener('message', listener); - return b = window.open("file://" + fixtures + "/pages/window-open-size.html", '', "show=no,width=" + size.width + ",height=" + size.height); + b = window.open("file://" + fixtures + "/pages/window-open-size.html", '', "show=no,width=" + size.width + ",height=" + size.height); }); }); + describe('window.opener', function() { - var url, w; this.timeout(10000); - url = "file://" + fixtures + "/pages/window-opener.html"; - w = null; + + var url = "file://" + fixtures + "/pages/window-opener.html"; + var w = null; + afterEach(function() { - return w != null ? w.destroy() : void 0; + w != null ? w.destroy() : void 0; }); + it('is null for main window', function(done) { w = new BrowserWindow({ show: false }); w.webContents.on('ipc-message', function(event, args) { assert.deepEqual(args, ['opener', null]); - return done(); + done(); }); - return w.loadURL(url); + w.loadURL(url); }); - return it('is not null for window opened by window.open', function(done) { + + it('is not null for window opened by window.open', function(done) { var b; listener = function(event) { assert.equal(event.data, 'object'); b.close(); - return done(); + done(); }; window.addEventListener('message', listener); - return b = window.open(url, '', 'show=no'); + b = window.open(url, '', 'show=no'); }); }); + describe('window.postMessage', function() { - return it('sets the source and origin correctly', function(done) { + it('sets the source and origin correctly', function(done) { var b, sourceId; sourceId = remote.getCurrentWindow().id; listener = function(event) { - var message; window.removeEventListener('message', listener); b.close(); - message = JSON.parse(event.data); + var message = JSON.parse(event.data); assert.equal(message.data, 'testing'); assert.equal(message.origin, 'file://'); assert.equal(message.sourceEqualsOpener, true); assert.equal(message.sourceId, sourceId); assert.equal(event.origin, 'file://'); - return done(); + done(); }; window.addEventListener('message', listener); b = window.open("file://" + fixtures + "/pages/window-open-postMessage.html", '', 'show=no'); - return BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function() { - return b.postMessage('testing', '*'); + BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function() { + b.postMessage('testing', '*'); }); }); }); + describe('window.opener.postMessage', function() { - return it('sets source and origin correctly', function(done) { + it('sets source and origin correctly', function(done) { var b; listener = function(event) { window.removeEventListener('message', listener); b.close(); assert.equal(event.source, b); assert.equal(event.origin, 'file://'); - return done(); + done(); }; window.addEventListener('message', listener); - return b = window.open("file://" + fixtures + "/pages/window-opener-postMessage.html", '', 'show=no'); + b = window.open("file://" + fixtures + "/pages/window-opener-postMessage.html", '', 'show=no'); }); }); + describe('creating a Uint8Array under browser side', function() { - return it('does not crash', function() { - var RUint8Array; - RUint8Array = remote.getGlobal('Uint8Array'); - return new RUint8Array; + it('does not crash', function() { + var RUint8Array = remote.getGlobal('Uint8Array'); + new RUint8Array; }); }); + describe('webgl', function() { - return it('can be get as context in canvas', function() { - var webgl; + it('can be get as context in canvas', function() { if (process.platform === 'linux') { return; } - webgl = document.createElement('canvas').getContext('webgl'); - return assert.notEqual(webgl, null); + var webgl = document.createElement('canvas').getContext('webgl'); + assert.notEqual(webgl, null); }); }); + describe('web workers', function() { it('Worker can work', function(done) { - var message, worker; - worker = new Worker('../fixtures/workers/worker.js'); - message = 'ping'; + var worker = new Worker('../fixtures/workers/worker.js'); + var message = 'ping'; worker.onmessage = function(event) { assert.equal(event.data, message); worker.terminate(); - return done(); + done(); }; - return worker.postMessage(message); + worker.postMessage(message); }); - return it('SharedWorker can work', function(done) { - var message, worker; - worker = new SharedWorker('../fixtures/workers/shared_worker.js'); - message = 'ping'; + + it('SharedWorker can work', function(done) { + var worker = new SharedWorker('../fixtures/workers/shared_worker.js'); + var message = 'ping'; worker.port.onmessage = function(event) { assert.equal(event.data, message); - return done(); + done(); }; - return worker.port.postMessage(message); + worker.port.postMessage(message); }); }); + describe('iframe', function() { - var iframe; - iframe = null; + var iframe = null; + beforeEach(function() { - return iframe = document.createElement('iframe'); + iframe = document.createElement('iframe'); }); + afterEach(function() { - return document.body.removeChild(iframe); + document.body.removeChild(iframe); }); - return it('does not have node integration', function(done) { + + it('does not have node integration', function(done) { iframe.src = "file://" + fixtures + "/pages/set-global.html"; document.body.appendChild(iframe); - return iframe.onload = function() { + iframe.onload = function() { assert.equal(iframe.contentWindow.test, 'undefined undefined undefined'); - return done(); + done(); }; }); }); + describe('storage', function() { - return it('requesting persitent quota works', function(done) { - return navigator.webkitPersistentStorage.requestQuota(1024 * 1024, function(grantedBytes) { + it('requesting persitent quota works', function(done) { + navigator.webkitPersistentStorage.requestQuota(1024 * 1024, function(grantedBytes) { assert.equal(grantedBytes, 1048576); - return done(); + done(); }); }); }); + describe('websockets', function() { - var WebSocketServer, server, wss; - wss = null; - server = null; - WebSocketServer = ws.Server; + var wss = null; + var server = null; + var WebSocketServer = ws.Server; + afterEach(function() { wss.close(); - return server.close(); + server.close(); }); - return it('has user agent', function(done) { + + it('has user agent', function(done) { server = http.createServer(); - return server.listen(0, '127.0.0.1', function() { + server.listen(0, '127.0.0.1', function() { var port = server.address().port; wss = new WebSocketServer({ server: server @@ -310,16 +348,17 @@ describe('chromium feature', function() { wss.on('error', done); wss.on('connection', function(ws) { if (ws.upgradeReq.headers['user-agent']) { - return done(); + done(); } else { - return done('user agent is empty'); + done('user agent is empty'); } }); new WebSocket("ws://127.0.0.1:" + port); }); }); }); - return describe('Promise', function() { + + describe('Promise', function() { it('resolves correctly in Node.js calls', function(done) { document.registerElement('x-element', { prototype: Object.create(HTMLElement.prototype, { @@ -328,17 +367,17 @@ describe('chromium feature', function() { } }) }); - return setImmediate(function() { - var called; - called = false; + setImmediate(function() { + var called = false; Promise.resolve().then(function() { - return done(called ? void 0 : new Error('wrong sequence')); + done(called ? void 0 : new Error('wrong sequence')); }); document.createElement('x-element'); - return called = true; + called = true; }); }); - return it('resolves correctly in Electron calls', function(done) { + + it('resolves correctly in Electron calls', function(done) { document.registerElement('y-element', { prototype: Object.create(HTMLElement.prototype, { createdCallback: { @@ -346,14 +385,13 @@ describe('chromium feature', function() { } }) }); - return remote.getGlobal('setImmediate')(function() { - var called; - called = false; + remote.getGlobal('setImmediate')(function() { + var called = false; Promise.resolve().then(function() { - return done(called ? void 0 : new Error('wrong sequence')); + done(called ? void 0 : new Error('wrong sequence')); }); document.createElement('y-element'); - return called = true; + called = true; }); }); }); diff --git a/spec/fixtures/api/quit-app/main.js b/spec/fixtures/api/quit-app/main.js index e2f97affe6d..114e830076a 100644 --- a/spec/fixtures/api/quit-app/main.js +++ b/spec/fixtures/api/quit-app/main.js @@ -1,12 +1,12 @@ -var app = require('electron').app +var app = require('electron').app; app.on('ready', function () { // This setImmediate call gets the spec passing on Linux setImmediate(function () { - app.exit(123) - }) -}) + app.exit(123); + }); +}); process.on('exit', function (code) { - console.log('Exit event with code: ' + code) -}) + console.log('Exit event with code: ' + code); +}); diff --git a/spec/fixtures/devtools-extensions/foo/manifest.json b/spec/fixtures/devtools-extensions/foo/manifest.json new file mode 100644 index 00000000000..bde99de9287 --- /dev/null +++ b/spec/fixtures/devtools-extensions/foo/manifest.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/spec/fixtures/module/call.js b/spec/fixtures/module/call.js index 4cf232e220d..ce0eb6324df 100644 --- a/spec/fixtures/module/call.js +++ b/spec/fixtures/module/call.js @@ -1,7 +1,7 @@ exports.call = function(func) { return func(); -} +}; exports.constructor = function() { this.test = 'test'; -} +}; diff --git a/spec/fixtures/module/class.js b/spec/fixtures/module/class.js new file mode 100644 index 00000000000..f25eb2593ff --- /dev/null +++ b/spec/fixtures/module/class.js @@ -0,0 +1,29 @@ +'use strict'; + +let value = 'old'; + +class BaseClass { + method() { + return 'method'; + } + + get readonly() { + return 'readonly'; + } + + get value() { + return value; + } + + set value(val) { + value = val; + } +} + +class DerivedClass extends BaseClass { +} + +module.exports = { + base: new BaseClass, + derived: new DerivedClass, +} diff --git a/spec/fixtures/module/id.js b/spec/fixtures/module/id.js index 2faec9d3832..5bfae457fe0 100644 --- a/spec/fixtures/module/id.js +++ b/spec/fixtures/module/id.js @@ -1 +1 @@ -exports.id = 1127 +exports.id = 1127; diff --git a/spec/fixtures/module/locale-compare.js b/spec/fixtures/module/locale-compare.js index f99e7c3be11..32dfb309926 100644 --- a/spec/fixtures/module/locale-compare.js +++ b/spec/fixtures/module/locale-compare.js @@ -1,4 +1,4 @@ -process.on('message', function (msg) { +process.on('message', function () { process.send([ 'a'.localeCompare('a'), 'ä'.localeCompare('z', 'de'), diff --git a/spec/fixtures/module/original-fs.js b/spec/fixtures/module/original-fs.js index 90b6abcf9b7..7a527c63358 100644 --- a/spec/fixtures/module/original-fs.js +++ b/spec/fixtures/module/original-fs.js @@ -1,3 +1,3 @@ -process.on('message', function (msg) { +process.on('message', function () { process.send(typeof require('original-fs')); }); diff --git a/spec/fixtures/module/print_name.js b/spec/fixtures/module/print_name.js index 01d13f4ba8b..96ac2d6f3cb 100644 --- a/spec/fixtures/module/print_name.js +++ b/spec/fixtures/module/print_name.js @@ -1,7 +1,7 @@ exports.print = function(obj) { return obj.constructor.name; -} +}; exports.echo = function(obj) { return obj; -} +}; diff --git a/spec/fixtures/module/promise.js b/spec/fixtures/module/promise.js index 2e52ed37440..b9b568855e3 100644 --- a/spec/fixtures/module/promise.js +++ b/spec/fixtures/module/promise.js @@ -2,4 +2,4 @@ exports.twicePromise = function (promise) { return promise.then(function (value) { return value * 2; }); -} +}; diff --git a/spec/fixtures/module/property.js b/spec/fixtures/module/property.js index 88e596f7308..36286d800e1 100644 --- a/spec/fixtures/module/property.js +++ b/spec/fixtures/module/property.js @@ -1 +1 @@ -exports.property = 1127 +exports.property = 1127; diff --git a/spec/fixtures/module/send-later.js b/spec/fixtures/module/send-later.js index 13f02452db1..704f47d328d 100644 --- a/spec/fixtures/module/send-later.js +++ b/spec/fixtures/module/send-later.js @@ -1,4 +1,4 @@ var ipcRenderer = require('electron').ipcRenderer; window.onload = function() { ipcRenderer.send('answer', typeof window.process); -} +}; diff --git a/spec/fixtures/module/set-immediate.js b/spec/fixtures/module/set-immediate.js index e7d44a75d1f..d36355ee23b 100644 --- a/spec/fixtures/module/set-immediate.js +++ b/spec/fixtures/module/set-immediate.js @@ -3,7 +3,7 @@ process.on('uncaughtException', function(error) { process.exit(1); }); -process.on('message', function(msg) { +process.on('message', function() { setImmediate(function() { process.send('ok'); process.exit(0); diff --git a/spec/fixtures/pages/process-exit.html b/spec/fixtures/pages/process-exit.html new file mode 100644 index 00000000000..012c9f85b15 --- /dev/null +++ b/spec/fixtures/pages/process-exit.html @@ -0,0 +1,9 @@ + + + + + diff --git a/spec/fixtures/workers/shared_worker.js b/spec/fixtures/workers/shared_worker.js index d35b47435e8..40220793979 100644 --- a/spec/fixtures/workers/shared_worker.js +++ b/spec/fixtures/workers/shared_worker.js @@ -1,7 +1,7 @@ -onconnect = function(event) { +this.onconnect = function(event) { var port = event.ports[0]; port.start(); port.onmessage = function(event) { port.postMessage(event.data); - } -} + }; +}; diff --git a/spec/fixtures/workers/worker.js b/spec/fixtures/workers/worker.js index 6f9533708a0..4f445470b9a 100644 --- a/spec/fixtures/workers/worker.js +++ b/spec/fixtures/workers/worker.js @@ -1,3 +1,3 @@ this.onmessage = function(msg) { this.postMessage(msg.data); -} +}; diff --git a/spec/modules-spec.js b/spec/modules-spec.js index 6a0471e4cea..fb53a90cf73 100644 --- a/spec/modules-spec.js +++ b/spec/modules-spec.js @@ -3,43 +3,43 @@ const path = require('path'); const temp = require('temp'); describe('third-party module', function() { - var fixtures; - fixtures = path.join(__dirname, 'fixtures'); + 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() { - return require('runas'); + require('runas'); }); - return it('can be required in node binary', function(done) { - var child, runas; - runas = path.join(fixtures, 'module', 'runas.js'); - child = require('child_process').fork(runas); - return child.on('message', function(msg) { + + 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'); - return done(); + done(); }); }); }); + describe('ffi', function() { - return it('does not crash', function() { - var ffi, libm; - ffi = require('ffi'); - libm = ffi.Library('libm', { + it('does not crash', function() { + var ffi = require('ffi'); + var libm = ffi.Library('libm', { ceil: ['double', ['double']] }); - return assert.equal(libm.ceil(1.5), 2); + assert.equal(libm.ceil(1.5), 2); }); }); } - return describe('q', function() { - var Q; - Q = require('q'); - return describe('Q.when', function() { - return it('emits the fullfil callback', function(done) { - return Q(true).then(function(val) { + + 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); - return done(); + done(); }); }); }); diff --git a/spec/node-spec.js b/spec/node-spec.js index 6f84f59921f..83f685d95f0 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -6,95 +6,96 @@ const os = require('os'); const remote = require('electron').remote; describe('node feature', function() { - var fixtures; - fixtures = path.join(__dirname, 'fixtures'); + var fixtures = path.join(__dirname, 'fixtures'); + describe('child_process', function() { - return describe('child_process.fork', function() { + describe('child_process.fork', function() { it('works in current process', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'module', 'ping.js')); + var child = child_process.fork(path.join(fixtures, 'module', 'ping.js')); child.on('message', function(msg) { assert.equal(msg, 'message'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); + it('preserves args', function(done) { - var args, child; - args = ['--expose_gc', '-test', '1']; - child = child_process.fork(path.join(fixtures, 'module', 'process_args.js'), args); + var args = ['--expose_gc', '-test', '1']; + var child = child_process.fork(path.join(fixtures, 'module', 'process_args.js'), args); child.on('message', function(msg) { assert.deepEqual(args, msg.slice(2)); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); + it('works in forked process', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'module', 'fork_ping.js')); + var child = child_process.fork(path.join(fixtures, 'module', 'fork_ping.js')); child.on('message', function(msg) { assert.equal(msg, 'message'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); + it('works in forked process when options.env is specifed', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], { + var child = child_process.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], { path: process.env['PATH'] }); child.on('message', function(msg) { assert.equal(msg, 'message'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); + it('works in browser process', function(done) { - var child, fork; - fork = remote.require('child_process').fork; - child = fork(path.join(fixtures, 'module', 'ping.js')); + var fork = remote.require('child_process').fork; + var child = fork(path.join(fixtures, 'module', 'ping.js')); child.on('message', function(msg) { assert.equal(msg, 'message'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); + it('has String::localeCompare working in script', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'module', 'locale-compare.js')); + var child = child_process.fork(path.join(fixtures, 'module', 'locale-compare.js')); child.on('message', function(msg) { assert.deepEqual(msg, [0, -1, 1]); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); - return it('has setImmediate working in script', function(done) { - var child; - child = child_process.fork(path.join(fixtures, 'module', 'set-immediate.js')); + + it('has setImmediate working in script', function(done) { + var child = child_process.fork(path.join(fixtures, 'module', 'set-immediate.js')); child.on('message', function(msg) { assert.equal(msg, 'ok'); - return done(); + done(); }); - return child.send('message'); + child.send('message'); }); }); }); + describe('contexts', function() { describe('setTimeout in fs callback', function() { if (process.env.TRAVIS === 'true') { return; } - return it('does not crash', function(done) { - return fs.readFile(__filename, function() { - return setTimeout(done, 0); + + it('does not crash', function(done) { + fs.readFile(__filename, function() { + setTimeout(done, 0); }); }); }); + describe('throw error in node context', function() { - return it('gets caught', function(done) { - var error, lsts; - error = new Error('boo!'); - lsts = process.listeners('uncaughtException'); + it('gets caught', function(done) { + var error = new Error('boo!'); + var lsts = process.listeners('uncaughtException'); process.removeAllListeners('uncaughtException'); process.on('uncaughtException', function() { var i, len, lst; @@ -103,91 +104,97 @@ describe('node feature', function() { lst = lsts[i]; process.on('uncaughtException', lst); } - return done(); + done(); }); - return fs.readFile(__filename, function() { + fs.readFile(__filename, function() { throw error; }); }); }); + describe('setTimeout called under Chromium event loop in browser process', function() { - return it('can be scheduled in time', function(done) { - return remote.getGlobal('setTimeout')(done, 0); + it('can be scheduled in time', function(done) { + remote.getGlobal('setTimeout')(done, 0); }); }); - return describe('setInterval called under Chromium event loop in browser process', function() { - return it('can be scheduled in time', function(done) { + + describe('setInterval called under Chromium event loop in browser process', function() { + it('can be scheduled in time', function(done) { var clear, interval; clear = function() { remote.getGlobal('clearInterval')(interval); - return done(); + done(); }; - return interval = remote.getGlobal('setInterval')(clear, 10); + interval = remote.getGlobal('setInterval')(clear, 10); }); }); }); + describe('message loop', function() { describe('process.nextTick', function() { it('emits the callback', function(done) { - return process.nextTick(done); + process.nextTick(done); }); - return it('works in nested calls', function(done) { - return process.nextTick(function() { - return process.nextTick(function() { - return process.nextTick(done); + + it('works in nested calls', function(done) { + process.nextTick(function() { + process.nextTick(function() { + process.nextTick(done); }); }); }); }); - return describe('setImmediate', function() { + + describe('setImmediate', function() { it('emits the callback', function(done) { - return setImmediate(done); + setImmediate(done); }); - return it('works in nested calls', function(done) { - return setImmediate(function() { - return setImmediate(function() { - return setImmediate(done); + + it('works in nested calls', function(done) { + setImmediate(function() { + setImmediate(function() { + setImmediate(done); }); }); }); }); }); + describe('net.connect', function() { if (process.platform !== 'darwin') { return; } - return it('emit error when connect to a socket path without listeners', function(done) { - var child, script, socketPath; - socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock'); - script = path.join(fixtures, 'module', 'create_socket.js'); - child = child_process.fork(script, [socketPath]); - return child.on('exit', function(code) { - var client; + + it('emit error when connect to a socket path without listeners', function(done) { + var socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock'); + var script = path.join(fixtures, 'module', 'create_socket.js'); + var child = child_process.fork(script, [socketPath]); + child.on('exit', function(code) { assert.equal(code, 0); - client = require('net').connect(socketPath); - return client.on('error', function(error) { + var client = require('net').connect(socketPath); + client.on('error', function(error) { assert.equal(error.code, 'ECONNREFUSED'); - return done(); + done(); }); }); }); }); + describe('Buffer', function() { it('can be created from WebKit external string', function() { - var b, p; - p = document.createElement('p'); + var p = document.createElement('p'); p.innerText = '闲云潭影日悠悠,物换星移几度秋'; - b = new Buffer(p.innerText); + var b = new Buffer(p.innerText); assert.equal(b.toString(), '闲云潭影日悠悠,物换星移几度秋'); - return assert.equal(Buffer.byteLength(p.innerText), 45); + assert.equal(Buffer.byteLength(p.innerText), 45); }); - return it('correctly parses external one-byte UTF8 string', function() { - var b, p; - p = document.createElement('p'); + + it('correctly parses external one-byte UTF8 string', function() { + var p = document.createElement('p'); p.innerText = 'Jøhänñéß'; - b = new Buffer(p.innerText); + var b = new Buffer(p.innerText); assert.equal(b.toString(), 'Jøhänñéß'); - return assert.equal(Buffer.byteLength(p.innerText), 13); + assert.equal(Buffer.byteLength(p.innerText), 13); }); }); diff --git a/spec/static/main.js b/spec/static/main.js index 13d2dd6a6e1..9a049e3f107 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -72,6 +72,11 @@ app.on('ready', function() { // Test if using protocol module would crash. electron.protocol.registerStringProtocol('test-if-crashes', function() {}); + // Send auto updater errors to window to be verified in specs + electron.autoUpdater.on('error', function (error) { + window.send('auto-updater-error', error.message); + }); + window = new BrowserWindow({ title: 'Electron Tests', show: false, @@ -103,7 +108,7 @@ app.on('ready', function() { // reply the result to renderer for verifying var downloadFilePath = path.join(__dirname, '..', 'fixtures', 'mock.pdf'); ipcMain.on('set-download-option', function(event, need_cancel, prevent_default) { - window.webContents.session.once('will-download', function(e, item, webContents) { + window.webContents.session.once('will-download', function(e, item) { if (prevent_default) { e.preventDefault(); const url = item.getURL(); @@ -133,4 +138,15 @@ app.on('ready', function() { }); event.returnValue = "done"; }); + + ipcMain.on('executeJavaScript', function(event, code, hasCallback) { + if (hasCallback) { + window.webContents.executeJavaScript(code, (result) => { + window.webContents.send('executeJavaScript-response', result); + }); + } else { + window.webContents.executeJavaScript(code); + event.returnValue = "success"; + } + }); }); diff --git a/spec/webview-spec.js b/spec/webview-spec.js index cac5fb56b44..cd2be69d737 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -1,565 +1,597 @@ -var assert, http, path, url; - -assert = require('assert'); - -path = require('path'); - -http = require('http'); - -url = require('url'); +const assert = require('assert'); +const path = require('path'); +const http = require('http'); +const url = require('url'); describe(' tag', function() { - var fixtures, webview; this.timeout(10000); - fixtures = path.join(__dirname, 'fixtures'); - webview = null; + + var fixtures = path.join(__dirname, 'fixtures'); + var webview = null; + beforeEach(function() { - return webview = new WebView; + webview = new WebView; }); + afterEach(function() { if (document.body.contains(webview)) { - return document.body.removeChild(webview); + document.body.removeChild(webview); } }); + describe('src attribute', function() { it('specifies the page to load', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'a'); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/a.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('navigates to new page when changed', function(done) { + + it('navigates to new page when changed', function(done) { var listener = function() { webview.src = "file://" + fixtures + "/pages/b.html"; webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'b'); - return done(); + done(); }); - return webview.removeEventListener('did-finish-load', listener); + webview.removeEventListener('did-finish-load', listener); }; webview.addEventListener('did-finish-load', listener); webview.src = "file://" + fixtures + "/pages/a.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('nodeintegration attribute', function() { it('inserts no node symbols when not set', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'undefined undefined undefined undefined'); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/c.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + it('inserts node symbols when set', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'function object object'); - return done(); + done(); }); webview.setAttribute('nodeintegration', 'on'); webview.src = "file://" + fixtures + "/pages/d.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + it('loads node symbols after POST navigation when set', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'function object object'); - return done(); + done(); }); webview.setAttribute('nodeintegration', 'on'); webview.src = "file://" + fixtures + "/pages/post.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + if (process.platform !== 'win32' || process.execPath.toLowerCase().indexOf('\\out\\d\\') === -1) { - return it('loads native modules when navigation happens', function(done) { + it('loads native modules when navigation happens', function(done) { var listener = function() { webview.removeEventListener('did-finish-load', listener); var listener2 = function(e) { assert.equal(e.message, 'function'); - return done(); + done(); }; webview.addEventListener('console-message', listener2); - return webview.reload(); + webview.reload(); }; webview.addEventListener('did-finish-load', listener); webview.setAttribute('nodeintegration', 'on'); webview.src = "file://" + fixtures + "/pages/native-module.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); } }); + describe('preload attribute', function() { it('loads the script before other scripts in window', function(done) { - var listener; - listener = function(e) { + var listener = function(e) { assert.equal(e.message, 'function object object'); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.setAttribute('preload', fixtures + "/module/preload.js"); webview.src = "file://" + fixtures + "/pages/e.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + it('preload script can still use "process" in required modules when nodeintegration is off', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'object undefined object'); - return done(); + done(); }); webview.setAttribute('preload', fixtures + "/module/preload-node-off.js"); webview.src = "file://" + fixtures + "/api/blank.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('receives ipc message in preload script', function(done) { - var listener, listener2, message; - message = 'boom!'; - listener = function(e) { + + it('receives ipc message in preload script', function(done) { + var message = 'boom!'; + var listener = function(e) { assert.equal(e.channel, 'pong'); assert.deepEqual(e.args, [message]); webview.removeEventListener('ipc-message', listener); - return done(); + done(); }; - listener2 = function() { + var listener2 = function() { webview.send('ping', message); - return webview.removeEventListener('did-finish-load', listener2); + webview.removeEventListener('did-finish-load', listener2); }; webview.addEventListener('ipc-message', listener); webview.addEventListener('did-finish-load', listener2); webview.setAttribute('preload', fixtures + "/module/preload-ipc.js"); webview.src = "file://" + fixtures + "/pages/e.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('httpreferrer attribute', function() { - return it('sets the referrer url', function(done) { - var listener, referrer; - referrer = 'http://github.com/'; - listener = function(e) { + it('sets the referrer url', function(done) { + var referrer = 'http://github.com/'; + var listener = function(e) { assert.equal(e.message, referrer); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.setAttribute('httpreferrer', referrer); webview.src = "file://" + fixtures + "/pages/referrer.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('useragent attribute', function() { - return it('sets the user agent', function(done) { - var listener, referrer; - referrer = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'; - listener = function(e) { + it('sets the user agent', function(done) { + var referrer = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'; + var listener = function(e) { assert.equal(e.message, referrer); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.setAttribute('useragent', referrer); webview.src = "file://" + fixtures + "/pages/useragent.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('disablewebsecurity attribute', function() { it('does not disable web security when not set', function(done) { - var encoded, listener, src; - src = " "; - encoded = btoa(unescape(encodeURIComponent(src))); - listener = function(e) { + var src = " "; + var encoded = btoa(unescape(encodeURIComponent(src))); + var listener = function(e) { assert(/Not allowed to load local resource/.test(e.message)); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.src = "data:text/html;base64," + encoded; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('disables web security when set', function(done) { - var encoded, listener, src; - src = " "; - encoded = btoa(unescape(encodeURIComponent(src))); - listener = function(e) { + + it('disables web security when set', function(done) { + var src = " "; + var encoded = btoa(unescape(encodeURIComponent(src))); + var listener = function(e) { assert.equal(e.message, 'ok'); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.setAttribute('disablewebsecurity', ''); webview.src = "data:text/html;base64," + encoded; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('partition attribute', function() { it('inserts no node symbols when not set', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'undefined undefined undefined undefined'); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/c.html"; webview.partition = 'test1'; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + it('inserts node symbols when set', function(done) { webview.addEventListener('console-message', function(e) { assert.equal(e.message, 'function object object'); - return done(); + done(); }); webview.setAttribute('nodeintegration', 'on'); webview.src = "file://" + fixtures + "/pages/d.html"; webview.partition = 'test2'; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + it('isolates storage for different id', function(done) { - var listener; - listener = function(e) { + var listener = function(e) { assert.equal(e.message, " 0"); webview.removeEventListener('console-message', listener); - return done(); + done(); }; window.localStorage.setItem('test', 'one'); webview.addEventListener('console-message', listener); webview.src = "file://" + fixtures + "/pages/partition/one.html"; webview.partition = 'test3'; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('uses current session storage when no id is provided', function(done) { - var listener; - listener = function(e) { + + it('uses current session storage when no id is provided', function(done) { + var listener = function(e) { assert.equal(e.message, "one 1"); webview.removeEventListener('console-message', listener); - return done(); + done(); }; window.localStorage.setItem('test', 'one'); webview.addEventListener('console-message', listener); webview.src = "file://" + fixtures + "/pages/partition/one.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('allowpopups attribute', function() { it('can not open new window when not set', function(done) { - var listener; - listener = function(e) { + var listener = function(e) { assert.equal(e.message, 'null'); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.src = "file://" + fixtures + "/pages/window-open-hide.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('can open new window when set', function(done) { - var listener; - listener = function(e) { + + it('can open new window when set', function(done) { + var listener = function(e) { assert.equal(e.message, 'window'); webview.removeEventListener('console-message', listener); - return done(); + done(); }; webview.addEventListener('console-message', listener); webview.setAttribute('allowpopups', 'on'); webview.src = "file://" + fixtures + "/pages/window-open-hide.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('new-window event', function() { it('emits when window.open is called', function(done) { webview.addEventListener('new-window', function(e) { assert.equal(e.url, 'http://host/'); assert.equal(e.frameName, 'host'); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/window-open.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('emits when link with target is called', function(done) { + + it('emits when link with target is called', function(done) { webview.addEventListener('new-window', function(e) { assert.equal(e.url, 'http://host/'); assert.equal(e.frameName, 'target'); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/target-name.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('ipc-message event', function() { - return it('emits when guest sends a ipc message to browser', function(done) { + it('emits when guest sends a ipc message to browser', function(done) { webview.addEventListener('ipc-message', function(e) { assert.equal(e.channel, 'channel'); assert.deepEqual(e.args, ['arg1', 'arg2']); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/ipc-message.html"; webview.setAttribute('nodeintegration', 'on'); - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('page-title-set event', function() { - return it('emits when title is set', function(done) { + it('emits when title is set', function(done) { webview.addEventListener('page-title-set', function(e) { assert.equal(e.title, 'test'); assert(e.explicitSet); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/a.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('page-favicon-updated event', function() { - return it('emits when favicon urls are received', function(done) { + it('emits when favicon urls are received', function(done) { webview.addEventListener('page-favicon-updated', function(e) { - var pageUrl; assert.equal(e.favicons.length, 2); - pageUrl = process.platform === 'win32' ? 'file:///C:/favicon.png' : 'file:///favicon.png'; + var pageUrl = process.platform === 'win32' ? 'file:///C:/favicon.png' : 'file:///favicon.png'; assert.equal(e.favicons[0], pageUrl); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/a.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('will-navigate event', function() { - return it('emits when a url that leads to oustide of the page is clicked', function(done) { + it('emits when a url that leads to oustide of the page is clicked', function(done) { webview.addEventListener('will-navigate', function(e) { assert.equal(e.url, "http://host/"); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/webview-will-navigate.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('did-navigate event', function() { - var p, pageUrl; - p = path.join(fixtures, 'pages', 'webview-will-navigate.html'); + var p = path.join(fixtures, 'pages', 'webview-will-navigate.html'); p = p.replace(/\\/g, '/'); - pageUrl = url.format({ + var pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p }); - return it('emits when a url that leads to outside of the page is clicked', function(done) { + + it('emits when a url that leads to outside of the page is clicked', function(done) { webview.addEventListener('did-navigate', function(e) { assert.equal(e.url, pageUrl); - return done(); + done(); }); webview.src = pageUrl; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('did-navigate-in-page event', function() { it('emits when an anchor link is clicked', function(done) { - var p, pageUrl; - p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html'); + var p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html'); p = p.replace(/\\/g, '/'); - pageUrl = url.format({ + var pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p }); webview.addEventListener('did-navigate-in-page', function(e) { assert.equal(e.url, pageUrl + "#test_content"); - return done(); + done(); }); webview.src = pageUrl; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); + it('emits when window.history.replaceState is called', function(done) { webview.addEventListener('did-navigate-in-page', function(e) { assert.equal(e.url, "http://host/"); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/webview-did-navigate-in-page-with-history.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('emits when window.location.hash is changed', function(done) { - var p, pageUrl; - p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html'); + + it('emits when window.location.hash is changed', function(done) { + var p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html'); p = p.replace(/\\/g, '/'); - pageUrl = url.format({ + var pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p }); webview.addEventListener('did-navigate-in-page', function(e) { assert.equal(e.url, pageUrl + "#test"); - return done(); + done(); }); webview.src = pageUrl; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('close event', function() { - return it('should fire when interior page calls window.close', function(done) { + it('should fire when interior page calls window.close', function(done) { webview.addEventListener('close', function() { - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/close.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('devtools-opened event', function() { - return it('should fire when webview.openDevTools() is called', function(done) { - var listener; - listener = function() { + it('should fire when webview.openDevTools() is called', function(done) { + var listener = function() { webview.removeEventListener('devtools-opened', listener); webview.closeDevTools(); - return done(); + done(); }; webview.addEventListener('devtools-opened', listener); webview.addEventListener('dom-ready', function() { - return webview.openDevTools(); + webview.openDevTools(); }); webview.src = "file://" + fixtures + "/pages/base-page.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('devtools-closed event', function() { - return it('should fire when webview.closeDevTools() is called', function(done) { - var listener, listener2; - listener2 = function() { + it('should fire when webview.closeDevTools() is called', function(done) { + var listener2 = function() { webview.removeEventListener('devtools-closed', listener2); - return done(); + done(); }; - listener = function() { + var listener = function() { webview.removeEventListener('devtools-opened', listener); - return webview.closeDevTools(); + webview.closeDevTools(); }; webview.addEventListener('devtools-opened', listener); webview.addEventListener('devtools-closed', listener2); webview.addEventListener('dom-ready', function() { - return webview.openDevTools(); + webview.openDevTools(); }); webview.src = "file://" + fixtures + "/pages/base-page.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('devtools-focused event', function() { - return it('should fire when webview.openDevTools() is called', function(done) { - var listener; - listener = function() { + it('should fire when webview.openDevTools() is called', function(done) { + var listener = function() { webview.removeEventListener('devtools-focused', listener); webview.closeDevTools(); - return done(); + done(); }; webview.addEventListener('devtools-focused', listener); webview.addEventListener('dom-ready', function() { - return webview.openDevTools(); + webview.openDevTools(); }); webview.src = "file://" + fixtures + "/pages/base-page.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('.reload()', function() { - return it('should emit beforeunload handler', function(done) { - var listener, listener2; - listener = function(e) { + it('should emit beforeunload handler', function(done) { + var listener = function(e) { assert.equal(e.channel, 'onbeforeunload'); webview.removeEventListener('ipc-message', listener); - return done(); + done(); }; - listener2 = function() { + var listener2 = function() { webview.reload(); - return webview.removeEventListener('did-finish-load', listener2); + webview.removeEventListener('did-finish-load', listener2); }; webview.addEventListener('ipc-message', listener); webview.addEventListener('did-finish-load', listener2); webview.setAttribute('nodeintegration', 'on'); webview.src = "file://" + fixtures + "/pages/beforeunload-false.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('.clearHistory()', function() { - return it('should clear the navigation history', function(done) { - var listener; - listener = function(e) { + it('should clear the navigation history', function(done) { + var listener = function(e) { assert.equal(e.channel, 'history'); assert.equal(e.args[0], 2); assert(webview.canGoBack()); webview.clearHistory(); assert(!webview.canGoBack()); webview.removeEventListener('ipc-message', listener); - return done(); + done(); }; webview.addEventListener('ipc-message', listener); webview.setAttribute('nodeintegration', 'on'); webview.src = "file://" + fixtures + "/pages/history.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('basic auth', function() { - var auth; - auth = require('basic-auth'); - return it('should authenticate with correct credentials', function(done) { - var message, server; - message = 'Authenticated'; - server = http.createServer(function(req, res) { - var credentials; - credentials = auth(req); + var auth = require('basic-auth'); + + it('should authenticate with correct credentials', function(done) { + var message = 'Authenticated'; + var server = http.createServer(function(req, res) { + var credentials = auth(req); if (credentials.name === 'test' && credentials.pass === 'test') { res.end(message); } else { res.end('failed'); } - return server.close(); + server.close(); }); - return server.listen(0, '127.0.0.1', function() { - var port; - port = server.address().port; + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; webview.addEventListener('ipc-message', function(e) { assert.equal(e.channel, message); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/basic-auth.html?port=" + port; webview.setAttribute('nodeintegration', 'on'); - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); }); + describe('dom-ready event', function() { - return it('emits when document is loaded', function(done) { - var server; - server = http.createServer(function() {}); - return server.listen(0, '127.0.0.1', function() { - var port; - port = server.address().port; + it('emits when document is loaded', function(done) { + var server = http.createServer(function() {}); + server.listen(0, '127.0.0.1', function() { + var port = server.address().port; webview.addEventListener('dom-ready', function() { - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/dom-ready.html?port=" + port; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + + it('throws a custom error when an API method is called before the event is emitted', function() { + assert.throws(function () { + webview.stop(); + }, 'Cannot call stop because the webContents is unavailable. The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.'); + }); }); + describe('executeJavaScript', function() { if (process.env.TRAVIS !== 'true') { return; } - return it('should support user gesture', function(done) { - var listener, listener2; - listener = function() { + + it('should support user gesture', function(done) { + var listener = function() { webview.removeEventListener('enter-html-full-screen', listener); - return done(); + done(); }; - listener2 = function() { - var jsScript; - jsScript = 'document.getElementsByTagName("video")[0].webkitRequestFullScreen()'; + var listener2 = function() { + var jsScript = "document.querySelector('video').webkitRequestFullscreen()"; webview.executeJavaScript(jsScript, true); - return webview.removeEventListener('did-finish-load', listener2); + webview.removeEventListener('did-finish-load', listener2); }; webview.addEventListener('enter-html-full-screen', listener); webview.addEventListener('did-finish-load', listener2); webview.src = "file://" + fixtures + "/pages/fullscreen.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); + }); + + it('can return the result of the executed script', function(done) { + var listener = function() { + var jsScript = "'4'+2"; + webview.executeJavaScript(jsScript, false, function(result) { + assert.equal(result, '42'); + done(); + }); + webview.removeEventListener('did-finish-load', listener); + }; + webview.addEventListener('did-finish-load', listener); + webview.src = "about:blank"; + document.body.appendChild(webview); }); }); + describe('sendInputEvent', function() { it('can send keyboard event', function(done) { webview.addEventListener('ipc-message', function(e) { assert.equal(e.channel, 'keyup'); assert.deepEqual(e.args, [67, true, false]); - return done(); + done(); }); webview.addEventListener('dom-ready', function() { - return webview.sendInputEvent({ + webview.sendInputEvent({ type: 'keyup', keyCode: 'c', modifiers: ['shift'] @@ -567,16 +599,17 @@ describe(' tag', function() { }); webview.src = "file://" + fixtures + "/pages/onkeyup.html"; webview.setAttribute('nodeintegration', 'on'); - return document.body.appendChild(webview); + document.body.appendChild(webview); }); - return it('can send mouse event', function(done) { + + it('can send mouse event', function(done) { webview.addEventListener('ipc-message', function(e) { assert.equal(e.channel, 'mouseup'); assert.deepEqual(e.args, [10, 20, false, true]); - return done(); + done(); }); webview.addEventListener('dom-ready', function() { - return webview.sendInputEvent({ + webview.sendInputEvent({ type: 'mouseup', modifiers: ['ctrl'], x: 10, @@ -585,54 +618,56 @@ describe(' tag', function() { }); webview.src = "file://" + fixtures + "/pages/onmouseup.html"; webview.setAttribute('nodeintegration', 'on'); - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('media-started-playing media-paused events', function() { - return it('emits when audio starts and stops playing', function(done) { - var audioPlayed; - audioPlayed = false; + it('emits when audio starts and stops playing', function(done) { + var audioPlayed = false; webview.addEventListener('media-started-playing', function() { - return audioPlayed = true; + audioPlayed = true; }); webview.addEventListener('media-paused', function() { assert(audioPlayed); - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/audio.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('found-in-page event', function() { - return it('emits when a request is made', function(done) { - var listener, listener2, requestId; - requestId = null; - listener = function(e) { + it('emits when a request is made', function(done) { + var requestId = null; + var listener = function(e) { assert.equal(e.result.requestId, requestId); if (e.result.finalUpdate) { assert.equal(e.result.matches, 3); webview.stopFindInPage("clearSelection"); - return done(); + done(); } }; - listener2 = function() { - return requestId = webview.findInPage("virtual"); + var listener2 = function() { + requestId = webview.findInPage("virtual"); }; webview.addEventListener('found-in-page', listener); webview.addEventListener('did-finish-load', listener2); webview.src = "file://" + fixtures + "/pages/content.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + xdescribe('did-change-theme-color event', function() { - return it('emits when theme color changes', function(done) { + it('emits when theme color changes', function(done) { webview.addEventListener('did-change-theme-color', function() { - return done(); + done(); }); webview.src = "file://" + fixtures + "/pages/theme-color.html"; - return document.body.appendChild(webview); + document.body.appendChild(webview); }); }); + describe('permission-request event', function() { function setUpRequestHandler(webview, requested_permission) { const session = require('electron').remote.session; @@ -645,7 +680,7 @@ describe(' tag', function() { session.fromPartition(webview.partition).setPermissionRequestHandler(listener); } - it ('emits when using navigator.getUserMedia api', function(done) { + it('emits when using navigator.getUserMedia api', function(done) { webview.addEventListener('ipc-message', function(e) { assert(e.channel, 'message'); assert(e.args, ['PermissionDeniedError']); @@ -658,7 +693,7 @@ describe(' tag', function() { document.body.appendChild(webview); }); - it ('emits when using navigator.geolocation api', function(done) { + it('emits when using navigator.geolocation api', function(done) { webview.addEventListener('ipc-message', function(e) { assert(e.channel, 'message'); assert(e.args, ['ERROR(1): User denied Geolocation']); @@ -671,7 +706,7 @@ describe(' tag', function() { document.body.appendChild(webview); }); - it ('emits when using navigator.requestMIDIAccess api', function(done) { + it('emits when using navigator.requestMIDIAccess api', function(done) { webview.addEventListener('ipc-message', function(e) { assert(e.channel, 'message'); assert(e.args, ['SecurityError']); @@ -684,4 +719,17 @@ describe(' tag', function() { document.body.appendChild(webview); }); }); + + describe('.getWebContents', function() { + it('can return the webcontents associated', function(done) { + webview.addEventListener('did-finish-load', function() { + const webviewContents = webview.getWebContents(); + assert(webviewContents); + assert.equal(webviewContents.getURL(), 'about:blank'); + done(); + }); + webview.src = "about:blank"; + document.body.appendChild(webview); + }); + }); });