diff --git a/app/atom_main_delegate.cc b/app/atom_main_delegate.cc index 1f3d9cb69039..47718e22baa3 100644 --- a/app/atom_main_delegate.cc +++ b/app/atom_main_delegate.cc @@ -54,6 +54,9 @@ void AtomMainDelegate::PreSandboxStartup() { // Disable accelerated compositing since it caused a lot of troubles (black // devtools, screen flashes) and needed lots of effort to make it right. command_line->AppendSwitch(switches::kDisableAcceleratedCompositing); + + // Add a flag to mark the end of switches added by atom-shell. + command_line->AppendSwitch("no-more-switches"); } void AtomMainDelegate::InitializeResourceBundle() { diff --git a/atom.gyp b/atom.gyp index 3a03dd51165b..63562cfa4e71 100644 --- a/atom.gyp +++ b/atom.gyp @@ -21,15 +21,15 @@ 'browser/api/lib/menu-item.coffee', 'browser/api/lib/power-monitor.coffee', 'browser/api/lib/protocol.coffee', - 'browser/atom/atom.coffee', - 'browser/atom/atom-renderer.coffee', - 'browser/atom/objects-registry.coffee', - 'browser/atom/rpc-server.coffee', + 'browser/lib/init.coffee', + 'browser/lib/objects-registry.coffee', + 'browser/lib/rpc-server.coffee', 'common/api/lib/callbacks-registry.coffee', 'common/api/lib/clipboard.coffee', 'common/api/lib/crash-reporter.coffee', 'common/api/lib/id-weak-map.coffee', 'common/api/lib/shell.coffee', + 'renderer/lib/init.coffee', 'renderer/api/lib/ipc.coffee', 'renderer/api/lib/remote.coffee', ], diff --git a/browser/api/lib/app.coffee b/browser/api/lib/app.coffee index 9fc9664ed423..e578c3ef7d3c 100644 --- a/browser/api/lib/app.coffee +++ b/browser/api/lib/app.coffee @@ -10,7 +10,7 @@ app.getHomeDir = -> process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME'] app.getBrowserWindows = -> - require('../../atom/objects-registry.js').getAllWindows() + require('../../lib/objects-registry.js').getAllWindows() app.setApplicationMenu = (menu) -> require('menu').setApplicationMenu menu diff --git a/browser/atom/atom-renderer.coffee b/browser/atom/atom-renderer.coffee deleted file mode 100644 index 8430d183340d..000000000000 --- a/browser/atom/atom-renderer.coffee +++ /dev/null @@ -1,5 +0,0 @@ -path = require 'path' - -process.__atom_type = 'renderer' -process.resourcesPath = path.resolve process.argv[1], '..', '..', '..' -process.argv.splice 1, 1 diff --git a/browser/atom_browser_main_parts.cc b/browser/atom_browser_main_parts.cc index 2328fda1bb29..3c3b2e70196e 100644 --- a/browser/atom_browser_main_parts.cc +++ b/browser/atom_browser_main_parts.cc @@ -43,9 +43,19 @@ void AtomBrowserMainParts::PostEarlyInitialization() { node_bindings_->Initialize(); - // Wrap whole process in one global context. - global_env->context()->Enter(); + v8::V8::Initialize(); + // Create context. + v8::HandleScope handle_scope(node_isolate); + v8::Local context = v8::Context::New(node_isolate); + + // Wrap whole process in one global context. + context->Enter(); + + // Create the global environment. + global_env = node_bindings_->CreateEnvironment(context); + + // Add atom-shell extended APIs. atom_bindings_->BindTo(global_env->process_object()); atom_bindings_->AfterLoad(); } diff --git a/browser/atom/atom.coffee b/browser/lib/init.coffee similarity index 94% rename from browser/atom/atom.coffee rename to browser/lib/init.coffee index 6212dd0b2959..a9393f16aa5d 100644 --- a/browser/atom/atom.coffee +++ b/browser/lib/init.coffee @@ -1,9 +1,12 @@ fs = require 'fs' path = require 'path' -# Restore the proces.argv. +# Expose information of current process. process.__atom_type = 'browser' process.resourcesPath = path.resolve process.argv[1], '..', '..', '..' + +# We modified the original process.argv to let node.js load the atom.js, +# we need to restore it here. process.argv.splice 1, 1 if process.platform is 'win32' diff --git a/browser/atom/objects-registry.coffee b/browser/lib/objects-registry.coffee similarity index 100% rename from browser/atom/objects-registry.coffee rename to browser/lib/objects-registry.coffee diff --git a/browser/atom/rpc-server.coffee b/browser/lib/rpc-server.coffee similarity index 100% rename from browser/atom/rpc-server.coffee rename to browser/lib/rpc-server.coffee diff --git a/common/node_bindings.cc b/common/node_bindings.cc index 9bda801b95f2..a2fe827d71a0 100644 --- a/common/node_bindings.cc +++ b/common/node_bindings.cc @@ -13,9 +13,6 @@ #include "base/path_service.h" #include "common/v8/native_type_conversions.h" #include "content/public/browser/browser_thread.h" -#include "net/base/escape.h" -#include "third_party/WebKit/public/web/WebDocument.h" -#include "third_party/WebKit/public/web/WebFrame.h" #include "vendor/node/src/node_javascript.h" #if defined(OS_WIN) @@ -28,24 +25,42 @@ using content::BrowserThread; // Forward declaration of internal node functions. namespace node { -void Init(int* argc, - const char** argv, - int* exec_argc, - const char*** exec_argv); -Environment* CreateEnvironment(v8::Isolate* isolate, - int argc, - const char* const* argv, - int exec_argc, - const char* const* exec_argv); +void Init(int*, const char**, int*, const char***); +void Load(Environment* env); +void SetupProcessObject(Environment*, int, const char* const*, int, + const char* const*); } namespace atom { namespace { +// Empty callback for async handle. void UvNoOp(uv_async_t* handle, int status) { } +// Convert the given vector to an array of C-strings. The strings in the +// returned vector are only guaranteed valid so long as the vector of strings +// is not modified. +scoped_ptr StringVectorToArgArray( + const std::vector& vector) { + scoped_ptr array(new const char*[vector.size()]); + for (size_t i = 0; i < vector.size(); ++i) + array[i] = vector[i].c_str(); + return array.Pass(); +} + +#if defined(OS_WIN) +std::vector String16VectorToStringVector( + const std::vector& vector) { + std::vector utf8_vector; + utf8_vector.reserve(vector.size()); + for (size_t i = 0; i < vector.size(); ++i) + utf8_vector.push_back(UTF16ToUTF8(vector[i])); + return utf8_vector; +} +#endif + } // namespace node::Environment* global_env = NULL; @@ -73,42 +88,6 @@ NodeBindings::~NodeBindings() { } void NodeBindings::Initialize() { - CommandLine::StringVector str_argv = CommandLine::ForCurrentProcess()->argv(); - -#if defined(OS_WIN) - std::vector utf8_str_argv; - utf8_str_argv.reserve(str_argv.size()); -#endif - - // Convert string vector to const char* array. - std::vector args(str_argv.size(), NULL); - for (size_t i = 0; i < str_argv.size(); ++i) { -#if defined(OS_WIN) - utf8_str_argv.push_back(UTF16ToUTF8(str_argv[i])); - args[i] = utf8_str_argv[i].c_str(); -#else - args[i] = str_argv[i].c_str(); -#endif - } - - // Feed node the path to initialization script. - base::FilePath exec_path(str_argv[0]); - PathService::Get(base::FILE_EXE, &exec_path); - base::FilePath resources_path = -#if defined(OS_MACOSX) - is_browser_ ? exec_path.DirName().DirName().Append("Resources") : - exec_path.DirName().DirName().DirName().DirName().DirName() - .Append("Resources"); -#else - exec_path.DirName().Append("resources"); -#endif - base::FilePath script_path = - resources_path.AppendASCII("browser") - .AppendASCII("atom") - .AppendASCII(is_browser_ ? "atom.js" : "atom-renderer.js"); - std::string script_path_str = script_path.AsUTF8Unsafe(); - args.insert(args.begin() + 1, script_path_str.c_str()); - // Init idle GC for browser. if (is_browser_) { uv_timer_init(uv_default_loop(), &idle_timer_); @@ -120,43 +99,76 @@ void NodeBindings::Initialize() { node::g_upstream_node_mode = false; // Init node. - int argc = args.size(); - const char** argv = &args[0]; - int exec_argc; - const char** exec_argv; - node::Init(&argc, argv, &exec_argc, &exec_argv); - v8::V8::Initialize(); - - // Create environment (setup process object and load node.js). - global_env = node::CreateEnvironment(node_isolate, argc, argv, argc, argv); + // (we assume it would not node::Init would not modify the parameters under + // embedded mode). + node::Init(NULL, NULL, NULL, NULL); } -void NodeBindings::BindTo(WebKit::WebFrame* frame) { - v8::HandleScope handle_scope(node_isolate); +node::Environment* NodeBindings::CreateEnvironment( + v8::Handle context) { + std::vector args = +#if defined(OS_WIN) + String16VectorToStringVector(CommandLine::ForCurrentProcess()->argv()); +#else + CommandLine::ForCurrentProcess()->argv(); +#endif - v8::Handle context = frame->mainWorldScriptContext(); - if (context.IsEmpty()) - return; + // Feed node the path to initialization script. + base::FilePath exec_path(args[0]); + PathService::Get(base::FILE_EXE, &exec_path); + base::FilePath resources_path = +#if defined(OS_MACOSX) + is_browser_ ? exec_path.DirName().DirName().Append("Resources") : + exec_path.DirName().DirName().DirName().DirName().DirName() + .Append("Resources"); +#else + exec_path.DirName().Append("resources"); +#endif + base::FilePath script_path = + resources_path.AppendASCII(is_browser_ ? "browser" : "renderer") + .AppendASCII("lib") + .AppendASCII("init.js"); + std::string script_path_str = script_path.AsUTF8Unsafe(); + args.insert(args.begin() + 1, script_path_str.c_str()); - v8::Context::Scope scope(context); + // Convert string vector to const char* array. + scoped_ptr c_argv = StringVectorToArgArray(args); - // Erase security token. - context->SetSecurityToken(global_env->context()->GetSecurityToken()); + // Construct the parameters that passed to node::CreateEnvironment: + v8::Isolate* isolate = context->GetIsolate(); + int argc = args.size(); + const char** argv = c_argv.get(); + int exec_argc = 0; + const char** exec_argv = NULL; - // Evaluate cefode.js. - v8::Handle script = node::CompileCefodeMainSource(); - v8::Local result = script->Run(); + using namespace v8; // NOLINT + using namespace node; // NOLINT - // Run the script of cefode.js. - std::string script_path(GURL(frame->document().url()).path()); - script_path = net::UnescapeURLComponent( - script_path, - net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS); - v8::Handle args[2] = { - global_env->process_object(), - ToV8Value(script_path), - }; - v8::Local::Cast(result)->Call(context->Global(), 2, args); + // Following code are stripped from node::CreateEnvironment in node.cc: + HandleScope handle_scope(isolate); + + Environment* env = Environment::New(context); + + uv_check_init(env->event_loop(), env->immediate_check_handle()); + uv_unref( + reinterpret_cast(env->immediate_check_handle())); + uv_idle_init(env->event_loop(), env->immediate_idle_handle()); + + uv_prepare_init(env->event_loop(), env->idle_prepare_handle()); + uv_check_init(env->event_loop(), env->idle_check_handle()); + uv_unref(reinterpret_cast(env->idle_prepare_handle())); + uv_unref(reinterpret_cast(env->idle_check_handle())); + + Local process_template = FunctionTemplate::New(); + process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process")); + + Local process_object = process_template->GetFunction()->NewInstance(); + env->set_process_object(process_object); + + SetupProcessObject(env, argc, argv, exec_argc, exec_argv); + Load(env); + + return env; } void NodeBindings::PrepareMessageLoop() { diff --git a/common/node_bindings.h b/common/node_bindings.h index a9ac5852a129..7ed22806d115 100644 --- a/common/node_bindings.h +++ b/common/node_bindings.h @@ -2,20 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef RAVE_COMMON_NODE_BINDINGS_ -#define RAVE_COMMON_NODE_BINDINGS_ +#ifndef ATOM_COMMON_NODE_BINDINGS_H_ +#define ATOM_COMMON_NODE_BINDINGS_H_ #include "base/basictypes.h" +#include "v8/include/v8.h" #include "vendor/node/deps/uv/include/uv.h" -namespace WebKit { -class WebFrame; -} - namespace base { class MessageLoop; } +namespace node { +class Environment; +} + namespace atom { class NodeBindings { @@ -24,11 +25,11 @@ class NodeBindings { virtual ~NodeBindings(); - // Setup V8, libuv and the process object, then load the node.js script. + // Setup V8, libuv. virtual void Initialize(); - // Load cefode.js script under web frame. - virtual void BindTo(WebKit::WebFrame* frame); + // Create the environment and load node.js. + virtual node::Environment* CreateEnvironment(v8::Handle context); // Prepare for message loop integration. virtual void PrepareMessageLoop(); @@ -87,4 +88,4 @@ class NodeBindings { } // namespace atom -#endif // RAVE_COMMON_NODE_BINDINGS_ +#endif // ATOM_COMMON_NODE_BINDINGS_H_ diff --git a/common/v8/node_common.h b/common/v8/node_common.h index 5b8147f93122..7174b902a59e 100644 --- a/common/v8/node_common.h +++ b/common/v8/node_common.h @@ -5,7 +5,7 @@ #ifndef ATOM_COMMON_V8_NODE_COMMON_H_ #define ATOM_COMMON_V8_NODE_COMMON_H_ -// Common helper for using node APIs. +// Include common headers for using node APIs. #undef CHECK #undef DISALLOW_COPY_AND_ASSIGN @@ -16,6 +16,8 @@ using node::node_isolate; namespace atom { // Defined in node_bindings.cc. +// For renderer it's created in atom_renderer_client.cc. +// For browser it's created in atom_browser_main_parts.cc. extern node::Environment* global_env; } diff --git a/renderer/atom_render_view_observer.cc b/renderer/atom_render_view_observer.cc index dd4dfbe553db..673435e048e1 100644 --- a/renderer/atom_render_view_observer.cc +++ b/renderer/atom_render_view_observer.cc @@ -4,9 +4,6 @@ #include "renderer/atom_render_view_observer.h" -#include -#include - #include "common/api/api_messages.h" #include "common/node_bindings.h" #include "ipc/ipc_message_macros.h" @@ -15,69 +12,47 @@ #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebDraggableRegion.h" #include "third_party/WebKit/public/web/WebFrame.h" -#include "v8/include/v8.h" + +#include "common/v8/node_common.h" using WebKit::WebFrame; -namespace webkit { -extern void SetGetFirstWindowContext(v8::Handle (*)()); -extern void SetIsValidWindowContext(bool (*)(v8::Handle)); -} - namespace atom { -namespace { - -std::vector& web_frames() { - CR_DEFINE_STATIC_LOCAL(std::vector, frames, ()); - return frames; -} - -v8::Handle GetFirstWindowContext() { - if (web_frames().size() == 0) - return v8::Handle(); - - return web_frames()[0]->mainWorldScriptContext(); -} - -bool IsValidWindowContext(v8::Handle context) { - size_t size = web_frames().size(); - for (size_t i = 0; i < size; ++i) - if (web_frames()[i]->mainWorldScriptContext() == context) - return true; - - return false; -} - -} // namespace - AtomRenderViewObserver::AtomRenderViewObserver( content::RenderView* render_view, AtomRendererClient* renderer_client) : content::RenderViewObserver(render_view), atom_bindings_(new AtomRendererBindings(render_view)), renderer_client_(renderer_client) { - // Interact with dirty workarounds of extra node context in WebKit. - webkit::SetGetFirstWindowContext(GetFirstWindowContext); - webkit::SetIsValidWindowContext(IsValidWindowContext); } AtomRenderViewObserver::~AtomRenderViewObserver() { } void AtomRenderViewObserver::DidClearWindowObject(WebFrame* frame) { - // Remember the web frame. - web_frames().push_back(frame); + // Get the context. + v8::HandleScope handle_scope(node_isolate); + v8::Handle context = frame->mainWorldScriptContext(); + if (context.IsEmpty()) + return; - renderer_client_->node_bindings()->BindTo(frame); + v8::Context::Scope scope(context); + + // Check the existance of process object to prevent duplicate initialization. + if (context->Global()->Has(v8::String::New("process"))) + return; + + // Give the node loop a run to make sure everything is ready. + renderer_client_->node_bindings()->RunMessageLoop(); + + // Setup node environment for each window. + renderer_client_->node_bindings()->CreateEnvironment(context); + + // Add atom-shell extended APIs. atom_bindings()->BindToFrame(frame); } -void AtomRenderViewObserver::FrameWillClose(WebFrame* frame) { - std::vector& vec = web_frames(); - vec.erase(std::remove(vec.begin(), vec.end(), frame), vec.end()); -} - void AtomRenderViewObserver::DraggableRegionsChanged(WebKit::WebFrame* frame) { WebKit::WebVector webregions = frame->document().draggableRegions(); diff --git a/renderer/atom_render_view_observer.h b/renderer/atom_render_view_observer.h index d8d90e0cb4b1..12c15fa63675 100644 --- a/renderer/atom_render_view_observer.h +++ b/renderer/atom_render_view_observer.h @@ -28,7 +28,6 @@ class AtomRenderViewObserver : content::RenderViewObserver { virtual ~AtomRenderViewObserver(); virtual void DidClearWindowObject(WebKit::WebFrame*) OVERRIDE; - virtual void FrameWillClose(WebKit::WebFrame*) OVERRIDE; private: // content::RenderViewObserver implementation. diff --git a/renderer/atom_renderer_client.cc b/renderer/atom_renderer_client.cc index 183d2d57ca4c..3574b02cacad 100644 --- a/renderer/atom_renderer_client.cc +++ b/renderer/atom_renderer_client.cc @@ -9,20 +9,8 @@ #include "common/v8/node_common.h" -namespace webkit { -extern void SetGetNodeContext(v8::Handle (*)()); -} - namespace atom { -namespace { - -v8::Handle GetNodeContext() { - return global_env->context(); -} - -} // namespace - AtomRendererClient::AtomRendererClient() : node_bindings_(NodeBindings::Create(false)) { } @@ -32,12 +20,15 @@ AtomRendererClient::~AtomRendererClient() { void AtomRendererClient::RenderThreadStarted() { node_bindings_->Initialize(); - - // Interact with dirty workarounds of extra node context in WebKit. - webkit::SetGetNodeContext(GetNodeContext); - node_bindings_->PrepareMessageLoop(); - node_bindings_->RunMessageLoop(); + + DCHECK(!global_env); + + // Create a default empty environment which would be used when we need to + // run V8 code out of a window context (like running a uv callback). + v8::HandleScope handle_scope(node_isolate); + v8::Local context = v8::Context::New(node_isolate); + global_env = node::Environment::New(context); } void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) { diff --git a/renderer/lib/init.coffee b/renderer/lib/init.coffee new file mode 100644 index 000000000000..8a14c69128d1 --- /dev/null +++ b/renderer/lib/init.coffee @@ -0,0 +1,23 @@ +path = require 'path' + +# Expose information of current process. +process.__atom_type = 'renderer' +process.resourcesPath = path.resolve process.argv[1], '..', '..', '..' + +# We modified the original process.argv to let node.js load the +# atom-renderer.js, we need to restore it here. +process.argv.splice 1, 1 + +# Add renderer/api/lib to require's search paths, which contains javascript part +# of Atom's built-in libraries. +globalPaths = require('module').globalPaths +globalPaths.push path.join process.resourcesPath, 'renderer', 'api', 'lib' + +# And also common/api/lib +globalPaths.push path.join process.resourcesPath, 'common', 'api', 'lib' + +# Expose global variables. +global.require = require +global.module = module +global.__filename = __filename +global.__dirname = __dirname diff --git a/vendor/node b/vendor/node index 9a7dea685069..2fce49829b4d 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit 9a7dea6850695c4a7c783c721aec0d4e5f1245b4 +Subproject commit 2fce49829b4dde69bceed91c00cec578517541b0