diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index eeae1d59111..d3f8237d5ec 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -14,6 +14,7 @@ #include "atom/browser/javascript_environment.h" #include "atom/browser/node_debugger.h" #include "atom/common/api/atom_bindings.h" +#include "atom/common/asar/asar_util.h" #include "atom/common/node_bindings.h" #include "atom/common/node_includes.h" #include "base/command_line.h" @@ -60,8 +61,8 @@ AtomBrowserMainParts::AtomBrowserMainParts() : fake_browser_process_(new BrowserProcess), exit_code_(nullptr), browser_(new Browser), - node_bindings_(NodeBindings::Create(true)), - atom_bindings_(new AtomBindings), + node_bindings_(NodeBindings::Create(NodeBindings::BROWSER)), + atom_bindings_(new AtomBindings(uv_default_loop())), gc_timer_(true, true) { DCHECK(!self_) << "Cannot have two AtomBrowserMainParts"; self_ = this; @@ -71,6 +72,7 @@ AtomBrowserMainParts::AtomBrowserMainParts() } AtomBrowserMainParts::~AtomBrowserMainParts() { + asar::ClearArchives(); // Leak the JavascriptEnvironment on exit. // This is to work around the bug that V8 would be waiting for background // tasks to finish on exit, while somehow it waits forever in Electron, more diff --git a/atom/browser/atom_browser_main_parts.h b/atom/browser/atom_browser_main_parts.h index 71adc43d5e6..2ba7d341f43 100644 --- a/atom/browser/atom_browser_main_parts.h +++ b/atom/browser/atom_browser_main_parts.h @@ -80,9 +80,9 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { std::unique_ptr browser_; std::unique_ptr js_env_; - std::unique_ptr node_env_; std::unique_ptr node_bindings_; std::unique_ptr atom_bindings_; + std::unique_ptr node_env_; std::unique_ptr node_debugger_; base::Timer gc_timer_; diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 9a1d671324f..091614ffd42 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.6.3 + 1.6.4 CFBundleShortVersionString - 1.6.3 + 1.6.4 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 4a500e3e649..5a2d8ea32ec 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,6,3,0 - PRODUCTVERSION 1,6,3,0 + FILEVERSION 1,6,4,0 + PRODUCTVERSION 1,6,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.6.3" + VALUE "FileVersion", "1.6.4" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "1.6.3" + VALUE "ProductVersion", "1.6.4" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index ab72114e2be..aa760a83d28 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -97,6 +97,10 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( command_line->AppendSwitchASCII(switches::kNodeIntegration, node_integration ? "true" : "false"); + // Whether to enable node integration in Worker. + if (web_preferences.GetBoolean(options::kNodeIntegrationInWorker, &b) && b) + command_line->AppendSwitch(switches::kNodeIntegrationInWorker); + // If the `sandbox` option was passed to the BrowserWindow's webPreferences, // pass `--enable-sandbox` to the renderer so it won't have any node.js // integration. diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index e94099bacf6..3151da6f171 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/atom/common/api/atom_api_asar.cc @@ -141,15 +141,16 @@ void InitAsarSupport(v8::Isolate* isolate, v8::Local result = asar_init->Run(); // Initialize asar support. - base::Callback, - v8::Local, - std::string)> init; - if (mate::ConvertFromV8(isolate, result, &init)) { + if (result->IsFunction()) { const char* asar_native = reinterpret_cast( static_cast(node::asar_data)); - init.Run(process, - require, - std::string(asar_native, sizeof(node::asar_data) - 1)); + base::StringPiece asar_data(asar_native, sizeof(node::asar_data) - 1); + v8::Local args[] = { + process, + require, + mate::ConvertToV8(isolate, asar_data), + }; + result.As()->Call(result, 3, args); } } diff --git a/atom/common/api/atom_bindings.cc b/atom/common/api/atom_bindings.cc index f1b56c67593..c82589d6b19 100644 --- a/atom/common/api/atom_bindings.cc +++ b/atom/common/api/atom_bindings.cc @@ -78,8 +78,8 @@ void FatalErrorCallback(const char* location, const char* message) { } // namespace -AtomBindings::AtomBindings() { - uv_async_init(uv_default_loop(), &call_next_tick_async_, OnCallNextTick); +AtomBindings::AtomBindings(uv_loop_t* loop) { + uv_async_init(loop, &call_next_tick_async_, OnCallNextTick); call_next_tick_async_.data = this; } diff --git a/atom/common/api/atom_bindings.h b/atom/common/api/atom_bindings.h index 3fd43cc8a14..81574171503 100644 --- a/atom/common/api/atom_bindings.h +++ b/atom/common/api/atom_bindings.h @@ -20,7 +20,7 @@ namespace atom { class AtomBindings { public: - AtomBindings(); + explicit AtomBindings(uv_loop_t* loop); virtual ~AtomBindings(); // Add process.atomBinding function, which behaves like process.binding but diff --git a/atom/common/asar/asar_util.cc b/atom/common/asar/asar_util.cc index 1eee09949af..0ffbfc6c365 100644 --- a/atom/common/asar/asar_util.cc +++ b/atom/common/asar/asar_util.cc @@ -12,6 +12,7 @@ #include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/stl_util.h" +#include "base/threading/thread_local.h" namespace asar { @@ -19,14 +20,17 @@ namespace { // The global instance of ArchiveMap, will be destroyed on exit. typedef std::map> ArchiveMap; -static base::LazyInstance g_archive_map = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance>::Leaky + g_archive_map_tls = LAZY_INSTANCE_INITIALIZER; const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar"); } // namespace std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { - ArchiveMap& archive_map = *g_archive_map.Pointer(); + if (!g_archive_map_tls.Pointer()->Get()) + g_archive_map_tls.Pointer()->Set(new ArchiveMap); + ArchiveMap& archive_map = *g_archive_map_tls.Pointer()->Get(); if (!ContainsKey(archive_map, path)) { std::shared_ptr archive(new Archive(path)); if (!archive->Init()) @@ -36,6 +40,11 @@ std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path) { return archive_map[path]; } +void ClearArchives() { + if (g_archive_map_tls.Pointer()->Get()) + delete g_archive_map_tls.Pointer()->Get(); +} + bool GetAsarArchivePath(const base::FilePath& full_path, base::FilePath* asar_path, base::FilePath* relative_path) { diff --git a/atom/common/asar/asar_util.h b/atom/common/asar/asar_util.h index 4cb5b88e048..90ffb9b46a3 100644 --- a/atom/common/asar/asar_util.h +++ b/atom/common/asar/asar_util.h @@ -19,6 +19,9 @@ class Archive; // Gets or creates a new Archive from the path. std::shared_ptr GetOrCreateAsarArchive(const base::FilePath& path); +// Destroy cached Archive objects. +void ClearArchives(); + // Separates the path to Archive out. bool GetAsarArchivePath(const base::FilePath& full_path, base::FilePath* asar_path, diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 155d9566f09..73528b45eaa 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 1 #define ATOM_MINOR_VERSION 6 -#define ATOM_PATCH_VERSION 3 +#define ATOM_PATCH_VERSION 4 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/native_mate_converters/callback.cc b/atom/common/native_mate_converters/callback.cc index dc6d30fd23d..a6a500be73a 100644 --- a/atom/common/native_mate_converters/callback.cc +++ b/atom/common/native_mate_converters/callback.cc @@ -4,8 +4,6 @@ #include "atom/common/native_mate_converters/callback.h" -#include "content/public/browser/browser_thread.h" - using content::BrowserThread; namespace mate { diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index decc36eb576..28bff3c82ad 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -11,6 +11,8 @@ #include "base/bind.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "content/public/browser/browser_thread.h" #include "native_mate/function_template.h" #include "native_mate/scoped_persistent.h" diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 4cf2ba56e4c..314a7b9c033 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -25,8 +25,6 @@ #include "atom/common/node_includes.h" -using content::BrowserThread; - // Force all builtin modules to be referenced so they can actually run their // DSO constructors, see http://git.io/DRIqCg. #define REFERENCE_MODULE(name) \ @@ -98,9 +96,9 @@ base::FilePath GetResourcesPath(bool is_browser) { } // namespace -NodeBindings::NodeBindings(bool is_browser) - : is_browser_(is_browser), - uv_loop_(uv_default_loop()), +NodeBindings::NodeBindings(BrowserEnvironment browser_env) + : browser_env_(browser_env), + uv_loop_(browser_env == WORKER ? uv_loop_new() : uv_default_loop()), embed_closed_(false), uv_env_(nullptr), weak_factory_(this) { @@ -118,16 +116,20 @@ NodeBindings::~NodeBindings() { // Clear uv. uv_sem_destroy(&embed_sem_); uv_close(reinterpret_cast(&dummy_uv_handle_), nullptr); + + // Destroy loop. + if (uv_loop_ != uv_default_loop()) + uv_loop_delete(uv_loop_); } void NodeBindings::Initialize() { // Open node's error reporting system for browser process. - node::g_standalone_mode = is_browser_; + node::g_standalone_mode = browser_env_ == BROWSER; node::g_upstream_node_mode = false; #if defined(OS_LINUX) // Get real command line in renderer process forked by zygote. - if (!is_browser_) + if (browser_env_ != BROWSER) AtomCommandLine::InitializeFromCommandLine(); #endif @@ -139,7 +141,7 @@ void NodeBindings::Initialize() { // uv_init overrides error mode to suppress the default crash dialog, bring // it back if user wants to show it. std::unique_ptr env(base::Environment::Create()); - if (is_browser_ || env->HasVar("ELECTRON_DEFAULT_ERROR_MODE")) + if (browser_env_ == BROWSER || env->HasVar("ELECTRON_DEFAULT_ERROR_MODE")) SetErrorMode(GetErrorMode() & ~SEM_NOGPFAULTERRORBOX); #endif } @@ -149,9 +151,19 @@ node::Environment* NodeBindings::CreateEnvironment( auto args = AtomCommandLine::argv(); // Feed node the path to initialization script. - base::FilePath::StringType process_type = is_browser_ ? - FILE_PATH_LITERAL("browser") : FILE_PATH_LITERAL("renderer"); - base::FilePath resources_path = GetResourcesPath(is_browser_); + base::FilePath::StringType process_type; + switch (browser_env_) { + case BROWSER: + process_type = FILE_PATH_LITERAL("browser"); + break; + case RENDERER: + process_type = FILE_PATH_LITERAL("renderer"); + break; + case WORKER: + process_type = FILE_PATH_LITERAL("worker"); + break; + } + base::FilePath resources_path = GetResourcesPath(browser_env_ == BROWSER); base::FilePath script_path = resources_path.Append(FILE_PATH_LITERAL("electron.asar")) .Append(process_type) @@ -161,10 +173,10 @@ node::Environment* NodeBindings::CreateEnvironment( std::unique_ptr c_argv = StringVectorToArgArray(args); node::Environment* env = node::CreateEnvironment( - new node::IsolateData(context->GetIsolate(), uv_default_loop()), context, + new node::IsolateData(context->GetIsolate(), uv_loop_), context, args.size(), c_argv.get(), 0, nullptr); - if (is_browser_) { + if (browser_env_ == BROWSER) { // SetAutorunMicrotasks is no longer called in node::CreateEnvironment // so instead call it here to match expected node behavior context->GetIsolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); @@ -178,7 +190,7 @@ node::Environment* NodeBindings::CreateEnvironment( process.Set("type", process_type); process.Set("resourcesPath", resources_path); // Do not set DOM globals for renderer process. - if (!is_browser_) + if (browser_env_ != BROWSER) process.Set("_noBrowserGlobals", resources_path); // The path to helper app. base::FilePath helper_exec_path; @@ -187,7 +199,7 @@ node::Environment* NodeBindings::CreateEnvironment( // Set process._debugWaitConnect if --debug-brk was specified to stop // the debugger on the first line - if (is_browser_ && + if (browser_env_ == BROWSER && base::CommandLine::ForCurrentProcess()->HasSwitch("debug-brk")) process.Set("_debugWaitConnect", true); @@ -200,8 +212,6 @@ void NodeBindings::LoadEnvironment(node::Environment* env) { } void NodeBindings::PrepareMessageLoop() { - DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - // Add dummy handle for libuv, otherwise libuv would quit when there is // nothing to do. uv_async_init(uv_loop_, &dummy_uv_handle_, nullptr); @@ -212,8 +222,6 @@ void NodeBindings::PrepareMessageLoop() { } void NodeBindings::RunMessageLoop() { - DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - // The MessageLoop should have been created, remember the one in main thread. task_runner_ = base::ThreadTaskRunnerHandle::Get(); @@ -222,8 +230,6 @@ void NodeBindings::RunMessageLoop() { } void NodeBindings::UvRunOnce() { - DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - node::Environment* env = uv_env(); // Use Locker in browser process. @@ -237,13 +243,13 @@ void NodeBindings::UvRunOnce() { v8::MicrotasksScope script_scope(env->isolate(), v8::MicrotasksScope::kRunMicrotasks); - if (!is_browser_) + if (browser_env_ != BROWSER) TRACE_EVENT_BEGIN0("devtools.timeline", "FunctionCall"); // Deal with uv events. int r = uv_run(uv_loop_, UV_RUN_NOWAIT); - if (!is_browser_) + if (browser_env_ != BROWSER) TRACE_EVENT_END0("devtools.timeline", "FunctionCall"); if (r == 0) diff --git a/atom/common/node_bindings.h b/atom/common/node_bindings.h index 663b0acf077..5047a9afb23 100644 --- a/atom/common/node_bindings.h +++ b/atom/common/node_bindings.h @@ -23,7 +23,13 @@ namespace atom { class NodeBindings { public: - static NodeBindings* Create(bool is_browser); + enum BrowserEnvironment { + BROWSER, + RENDERER, + WORKER, + }; + + static NodeBindings* Create(BrowserEnvironment browser_env); virtual ~NodeBindings(); @@ -46,8 +52,10 @@ class NodeBindings { void set_uv_env(node::Environment* env) { uv_env_ = env; } node::Environment* uv_env() const { return uv_env_; } + uv_loop_t* uv_loop() const { return uv_loop_; } + protected: - explicit NodeBindings(bool is_browser); + explicit NodeBindings(BrowserEnvironment browser_env); // Called to poll events in new thread. virtual void PollEvents() = 0; @@ -61,13 +69,13 @@ class NodeBindings { // Interrupt the PollEvents. void WakeupEmbedThread(); - // Are we running in browser. - bool is_browser_; + // Which environment we are running. + BrowserEnvironment browser_env_; - // Main thread's MessageLoop. + // Current thread's MessageLoop. scoped_refptr task_runner_; - // Main thread's libuv loop. + // Current thread's libuv loop. uv_loop_t* uv_loop_; private: diff --git a/atom/common/node_bindings_linux.cc b/atom/common/node_bindings_linux.cc index 34b9ea95236..3ced7029cbc 100644 --- a/atom/common/node_bindings_linux.cc +++ b/atom/common/node_bindings_linux.cc @@ -8,8 +8,8 @@ namespace atom { -NodeBindingsLinux::NodeBindingsLinux(bool is_browser) - : NodeBindings(is_browser), +NodeBindingsLinux::NodeBindingsLinux(BrowserEnvironment browser_env) + : NodeBindings(browser_env), epoll_(epoll_create(1)) { int backend_fd = uv_backend_fd(uv_loop_); struct epoll_event ev = { 0 }; @@ -50,8 +50,8 @@ void NodeBindingsLinux::PollEvents() { } // static -NodeBindings* NodeBindings::Create(bool is_browser) { - return new NodeBindingsLinux(is_browser); +NodeBindings* NodeBindings::Create(BrowserEnvironment browser_env) { + return new NodeBindingsLinux(browser_env); } } // namespace atom diff --git a/atom/common/node_bindings_linux.h b/atom/common/node_bindings_linux.h index 341bf133ed4..829a9d84ddb 100644 --- a/atom/common/node_bindings_linux.h +++ b/atom/common/node_bindings_linux.h @@ -12,7 +12,7 @@ namespace atom { class NodeBindingsLinux : public NodeBindings { public: - explicit NodeBindingsLinux(bool is_browser); + explicit NodeBindingsLinux(BrowserEnvironment browser_env); virtual ~NodeBindingsLinux(); void RunMessageLoop() override; diff --git a/atom/common/node_bindings_mac.cc b/atom/common/node_bindings_mac.cc index cbcbdba3605..e7006a507be 100644 --- a/atom/common/node_bindings_mac.cc +++ b/atom/common/node_bindings_mac.cc @@ -14,8 +14,8 @@ namespace atom { -NodeBindingsMac::NodeBindingsMac(bool is_browser) - : NodeBindings(is_browser) { +NodeBindingsMac::NodeBindingsMac(BrowserEnvironment browser_env) + : NodeBindings(browser_env) { } NodeBindingsMac::~NodeBindingsMac() { @@ -60,8 +60,8 @@ void NodeBindingsMac::PollEvents() { } // static -NodeBindings* NodeBindings::Create(bool is_browser) { - return new NodeBindingsMac(is_browser); +NodeBindings* NodeBindings::Create(BrowserEnvironment browser_env) { + return new NodeBindingsMac(browser_env); } } // namespace atom diff --git a/atom/common/node_bindings_mac.h b/atom/common/node_bindings_mac.h index 96c79ec6f0d..6b0082bc227 100644 --- a/atom/common/node_bindings_mac.h +++ b/atom/common/node_bindings_mac.h @@ -12,7 +12,7 @@ namespace atom { class NodeBindingsMac : public NodeBindings { public: - explicit NodeBindingsMac(bool is_browser); + explicit NodeBindingsMac(BrowserEnvironment browser_env); virtual ~NodeBindingsMac(); void RunMessageLoop() override; diff --git a/atom/common/node_bindings_win.cc b/atom/common/node_bindings_win.cc index b8de4f59da3..419b0ce4c6f 100644 --- a/atom/common/node_bindings_win.cc +++ b/atom/common/node_bindings_win.cc @@ -14,8 +14,8 @@ extern "C" { namespace atom { -NodeBindingsWin::NodeBindingsWin(bool is_browser) - : NodeBindings(is_browser) { +NodeBindingsWin::NodeBindingsWin(BrowserEnvironment browser_env) + : NodeBindings(browser_env) { } NodeBindingsWin::~NodeBindingsWin() { @@ -45,8 +45,8 @@ void NodeBindingsWin::PollEvents() { } // static -NodeBindings* NodeBindings::Create(bool is_browser) { - return new NodeBindingsWin(is_browser); +NodeBindings* NodeBindings::Create(BrowserEnvironment browser_env) { + return new NodeBindingsWin(browser_env); } } // namespace atom diff --git a/atom/common/node_bindings_win.h b/atom/common/node_bindings_win.h index 3950098e5eb..793586d88da 100644 --- a/atom/common/node_bindings_win.h +++ b/atom/common/node_bindings_win.h @@ -12,7 +12,7 @@ namespace atom { class NodeBindingsWin : public NodeBindings { public: - explicit NodeBindingsWin(bool is_browser); + explicit NodeBindingsWin(BrowserEnvironment browser_env); virtual ~NodeBindingsWin(); private: diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index b0099a42550..e1361d223ed 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -122,6 +122,9 @@ const char kBlinkFeatures[] = "blinkFeatures"; // Disable blink features. const char kDisableBlinkFeatures[] = "disableBlinkFeatures"; +// Enable the node integration in WebWorker. +const char kNodeIntegrationInWorker[] = "nodeIntegrationInWorker"; + } // namespace options namespace switches { @@ -165,6 +168,9 @@ const char kScrollBounce[] = "scroll-bounce"; const char kHiddenPage[] = "hidden-page"; const char kNativeWindowOpen[] = "native-window-open"; +// Command switch passed to renderer process to control nodeIntegration. +const char kNodeIntegrationInWorker[] = "node-integration-in-worker"; + // Widevine options // Path to Widevine CDM binaries. const char kWidevineCdmPath[] = "widevine-cdm-path"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 8d0de3d372f..31ebd590261 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -62,6 +62,7 @@ extern const char kOpenerID[]; extern const char kScrollBounce[]; extern const char kBlinkFeatures[]; extern const char kDisableBlinkFeatures[]; +extern const char kNodeIntegrationInWorker[]; } // namespace options @@ -90,6 +91,7 @@ extern const char kOpenerID[]; extern const char kScrollBounce[]; extern const char kHiddenPage[]; extern const char kNativeWindowOpen[]; +extern const char kNodeIntegrationInWorker[]; extern const char kWidevineCdmPath[]; extern const char kWidevineCdmVersion[]; diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 9b3d2214b4c..8dd8e04e1ac 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -12,6 +12,7 @@ #include "atom/common/api/api_messages.h" #include "atom/common/api/atom_bindings.h" #include "atom/common/api/event_emitter_caller.h" +#include "atom/common/asar/asar_util.h" #include "atom/common/atom_constants.h" #include "atom/common/color_util.h" #include "atom/common/native_mate_converters/value_converter.h" @@ -23,6 +24,7 @@ #include "atom/renderer/guest_view_container.h" #include "atom/renderer/node_array_buffer_bridge.h" #include "atom/renderer/preferences_manager.h" +#include "atom/renderer/web_worker_observer.h" #include "base/command_line.h" #include "chrome/renderer/media/chrome_key_systems.h" #include "chrome/renderer/pepper/pepper_helper.h" @@ -215,8 +217,8 @@ std::vector ParseSchemesCLISwitch(const char* switch_name) { AtomRendererClient::AtomRendererClient() : node_integration_initialized_(false), - node_bindings_(NodeBindings::Create(false)), - atom_bindings_(new AtomBindings) { + node_bindings_(NodeBindings::Create(NodeBindings::RENDERER)), + atom_bindings_(new AtomBindings(uv_default_loop())) { isolated_world_ = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kContextIsolation); // Parse --standard-schemes=scheme1,scheme2 @@ -227,6 +229,7 @@ AtomRendererClient::AtomRendererClient() } AtomRendererClient::~AtomRendererClient() { + asar::ClearArchives(); } void AtomRendererClient::RenderThreadStarted() { @@ -436,6 +439,22 @@ void AtomRendererClient::AddSupportedKeySystems( AddChromeKeySystems(key_systems); } +void AtomRendererClient::DidInitializeWorkerContextOnWorkerThread( + v8::Local context) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNodeIntegrationInWorker)) { + WebWorkerObserver::GetCurrent()->ContextCreated(context); + } +} + +void AtomRendererClient::WillDestroyWorkerContextOnWorkerThread( + v8::Local context) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNodeIntegrationInWorker)) { + WebWorkerObserver::GetCurrent()->ContextWillDestroy(context); + } +} + v8::Local AtomRendererClient::GetContext( blink::WebFrame* frame, v8::Isolate* isolate) { if (isolated_world()) diff --git a/atom/renderer/atom_renderer_client.h b/atom/renderer/atom_renderer_client.h index c1d86cddf5e..af8397854e3 100644 --- a/atom/renderer/atom_renderer_client.h +++ b/atom/renderer/atom_renderer_client.h @@ -65,6 +65,10 @@ class AtomRendererClient : public content::ContentRendererClient { void AddSupportedKeySystems( std::vector>* key_systems) override; + void DidInitializeWorkerContextOnWorkerThread( + v8::Local context) override; + void WillDestroyWorkerContextOnWorkerThread( + v8::Local context) override; // Whether the node integration has been initialized. bool node_integration_initialized_; diff --git a/atom/renderer/web_worker_observer.cc b/atom/renderer/web_worker_observer.cc new file mode 100644 index 00000000000..954b2f23614 --- /dev/null +++ b/atom/renderer/web_worker_observer.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/renderer/web_worker_observer.h" + +#include "atom/common/api/atom_bindings.h" +#include "atom/common/api/event_emitter_caller.h" +#include "atom/common/asar/asar_util.h" +#include "atom/common/node_bindings.h" +#include "base/lazy_instance.h" +#include "base/threading/thread_local.h" + +#include "atom/common/node_includes.h" + +namespace atom { + +namespace { + +static base::LazyInstance> + lazy_tls = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +WebWorkerObserver* WebWorkerObserver::GetCurrent() { + WebWorkerObserver* self = lazy_tls.Pointer()->Get(); + return self ? self : new WebWorkerObserver; +} + +WebWorkerObserver::WebWorkerObserver() + : node_bindings_(NodeBindings::Create(NodeBindings::WORKER)), + atom_bindings_(new AtomBindings(node_bindings_->uv_loop())) { + lazy_tls.Pointer()->Set(this); +} + +WebWorkerObserver::~WebWorkerObserver() { + lazy_tls.Pointer()->Set(nullptr); + node::FreeEnvironment(node_bindings_->uv_env()); + asar::ClearArchives(); +} + +void WebWorkerObserver::ContextCreated(v8::Local context) { + v8::Context::Scope context_scope(context); + + // Start the embed thread. + node_bindings_->PrepareMessageLoop(); + + // Setup node environment for each window. + node::Environment* env = node_bindings_->CreateEnvironment(context); + + // Add Electron extended APIs. + atom_bindings_->BindTo(env->isolate(), env->process_object()); + + // Load everything. + node_bindings_->LoadEnvironment(env); + + // Make uv loop being wrapped by window context. + node_bindings_->set_uv_env(env); + + // Give the node loop a run to make sure everything is ready. + node_bindings_->RunMessageLoop(); +} + +void WebWorkerObserver::ContextWillDestroy(v8::Local context) { + node::Environment* env = node::Environment::GetCurrent(context); + if (env) + mate::EmitEvent(env->isolate(), env->process_object(), "exit"); + + delete this; +} + +} // namespace atom diff --git a/atom/renderer/web_worker_observer.h b/atom/renderer/web_worker_observer.h new file mode 100644 index 00000000000..45ccfc3dc96 --- /dev/null +++ b/atom/renderer/web_worker_observer.h @@ -0,0 +1,37 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_RENDERER_WEB_WORKER_OBSERVER_H_ +#define ATOM_RENDERER_WEB_WORKER_OBSERVER_H_ + +#include "base/macros.h" +#include "v8/include/v8.h" + +namespace atom { + +class AtomBindings; +class NodeBindings; + +// Watches for WebWorker and insert node integration to it. +class WebWorkerObserver { + public: + // Returns the WebWorkerObserver for current worker thread. + static WebWorkerObserver* GetCurrent(); + + void ContextCreated(v8::Local context); + void ContextWillDestroy(v8::Local context); + + private: + WebWorkerObserver(); + ~WebWorkerObserver(); + + std::unique_ptr node_bindings_; + std::unique_ptr atom_bindings_; + + DISALLOW_COPY_AND_ASSIGN(WebWorkerObserver); +}; + +} // namespace atom + +#endif // ATOM_RENDERER_WEB_WORKER_OBSERVER_H_ diff --git a/default_app/default_app.js b/default_app/default_app.js index bfb97a9ab08..2a3ce3a85ec 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -15,6 +15,9 @@ exports.load = (appUrl) => { height: 600, autoHideMenuBar: true, backgroundColor: '#FFFFFF', + webPreferences: { + nodeIntegrationInWorker: true + }, useContentSize: true } if (process.platform === 'linux') { diff --git a/docs-translations/es/project/README.md b/docs-translations/es/project/README.md index c62c5c7dc69..1aac1d3c780 100644 --- a/docs-translations/es/project/README.md +++ b/docs-translations/es/project/README.md @@ -5,19 +5,19 @@ [![devDependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron?type=dev) [![Join the Electron Community on Slack](http://atom-slack.herokuapp.com/badge.svg)](http://atom-slack.herokuapp.com/) -:memo: Traducciones disponibles: [Koreano](https://github.com/electron/electron/tree/master/docs-translations/ko-KR/project/README.md) | [Chino Simplificado](https://github.com/electron/electron/tree/master/docs-translations/zh-CN/project/README.md) | [Portugues Brasileño](https://github.com/electron/electron/tree/master/docs-translations/pt-BR/project/README.md) | [Chino Tradicional](https://github.com/electron/electron/tree/master/docs-translations/zh-TW/project/README.md) +:memo: Traducciones disponibles: [Koreano](https://github.com/electron/electron/tree/master/docs-translations/ko-KR/project/README.md) | [Chino Simplificado](https://github.com/electron/electron/tree/master/docs-translations/zh-CN/project/README.md) | [Portugués Brasileño](https://github.com/electron/electron/tree/master/docs-translations/pt-BR/project/README.md) | [Chino Tradicional](https://github.com/electron/electron/tree/master/docs-translations/zh-TW/project/README.md) -Electron es framework que permite escribir aplicaciones de escritorio multiplataforma -usando JavaScript, HTML y CSS. Está basado en [Node.js](https://nodejs.org/) con -[Chromium](http://www.chromium.org). Es usado por [Atom +Electron es un framework que permite escribir aplicaciones de escritorio multiplataforma +usando JavaScript, HTML y CSS. Está basado en [Node.js](https://nodejs.org/) con +[Chromium](http://www.chromium.org). Es usado por [Atom editor](https://github.com/atom/atom) y muchas otras [aplicaciones](https://electron.atom.io/apps). Sigue a [@ElectronJS](https://twitter.com/electronjs) en Twitter para estar informado de anuncios importantes. -Este projecto se adhiere a [Código de Conducta convenido para Contribuyentes](CODE_OF_CONDUCT.md). +Este proyecto se adhiere al [Código de Conducta convenido para Colaboradores](CODE_OF_CONDUCT.md). Si desea participar, debes seguir este código de conducta. Por favor reporta un comportamiento -no aceptado a electron@github.com. +no aceptado a electron@github.com. ## Downloads @@ -33,7 +33,7 @@ npm install electron -g ``` Mira la [página de lanzamientos](https://github.com/electron/electron/releases) para -los prebuilt binaries, debug symbols, and more. +los prebuilt binaries, debug symbols, y más. ### Mirrors @@ -41,13 +41,13 @@ los prebuilt binaries, debug symbols, and more. ## Documentación -Las guias y API de referencia están disponibles en el directorio +Las guías y API de referencia están disponibles en el directorio [docs](https://github.com/electron/electron/tree/master/docs). Ahí también -puedes encontrar documentos que describen como construir y contribuir a Electron. +puedes encontrar documentos que describen cómo construir y contribuir en Electron. ## Traducciones de la Documentación -- [Portugues Brasileño](https://github.com/electron/electron/tree/master/docs-translations/pt-BR) +- [Portugués Brasileño](https://github.com/electron/electron/tree/master/docs-translations/pt-BR) - [Koreano](https://github.com/electron/electron/tree/master/docs-translations/ko-KR) - [Japonés](https://github.com/electron/electron/tree/master/docs-translations/jp) - [Español](https://github.com/electron/electron/tree/master/docs-translations/es) @@ -57,12 +57,12 @@ puedes encontrar documentos que describen como construir y contribuir a Electron - [Thai](https://github.com/electron/electron/tree/master/docs-Translations/th-TH) - [Ucraniano](https://github.com/electron/electron/tree/master/docs-translations/uk-UA) - [Ruso](https://github.com/electron/electron/tree/master/docs-translations/ru-RU) -- [Frances](https://github.com/electron/electron/tree/master/docs-translations/fr-FR) +- [Francés](https://github.com/electron/electron/tree/master/docs-translations/fr-FR) ## Inicio rápido Clona y ejecuta el repositorio [`electron/electron-quick-start`](https://github.com/electron/electron-quick-start) -para ver una aplicación minima en acción. +para ver una aplicación mínima en acción. ## Comunidad @@ -71,7 +71,7 @@ Puedes preguntar y interactuar con la comunidad en los siguientes lugares: Atom. - `#atom-shell` canal de IRC en Freenode - [`Atom`](http://atom-slack.herokuapp.com/) canales en Slack -- [`electron-br`](https://electron-br.slack.com) *(Portugues Brasileño)* +- [`electron-br`](https://electron-br.slack.com) *(Portugués Brasileño)* - [`electron-kr`](http://www.meetup.com/electron-kr/) *(Koreano)* - [`electron-jp`](https://electron-jp.slack.com) *(Japonés)* - [`electron-tr`](http://www.meetup.com/Electron-JS-Istanbul/) *(Turco)* @@ -84,4 +84,4 @@ donde la comunidad mantiene una lista útil de ejemplos de aplicaciones, herrami [MIT](https://github.com/electron/electron/blob/master/LICENSE) -Si usas los logos de Electron ó GitHub, asegurate de seguir las [GitHub logo guidelines](https://github.com/logos). +Si usas los logos de Electron ó GitHub, asegúrate de seguir las [GitHub logo guidelines](https://github.com/logos). diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index fcb792fe4d3..a21033df401 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -35,9 +35,9 @@ without visual flash, there are two solutions for different situations. ### Using `ready-to-show` event -While loading the page, the `ready-to-show` event will be emitted when renderer -process has done drawing for the first time, showing window after this event -will have no visual flash: +While loading the page, the `ready-to-show` event will be emitted when the renderer +process has rendered the page for the first time if the window has not been shown yet. Showing +the window after this event will have no visual flash: ```javascript const {BrowserWindow} = require('electron') @@ -215,6 +215,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. * `devTools` Boolean (optional) - Whether to enable DevTools. If it is set to `false`, can not use `BrowserWindow.webContents.openDevTools()` to open DevTools. Default is `true`. * `nodeIntegration` Boolean (optional) - Whether node integration is enabled. Default is `true`. + * `nodeIntegrationInWorker` Boolean (optional) - Whether node integration is + enabled in web workers. Default is `false`. More about this can be found + in [Multithreading](../tutorial/multithreading.md). * `preload` String (optional) - Specifies a script that will be loaded before other scripts run in the page. This script will always have access to node APIs no matter whether node integration is turned on or off. The value should @@ -391,7 +394,7 @@ Emitted when the window is hidden. #### Event: 'ready-to-show' -Emitted when the web page has been rendered and window can be displayed without +Emitted when the web page has been rendered (while not being shown) and window can be displayed without a visual flash. #### Event: 'maximize' @@ -633,7 +636,8 @@ Returns `Boolean` - Whether current window is a modal window. #### `win.maximize()` -Maximizes the window. +Maximizes the window. This will also show (but not focus) the window if it +isn't being displayed already. #### `win.unmaximize()` diff --git a/docs/development/build-instructions-linux.md b/docs/development/build-instructions-linux.md index 42b7730a84a..e6b5004bf28 100644 --- a/docs/development/build-instructions-linux.md +++ b/docs/development/build-instructions-linux.md @@ -117,6 +117,14 @@ To clean the build files: $ npm run clean ``` +To clean only `out` and `dist` directories: + +```bash +$ npm run clean-build +``` + +**Note:** Both clean commands require running `bootstrap` again before building. + ## Troubleshooting ### Error While Loading Shared Libraries: libtinfo.so.5 diff --git a/docs/development/build-instructions-osx.md b/docs/development/build-instructions-osx.md index 7b934cd55b2..d0e95aaf18c 100644 --- a/docs/development/build-instructions-osx.md +++ b/docs/development/build-instructions-osx.md @@ -85,6 +85,14 @@ To clean the build files: $ npm run clean ``` +To clean only `out` and `dist` directories: + +```bash +$ npm run clean-build +``` + +**Note:** Both clean commands require running `bootstrap` again before building. + ## Tests See [Build System Overview: Tests](build-system-overview.md#tests) diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 2146abad97b..4f7335e108f 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -83,6 +83,14 @@ To clean the build files: $ npm run clean ``` +To clean only `out` and `dist` directories: + +```bash +$ npm run clean-build +``` + +**Note:** Both clean commands require running `bootstrap` again before building. + ## Tests See [Build System Overview: Tests](build-system-overview.md#tests) diff --git a/docs/tutorial/multithreading.md b/docs/tutorial/multithreading.md new file mode 100644 index 00000000000..b0d298e57e2 --- /dev/null +++ b/docs/tutorial/multithreading.md @@ -0,0 +1,50 @@ +# Multithreading + +With [Web Workers][web-workers], it is possible to run JavaScript in OS-level +threads. + +## Multi-threaded Node.js + +It is possible to use Node.js features in Electron's Web Workers, to do +so the `nodeIntegrationInWorker` option should be set to `true` in +`webPreferences`. + +```javascript +let win = new BrowserWindow({ + webPreferences: { + nodeIntegrationInWorker: true + } +}) +``` + +The `nodeIntegrationInWorker` can be used independent of `nodeIntegration`, but +`sandbox` must not be set to `true`. + +## Available APIs + +All built-in modules of Node.js are supported in Web Workers, and `asar` +archives can still be read with Node.js APIs. However none of Electron's +built-in modules can be used in a multi-threaded environment. + +## Native Node.js modules + +Any native Node.js module can be loaded directly in Web Workers, but it is +strongly recommended not to do so. Most existing native modules have been +written assuming single-threaded environment, using them in Web Workers will +lead to crashes and memory corruptions. + +Note that even if a native Node.js module is thread-safe it's still not safe to +load it in a Web Worker because the `process.dlopen` function is not thread +safe. + +The only way to load a native module safely for now, is to make sure the app +loads no native modules after the Web Workers get started. + +```javascript +process.dlopen = () => { + throw new Error('Load native module is not safe') +} +let worker = new Worker('script.js') +``` + +[web-workers]: https://developer.mozilla.org/en/docs/Web/API/Web_Workers_API/Using_web_workers diff --git a/electron.gyp b/electron.gyp index ab9810bfc02..15336a4e6bf 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.6.3', + 'version%': '1.6.4', 'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c', }, 'includes': [ diff --git a/filenames.gypi b/filenames.gypi index 000e08c0a30..3b557fcb88a 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -76,6 +76,7 @@ 'lib/renderer/extensions/i18n.js', 'lib/renderer/extensions/storage.js', 'lib/renderer/extensions/web-navigation.js', + 'lib/worker/init.js', ], 'js2c_sources': [ 'lib/common/asar.js', @@ -479,6 +480,8 @@ 'atom/renderer/node_array_buffer_bridge.h', 'atom/renderer/preferences_manager.cc', 'atom/renderer/preferences_manager.h', + 'atom/renderer/web_worker_observer.cc', + 'atom/renderer/web_worker_observer.h', 'atom/utility/atom_content_utility_client.cc', 'atom/utility/atom_content_utility_client.h', 'chromium_src/chrome/browser/browser_process.cc', diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index 9615962ac66..99d650d773d 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -144,7 +144,7 @@ Menu.prototype._init = function () { } Menu.prototype.popup = function (window, x, y, positioningItem) { - let asyncPopup = false + let asyncPopup // menu.popup(x, y, positioningItem) if (window != null && (typeof window !== 'object' || window.constructor !== BrowserWindow)) { @@ -174,6 +174,9 @@ Menu.prototype.popup = function (window, x, y, positioningItem) { // Default to not highlighting any item. if (typeof positioningItem !== 'number') positioningItem = -1 + // Default to synchronous for backwards compatibility. + if (typeof asyncPopup !== 'boolean') asyncPopup = false + this.popupAt(window, x, y, positioningItem, asyncPopup) } diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index dea3f404a06..b3d3d2ab17c 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -179,7 +179,15 @@ const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) { }, get: (target, property, receiver) => { if (!target.hasOwnProperty(property)) loadRemoteProperties() - return target[property] + const value = target[property] + + // Bind toString to target if it is a function to avoid + // Function.prototype.toString is not generic errors + if (property === 'toString' && typeof value === 'function') { + return value.bind(target) + } + + return value }, ownKeys: (target) => { loadRemoteProperties() diff --git a/lib/renderer/init.js b/lib/renderer/init.js index 892be116d9a..38441c9ec14 100644 --- a/lib/renderer/init.js +++ b/lib/renderer/init.js @@ -6,7 +6,7 @@ const Module = require('module') const resolvePromise = Promise.resolve.bind(Promise) // We modified the original process.argv to let node.js load the -// atom-renderer.js, we need to restore it here. +// init.js, we need to restore it here. process.argv.splice(1, 1) // Clear search paths. diff --git a/lib/worker/init.js b/lib/worker/init.js new file mode 100644 index 00000000000..6a38ce85dc8 --- /dev/null +++ b/lib/worker/init.js @@ -0,0 +1,37 @@ +'use strict' + +const path = require('path') +const Module = require('module') + +// We modified the original process.argv to let node.js load the +// init.js, we need to restore it here. +process.argv.splice(1, 1) + +// Clear search paths. +require('../common/reset-search-paths') + +// Import common settings. +require('../common/init') + +// Expose public APIs. +Module.globalPaths.push(path.join(__dirname, 'api', 'exports')) + +// Export node bindings to global. +global.require = require +global.module = module + +// Set the __filename to the path of html file if it is file: protocol. +if (self.location.protocol === 'file:') { + let pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname + global.__filename = path.normalize(decodeURIComponent(pathname)) + global.__dirname = path.dirname(global.__filename) + + // Set module's filename so relative require can work as expected. + module.filename = global.__filename + + // Also search for module under the html file. + module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname)) +} else { + global.__filename = __filename + global.__dirname = __dirname +} diff --git a/package.json b/package.json index a572a1918ef..d321d7831f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "1.6.3", + "version": "1.6.4", "devDependencies": { "asar": "^0.11.0", "browserify": "^13.1.0", @@ -30,6 +30,7 @@ "bump-version": "./script/bump-version.py", "build": "python ./script/build.py -c D", "clean": "python ./script/clean.py", + "clean-build": "python ./script/clean.py --build", "coverage": "npm run instrument-code-coverage && npm test -- --use-instrumented-asar", "instrument-code-coverage": "electabul instrument --input-path ./lib --output-path ./out/coverage/electron.asar", "lint": "npm run lint-js && npm run lint-cpp && npm run lint-py && npm run lint-api-docs-js && npm run lint-api-docs", diff --git a/script/clean.py b/script/clean.py index 669b6b4dd5f..c63bdbbadee 100755 --- a/script/clean.py +++ b/script/clean.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import argparse import os import sys @@ -11,13 +12,34 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) def main(): os.chdir(SOURCE_ROOT) - rm_rf('node_modules') - rm_rf('dist') - rm_rf('out') - rm_rf('spec/node_modules') - rm_rf('vendor/brightray/vendor/download/libchromiumcontent') - rm_rf('vendor/brightray/vendor/libchromiumcontent/src') - rm_rf(os.path.expanduser('~/.node-gyp')) + + args = parse_args() + + remove_directory('dist') + remove_directory('out') + + if not args.build: + remove_directory('node_modules') + remove_directory('spec/node_modules') + + remove_directory('vendor/brightray/vendor/download/libchromiumcontent') + remove_directory('vendor/brightray/vendor/libchromiumcontent/src') + + remove_directory(os.path.expanduser('~/.node-gyp')) + + +def parse_args(): + parser = argparse.ArgumentParser(description='Remove generated and' \ + 'downloaded build files') + parser.add_argument('-b', '--build', + help='Only remove out and dist directories', + action='store_true') + return parser.parse_args() + + +def remove_directory(directory): + print 'Removing %s' % directory + rm_rf(directory) if __name__ == '__main__': diff --git a/spec/api-ipc-spec.js b/spec/api-ipc-spec.js index e812eb5fef2..47f2eef2168 100644 --- a/spec/api-ipc-spec.js +++ b/spec/api-ipc-spec.js @@ -161,6 +161,14 @@ describe('ipc module', function () { assert.equal(typeof remote.clipboard.readText, 'function') assert.equal(typeof remote.shell.openExternal, 'function') }) + + it('returns toString() of original function via toString()', function () { + const {readText} = remote.clipboard + assert(readText.toString().startsWith('function')) + + var {functionWithToStringProperty} = remote.require(path.join(fixtures, 'module', 'to-string-non-function.js')) + assert.equal(functionWithToStringProperty.toString, 'hello') + }) }) describe('remote object in renderer', function () { diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index dd06f4455e1..a54c6744f7c 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -590,6 +590,27 @@ describe('chromium feature', function () { worker.postMessage(message) }) + it('Worker has no node integration by default', function (done) { + let worker = new Worker('../fixtures/workers/worker_node.js') + worker.onmessage = function (event) { + assert.equal(event.data, 'undefined undefined undefined undefined') + worker.terminate() + done() + } + }) + + it('Worker has node integration with nodeIntegrationInWorker', function (done) { + let webview = new WebView() + webview.addEventListener('ipc-message', function (e) { + assert.equal(e.channel, 'object function object function') + webview.remove() + done() + }) + webview.src = 'file://' + fixtures + '/pages/worker.html' + webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker') + document.body.appendChild(webview) + }) + it('SharedWorker can work', function (done) { var worker = new SharedWorker('../fixtures/workers/shared_worker.js') var message = 'ping' @@ -599,6 +620,29 @@ describe('chromium feature', function () { } worker.port.postMessage(message) }) + + it('SharedWorker has no node integration by default', function (done) { + let worker = new SharedWorker('../fixtures/workers/shared_worker_node.js') + worker.port.onmessage = function (event) { + assert.equal(event.data, 'undefined undefined undefined undefined') + done() + } + }) + + it('SharedWorker has node integration with nodeIntegrationInWorker', function (done) { + let webview = new WebView() + webview.addEventListener('console-message', function (e) { + console.log(e) + }) + webview.addEventListener('ipc-message', function (e) { + assert.equal(e.channel, 'object function object function') + webview.remove() + done() + }) + webview.src = 'file://' + fixtures + '/pages/shared_worker.html' + webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker') + document.body.appendChild(webview) + }) }) describe('iframe', function () { diff --git a/spec/fixtures/module/to-string-non-function.js b/spec/fixtures/module/to-string-non-function.js new file mode 100644 index 00000000000..3e91921037c --- /dev/null +++ b/spec/fixtures/module/to-string-non-function.js @@ -0,0 +1,4 @@ +function hello () { +} +hello.toString = 'hello' +module.exports = {functionWithToStringProperty: hello} diff --git a/spec/fixtures/pages/shared_worker.html b/spec/fixtures/pages/shared_worker.html new file mode 100644 index 00000000000..7a0d0757ab2 --- /dev/null +++ b/spec/fixtures/pages/shared_worker.html @@ -0,0 +1,12 @@ + + + + + diff --git a/spec/fixtures/pages/worker.html b/spec/fixtures/pages/worker.html new file mode 100644 index 00000000000..c84ef52065e --- /dev/null +++ b/spec/fixtures/pages/worker.html @@ -0,0 +1,12 @@ + + + + + diff --git a/spec/fixtures/workers/shared_worker_node.js b/spec/fixtures/workers/shared_worker_node.js new file mode 100644 index 00000000000..0a52d60fbf5 --- /dev/null +++ b/spec/fixtures/workers/shared_worker_node.js @@ -0,0 +1,5 @@ +self.onconnect = function (event) { + let port = event.ports[0] + port.start() + port.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')) +} diff --git a/spec/fixtures/workers/worker_node.js b/spec/fixtures/workers/worker_node.js new file mode 100644 index 00000000000..5d59d2d0c34 --- /dev/null +++ b/spec/fixtures/workers/worker_node.js @@ -0,0 +1 @@ +self.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')) diff --git a/vendor/native_mate b/vendor/native_mate index ad0fd825663..fd0e7dc4ab7 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit ad0fd825663932ee3fa29ff935dfec99933bdd8c +Subproject commit fd0e7dc4ab778f0d1ccda6c9640464ea06ee771e