Merge pull request #1044 from atom/load-environment-later
Fix race condition when initializing node integration
This commit is contained in:
commit
6c62895898
20 changed files with 198 additions and 430 deletions
2
atom.gyp
2
atom.gyp
|
@ -266,8 +266,6 @@
|
|||
'atom/renderer/api/atom_api_spell_check_client.h',
|
||||
'atom/renderer/api/atom_api_web_frame.cc',
|
||||
'atom/renderer/api/atom_api_web_frame.h',
|
||||
'atom/renderer/api/atom_renderer_bindings.cc',
|
||||
'atom/renderer/api/atom_renderer_bindings.h',
|
||||
'atom/renderer/atom_render_view_observer.cc',
|
||||
'atom/renderer/atom_render_view_observer.h',
|
||||
'atom/renderer/atom_renderer_client.cc',
|
||||
|
|
|
@ -72,6 +72,9 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
|
|||
|
||||
// Add atom-shell extended APIs.
|
||||
atom_bindings_->BindTo(js_env_->isolate(), global_env->process_object());
|
||||
|
||||
// Load everything.
|
||||
node_bindings_->LoadEnvironment(global_env);
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||
|
|
|
@ -23,83 +23,81 @@ process.argv.splice startMark, endMark - startMark + 1
|
|||
globalPaths = module.globalPaths
|
||||
globalPaths.push path.join process.resourcesPath, 'atom', 'browser', 'api', 'lib'
|
||||
|
||||
# Following operations need extra bindings by AtomBindings.
|
||||
process.once 'BIND_DONE', ->
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js')
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
|
||||
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle console output when running as GUI program.
|
||||
print = (args...) ->
|
||||
process.log util.format(args...)
|
||||
console.log = console.error = console.warn = print
|
||||
process.stdout.write = process.stderr.write = print
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle console output when running as GUI program.
|
||||
print = (args...) ->
|
||||
process.log util.format(args...)
|
||||
console.log = console.error = console.warn = print
|
||||
process.stdout.write = process.stderr.write = print
|
||||
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
|
||||
# Don't quit on fatal error.
|
||||
process.on 'uncaughtException', (error) ->
|
||||
# Do nothing if the user has a custom uncaught exception handler.
|
||||
if process.listeners('uncaughtException').length > 1
|
||||
return
|
||||
# Don't quit on fatal error.
|
||||
process.on 'uncaughtException', (error) ->
|
||||
# Do nothing if the user has a custom uncaught exception handler.
|
||||
if process.listeners('uncaughtException').length > 1
|
||||
return
|
||||
|
||||
# Show error in GUI.
|
||||
stack = error.stack ? "#{error.name}: #{error.message}"
|
||||
message = "Uncaught Exception:\n#{stack}"
|
||||
require('dialog').showErrorBox 'A JavaScript error occured in the browser process', message
|
||||
# Show error in GUI.
|
||||
stack = error.stack ? "#{error.name}: #{error.message}"
|
||||
message = "Uncaught Exception:\n#{stack}"
|
||||
require('dialog').showErrorBox 'A JavaScript error occured in the browser process', message
|
||||
|
||||
# Emit 'exit' event on quit.
|
||||
require('app').on 'quit', ->
|
||||
process.emit 'exit'
|
||||
# Emit 'exit' event on quit.
|
||||
app = require 'app'
|
||||
app.on 'quit', ->
|
||||
process.emit 'exit'
|
||||
|
||||
# Load the RPC server.
|
||||
require './rpc-server'
|
||||
# Load the RPC server.
|
||||
require './rpc-server'
|
||||
|
||||
# Load the guest view manager.
|
||||
require './guest-view-manager'
|
||||
require './guest-window-manager'
|
||||
# Load the guest view manager.
|
||||
require './guest-view-manager'
|
||||
require './guest-window-manager'
|
||||
|
||||
# Now we try to load app's package.json.
|
||||
packageJson = null
|
||||
# Now we try to load app's package.json.
|
||||
packageJson = null
|
||||
|
||||
searchPaths = [ 'app', 'app.asar', 'default_app' ]
|
||||
for packagePath in searchPaths
|
||||
try
|
||||
packagePath = path.join process.resourcesPath, packagePath
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
break
|
||||
catch e
|
||||
continue
|
||||
searchPaths = [ 'app', 'app.asar', 'default_app' ]
|
||||
for packagePath in searchPaths
|
||||
try
|
||||
packagePath = path.join process.resourcesPath, packagePath
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
break
|
||||
catch e
|
||||
continue
|
||||
|
||||
throw new Error("Unable to find a valid app") unless packageJson?
|
||||
throw new Error("Unable to find a valid app") unless packageJson?
|
||||
|
||||
# Set application's version.
|
||||
app = require 'app'
|
||||
app.setVersion packageJson.version if packageJson.version?
|
||||
# Set application's version.
|
||||
app.setVersion packageJson.version if packageJson.version?
|
||||
|
||||
# Set application's name.
|
||||
if packageJson.productName?
|
||||
app.setName packageJson.productName
|
||||
else if packageJson.name?
|
||||
app.setName packageJson.name
|
||||
# Set application's name.
|
||||
if packageJson.productName?
|
||||
app.setName packageJson.productName
|
||||
else if packageJson.name?
|
||||
app.setName packageJson.name
|
||||
|
||||
# Set application's desktop name.
|
||||
if packageJson.desktopName?
|
||||
app.setDesktopName packageJson.desktopName
|
||||
else
|
||||
app.setDesktopName '#{app.getName()}.desktop'
|
||||
# Set application's desktop name.
|
||||
if packageJson.desktopName?
|
||||
app.setDesktopName packageJson.desktopName
|
||||
else
|
||||
app.setDesktopName "#{app.getName()}.desktop"
|
||||
|
||||
# Set the user path according to application's name.
|
||||
app.setPath 'userData', path.join(app.getPath('appData'), app.getName())
|
||||
app.setPath 'userCache', path.join(app.getPath('cache'), app.getName())
|
||||
# Set the user path according to application's name.
|
||||
app.setPath 'userData', path.join(app.getPath('appData'), app.getName())
|
||||
app.setPath 'userCache', path.join(app.getPath('cache'), app.getName())
|
||||
|
||||
# Load the chrome extension support.
|
||||
require './chrome-extension.js'
|
||||
# Load the chrome extension support.
|
||||
require './chrome-extension'
|
||||
|
||||
# Finally load app's main.js and transfer control to C++.
|
||||
module._load path.join(packagePath, packageJson.main), module, true
|
||||
# Finally load app's main.js and transfer control to C++.
|
||||
module._load path.join(packagePath, packageJson.main), module, true
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "atom/common/chrome_version.h"
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "base/logging.h"
|
||||
#include "native_mate/callback.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
@ -20,20 +19,9 @@ namespace atom {
|
|||
|
||||
namespace {
|
||||
|
||||
// Async handle to execute the stored v8 callback.
|
||||
uv_async_t g_callback_uv_handle;
|
||||
|
||||
// Stored v8 callback, to be called by the async handler.
|
||||
base::Closure g_v8_callback;
|
||||
|
||||
// Dummy class type that used for crashing the program.
|
||||
struct DummyClass { bool crash; };
|
||||
|
||||
// Async handler to execute the stored v8 callback.
|
||||
void UvOnCallback(uv_async_t* handle) {
|
||||
g_v8_callback.Run();
|
||||
}
|
||||
|
||||
void Crash() {
|
||||
static_cast<DummyClass*>(NULL)->crash = true;
|
||||
}
|
||||
|
@ -49,19 +37,12 @@ void Log(const base::string16& message) {
|
|||
logging::LogMessage("CONSOLE", 0, 0).stream() << message;
|
||||
}
|
||||
|
||||
void ScheduleCallback(const base::Closure& callback) {
|
||||
g_v8_callback = callback;
|
||||
uv_async_send(&g_callback_uv_handle);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
AtomBindings::AtomBindings() {
|
||||
uv_async_init(uv_default_loop(), &call_next_tick_async_, OnCallNextTick);
|
||||
call_next_tick_async_.data = this;
|
||||
|
||||
uv_async_init(uv_default_loop(), &g_callback_uv_handle, UvOnCallback);
|
||||
}
|
||||
|
||||
AtomBindings::~AtomBindings() {
|
||||
|
@ -74,20 +55,14 @@ void AtomBindings::BindTo(v8::Isolate* isolate,
|
|||
mate::Dictionary dict(isolate, process);
|
||||
dict.SetMethod("crash", &Crash);
|
||||
dict.SetMethod("log", &Log);
|
||||
dict.SetMethod("scheduleCallback", &ScheduleCallback);
|
||||
dict.SetMethod("activateUvLoop",
|
||||
base::Bind(&AtomBindings::ActivateUVLoop, base::Unretained(this)));
|
||||
|
||||
v8::Handle<v8::Object> versions;
|
||||
mate::Dictionary versions;
|
||||
if (dict.Get("versions", &versions)) {
|
||||
versions->Set(mate::StringToV8(isolate, "atom-shell"),
|
||||
mate::StringToV8(isolate, ATOM_VERSION_STRING));
|
||||
versions->Set(mate::StringToV8(isolate, "chrome"),
|
||||
mate::StringToV8(isolate, CHROME_VERSION_STRING));
|
||||
versions.Set("atom-shell", ATOM_VERSION_STRING);
|
||||
versions.Set("chrome", CHROME_VERSION_STRING);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> event = mate::StringToV8(isolate, "BIND_DONE");
|
||||
node::MakeCallback(isolate, process, "emit", 1, &event);
|
||||
}
|
||||
|
||||
void AtomBindings::ActivateUVLoop(v8::Isolate* isolate) {
|
||||
|
|
|
@ -24,7 +24,7 @@ class AtomBindings {
|
|||
|
||||
// Add process.atomBinding function, which behaves like process.binding but
|
||||
// load native code from atom-shell instead.
|
||||
virtual void BindTo(v8::Isolate* isolate, v8::Handle<v8::Object> process);
|
||||
void BindTo(v8::Isolate* isolate, v8::Handle<v8::Object> process);
|
||||
|
||||
private:
|
||||
void ActivateUVLoop(v8::Isolate* isolate);
|
||||
|
|
|
@ -26,9 +26,6 @@ using content::BrowserThread;
|
|||
// Forward declaration of internal node functions.
|
||||
namespace node {
|
||||
void Init(int*, const char**, int*, const char***);
|
||||
void Load(Environment* env);
|
||||
void SetupProcessObject(Environment*, int, const char* const*, int,
|
||||
const char* const*);
|
||||
}
|
||||
|
||||
// Force all builtin modules to be referenced so they can actually run their
|
||||
|
@ -89,19 +86,6 @@ namespace {
|
|||
void UvNoOp(uv_async_t* handle) {
|
||||
}
|
||||
|
||||
// Moved from node.cc.
|
||||
void HandleCloseCb(uv_handle_t* handle) {
|
||||
node::Environment* env = reinterpret_cast<node::Environment*>(handle->data);
|
||||
env->FinishHandleCleanup(handle);
|
||||
}
|
||||
|
||||
void HandleCleanup(node::Environment* env,
|
||||
uv_handle_t* handle,
|
||||
void* arg) {
|
||||
handle->data = env;
|
||||
uv_close(handle, HandleCloseCb);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -126,14 +110,14 @@ std::vector<std::string> String16VectorToStringVector(
|
|||
|
||||
} // namespace
|
||||
|
||||
node::Environment* global_env = NULL;
|
||||
node::Environment* global_env = nullptr;
|
||||
|
||||
NodeBindings::NodeBindings(bool is_browser)
|
||||
: is_browser_(is_browser),
|
||||
message_loop_(NULL),
|
||||
message_loop_(nullptr),
|
||||
uv_loop_(uv_default_loop()),
|
||||
embed_closed_(false),
|
||||
uv_env_(NULL),
|
||||
uv_env_(nullptr),
|
||||
weak_factory_(this) {
|
||||
}
|
||||
|
||||
|
@ -158,20 +142,21 @@ void NodeBindings::Initialize() {
|
|||
// Init node.
|
||||
// (we assume it would not node::Init would not modify the parameters under
|
||||
// embedded mode).
|
||||
node::Init(NULL, NULL, NULL, NULL);
|
||||
node::Init(nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
node::Environment* NodeBindings::CreateEnvironment(
|
||||
v8::Handle<v8::Context> context) {
|
||||
CommandLine* command_line = CommandLine::ForCurrentProcess();
|
||||
std::vector<std::string> args =
|
||||
#if defined(OS_WIN)
|
||||
String16VectorToStringVector(CommandLine::ForCurrentProcess()->argv());
|
||||
String16VectorToStringVector(command_line->argv());
|
||||
#else
|
||||
CommandLine::ForCurrentProcess()->argv();
|
||||
command_line->argv();
|
||||
#endif
|
||||
|
||||
// Feed node the path to initialization script.
|
||||
base::FilePath exec_path(CommandLine::ForCurrentProcess()->argv()[0]);
|
||||
base::FilePath exec_path(command_line->argv()[0]);
|
||||
PathService::Get(base::FILE_EXE, &exec_path);
|
||||
base::FilePath resources_path =
|
||||
#if defined(OS_MACOSX)
|
||||
|
@ -179,77 +164,27 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
exec_path.DirName().DirName().DirName().DirName().DirName()
|
||||
.Append("Resources");
|
||||
#else
|
||||
exec_path.DirName().AppendASCII("resources");
|
||||
exec_path.DirName().Append(FILE_PATH_LITERAL("resources"));
|
||||
#endif
|
||||
base::FilePath script_path =
|
||||
resources_path.AppendASCII("atom")
|
||||
.AppendASCII(is_browser_ ? "browser" : "renderer")
|
||||
.AppendASCII("lib")
|
||||
.AppendASCII("init.js");
|
||||
resources_path.Append(FILE_PATH_LITERAL("atom"))
|
||||
.Append(is_browser_ ? FILE_PATH_LITERAL("browser") :
|
||||
FILE_PATH_LITERAL("renderer"))
|
||||
.Append(FILE_PATH_LITERAL("lib"))
|
||||
.Append(FILE_PATH_LITERAL("init.js"));
|
||||
std::string script_path_str = script_path.AsUTF8Unsafe();
|
||||
args.insert(args.begin() + 1, script_path_str.c_str());
|
||||
|
||||
// Convert string vector to const char* array.
|
||||
scoped_ptr<const char*[]> c_argv = StringVectorToArgArray(args);
|
||||
return node::CreateEnvironment(context->GetIsolate(),
|
||||
uv_default_loop(),
|
||||
context,
|
||||
args.size(), c_argv.get(),
|
||||
0, nullptr);
|
||||
}
|
||||
|
||||
// Construct the parameters that passed to node::CreateEnvironment:
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
int argc = args.size();
|
||||
const char** argv = c_argv.get();
|
||||
int exec_argc = 0;
|
||||
const char** exec_argv = NULL;
|
||||
|
||||
using namespace v8; // NOLINT
|
||||
using namespace node; // NOLINT
|
||||
|
||||
// Following code are stripped from node::CreateEnvironment in node.cc:
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
Context::Scope context_scope(context);
|
||||
Environment* env = Environment::New(context, loop);
|
||||
|
||||
isolate->SetAutorunMicrotasks(false);
|
||||
|
||||
uv_check_init(env->event_loop(), env->immediate_check_handle());
|
||||
uv_unref(
|
||||
reinterpret_cast<uv_handle_t*>(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<uv_handle_t*>(env->idle_prepare_handle()));
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle()));
|
||||
|
||||
// Register handle cleanups
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()),
|
||||
HandleCleanup,
|
||||
nullptr);
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->immediate_idle_handle()),
|
||||
HandleCleanup,
|
||||
nullptr);
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()),
|
||||
HandleCleanup,
|
||||
nullptr);
|
||||
env->RegisterHandleCleanup(
|
||||
reinterpret_cast<uv_handle_t*>(env->idle_check_handle()),
|
||||
HandleCleanup,
|
||||
nullptr);
|
||||
|
||||
Local<FunctionTemplate> process_template = FunctionTemplate::New(isolate);
|
||||
process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process"));
|
||||
|
||||
Local<Object> process_object = process_template->GetFunction()->NewInstance();
|
||||
env->set_process_object(process_object);
|
||||
|
||||
SetupProcessObject(env, argc, argv, exec_argc, exec_argv);
|
||||
LoadEnvironment(env);
|
||||
|
||||
return env;
|
||||
void NodeBindings::LoadEnvironment(node::Environment* env) {
|
||||
node::LoadEnvironment(env);
|
||||
}
|
||||
|
||||
void NodeBindings::PrepareMessageLoop() {
|
||||
|
|
|
@ -27,13 +27,16 @@ class NodeBindings {
|
|||
virtual ~NodeBindings();
|
||||
|
||||
// Setup V8, libuv.
|
||||
virtual void Initialize();
|
||||
void Initialize();
|
||||
|
||||
// Create the environment and load node.js.
|
||||
virtual node::Environment* CreateEnvironment(v8::Handle<v8::Context> context);
|
||||
node::Environment* CreateEnvironment(v8::Handle<v8::Context> context);
|
||||
|
||||
// Load node.js in the environment.
|
||||
void LoadEnvironment(node::Environment* env);
|
||||
|
||||
// Prepare for message loop integration.
|
||||
virtual void PrepareMessageLoop();
|
||||
void PrepareMessageLoop();
|
||||
|
||||
// Do message loop integration.
|
||||
virtual void RunMessageLoop();
|
||||
|
|
|
@ -15,13 +15,13 @@ class NodeBindingsLinux : public NodeBindings {
|
|||
explicit NodeBindingsLinux(bool is_browser);
|
||||
virtual ~NodeBindingsLinux();
|
||||
|
||||
virtual void RunMessageLoop() OVERRIDE;
|
||||
void RunMessageLoop() override;
|
||||
|
||||
private:
|
||||
// Called when uv's watcher queue changes.
|
||||
static void OnWatcherQueueChanged(uv_loop_t* loop);
|
||||
|
||||
virtual void PollEvents() OVERRIDE;
|
||||
void PollEvents() override;
|
||||
|
||||
// Epoll to poll for uv's backend fd.
|
||||
int epoll_;
|
||||
|
|
|
@ -15,13 +15,13 @@ class NodeBindingsMac : public NodeBindings {
|
|||
explicit NodeBindingsMac(bool is_browser);
|
||||
virtual ~NodeBindingsMac();
|
||||
|
||||
virtual void RunMessageLoop() OVERRIDE;
|
||||
void RunMessageLoop() override;
|
||||
|
||||
private:
|
||||
// Called when uv's watcher queue changes.
|
||||
static void OnWatcherQueueChanged(uv_loop_t* loop);
|
||||
|
||||
virtual void PollEvents() OVERRIDE;
|
||||
void PollEvents() override;
|
||||
|
||||
// Kqueue to poll for uv's backend fd.
|
||||
int kqueue_;
|
||||
|
|
|
@ -16,7 +16,7 @@ class NodeBindingsWin : public NodeBindings {
|
|||
virtual ~NodeBindingsWin();
|
||||
|
||||
private:
|
||||
virtual void PollEvents() OVERRIDE;
|
||||
void PollEvents() override;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeBindingsWin);
|
||||
};
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/renderer/api/atom_renderer_bindings.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/renderer/render_view.h"
|
||||
#include "native_mate/converter.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
v8::Handle<v8::Object> GetProcessObject(v8::Handle<v8::Context> context) {
|
||||
v8::Handle<v8::Object> process = context->Global()->Get(
|
||||
mate::StringToV8(context->GetIsolate(), "process"))->ToObject();
|
||||
DCHECK(!process.IsEmpty());
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomRendererBindings::AtomRendererBindings() {
|
||||
}
|
||||
|
||||
AtomRendererBindings::~AtomRendererBindings() {
|
||||
}
|
||||
|
||||
void AtomRendererBindings::BindToFrame(blink::WebFrame* frame) {
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
|
||||
if (context.IsEmpty())
|
||||
return;
|
||||
|
||||
v8::Context::Scope scope(context);
|
||||
AtomBindings::BindTo(isolate, GetProcessObject(context));
|
||||
}
|
||||
|
||||
void AtomRendererBindings::OnBrowserMessage(content::RenderView* render_view,
|
||||
const base::string16& channel,
|
||||
const base::ListValue& args) {
|
||||
if (!render_view->GetWebView())
|
||||
return;
|
||||
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context =
|
||||
render_view->GetWebView()->mainFrame()->mainWorldScriptContext();
|
||||
if (context.IsEmpty())
|
||||
return;
|
||||
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Handle<v8::Object> process = GetProcessObject(context);
|
||||
scoped_ptr<V8ValueConverter> converter(new V8ValueConverter);
|
||||
|
||||
std::vector<v8::Handle<v8::Value>> arguments;
|
||||
arguments.reserve(1 + args.GetSize());
|
||||
arguments.push_back(mate::ConvertToV8(isolate, channel));
|
||||
|
||||
for (size_t i = 0; i < args.GetSize(); i++) {
|
||||
const base::Value* value;
|
||||
if (args.Get(i, &value))
|
||||
arguments.push_back(converter->ToV8Value(value, context));
|
||||
}
|
||||
|
||||
node::MakeCallback(isolate, process, "emit", arguments.size(), &arguments[0]);
|
||||
}
|
||||
|
||||
} // namespace atom
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_RENDERER_API_ATOM_RENDERER_BINDINGS_H_
|
||||
#define ATOM_RENDERER_API_ATOM_RENDERER_BINDINGS_H_
|
||||
|
||||
#include "atom/common/api/atom_bindings.h"
|
||||
|
||||
#include "base/strings/string16.h"
|
||||
|
||||
namespace base {
|
||||
class ListValue;
|
||||
}
|
||||
|
||||
namespace content {
|
||||
class RenderView;
|
||||
}
|
||||
|
||||
namespace blink {
|
||||
class WebFrame;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomRendererBindings : public AtomBindings {
|
||||
public:
|
||||
AtomRendererBindings();
|
||||
virtual ~AtomRendererBindings();
|
||||
|
||||
// Call BindTo for process object of the frame.
|
||||
void BindToFrame(blink::WebFrame* frame);
|
||||
|
||||
// Dispatch messages from browser.
|
||||
void OnBrowserMessage(content::RenderView* render_view,
|
||||
const base::string16& channel,
|
||||
const base::ListValue& args);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomRendererBindings);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_RENDERER_API_ATOM_RENDERER_BINDINGS_H_
|
|
@ -8,8 +8,9 @@
|
|||
#include <vector>
|
||||
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "atom/renderer/api/atom_renderer_bindings.h"
|
||||
#include "atom/renderer/atom_renderer_client.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
@ -19,19 +20,38 @@
|
|||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebKit.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using blink::WebFrame;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
v8::Handle<v8::Object> GetProcessObject(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Context> context) {
|
||||
v8::Handle<v8::String> key = mate::StringToV8(isolate, "process");
|
||||
return context->Global()->Get(key)->ToObject();
|
||||
}
|
||||
|
||||
std::vector<v8::Handle<v8::Value>> ListValueToVector(
|
||||
v8::Isolate* isolate,
|
||||
const base::ListValue& list) {
|
||||
v8::Handle<v8::Value> array = mate::ConvertToV8(isolate, list);
|
||||
std::vector<v8::Handle<v8::Value>> result;
|
||||
mate::ConvertFromV8(isolate, array, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomRenderViewObserver::AtomRenderViewObserver(
|
||||
content::RenderView* render_view,
|
||||
AtomRendererClient* renderer_client)
|
||||
: content::RenderViewObserver(render_view),
|
||||
renderer_client_(renderer_client) {
|
||||
renderer_client_(renderer_client),
|
||||
document_created_(false) {
|
||||
}
|
||||
|
||||
AtomRenderViewObserver::~AtomRenderViewObserver() {
|
||||
|
@ -39,6 +59,8 @@ AtomRenderViewObserver::~AtomRenderViewObserver() {
|
|||
|
||||
void AtomRenderViewObserver::DidCreateDocumentElement(
|
||||
blink::WebLocalFrame* frame) {
|
||||
document_created_ = true;
|
||||
|
||||
// Read --zoom-factor from command line.
|
||||
std::string zoom_factor_str = CommandLine::ForCurrentProcess()->
|
||||
GetSwitchValueASCII(switches::kZoomFactor);;
|
||||
|
@ -76,8 +98,28 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
|
|||
|
||||
void AtomRenderViewObserver::OnBrowserMessage(const base::string16& channel,
|
||||
const base::ListValue& args) {
|
||||
renderer_client_->atom_bindings()->OnBrowserMessage(
|
||||
render_view(), channel, args);
|
||||
if (!document_created_)
|
||||
return;
|
||||
|
||||
if (!render_view()->GetWebView())
|
||||
return;
|
||||
|
||||
blink::WebFrame* frame = render_view()->GetWebView()->mainFrame();
|
||||
if (!frame || frame->isWebRemoteFrame())
|
||||
return;
|
||||
|
||||
v8::Isolate* isolate = blink::mainThreadIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context = frame->mainWorldScriptContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
std::vector<v8::Handle<v8::Value>> arguments = ListValueToVector(
|
||||
isolate, args);
|
||||
arguments.insert(arguments.begin(), mate::ConvertToV8(isolate, channel));
|
||||
|
||||
v8::Handle<v8::Object> process = GetProcessObject(isolate, context);
|
||||
node::MakeCallback(isolate, process, "emit", arguments.size(), &arguments[0]);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
|
|
@ -36,6 +36,9 @@ class AtomRenderViewObserver : public content::RenderViewObserver {
|
|||
// Weak reference to renderer client.
|
||||
AtomRendererClient* renderer_client_;
|
||||
|
||||
// Whether the document object has been created.
|
||||
bool document_created_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomRenderViewObserver);
|
||||
};
|
||||
|
||||
|
|
|
@ -4,21 +4,17 @@
|
|||
|
||||
#include "atom/renderer/atom_renderer_client.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/api/atom_bindings.h"
|
||||
#include "atom/common/node_bindings.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "atom/renderer/api/atom_renderer_bindings.h"
|
||||
#include "atom/renderer/atom_render_view_observer.h"
|
||||
#include "chrome/renderer/printing/print_web_view_helper.h"
|
||||
#include "chrome/renderer/tts_dispatcher.h"
|
||||
#include "content/public/common/content_constants.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "content/public/renderer/render_thread.h"
|
||||
#include "base/command_line.h"
|
||||
#include "native_mate/converter.h"
|
||||
#include "third_party/WebKit/public/web/WebCustomElement.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebPluginParams.h"
|
||||
|
@ -44,33 +40,12 @@ bool IsSwitchEnabled(base::CommandLine* command_line,
|
|||
return true;
|
||||
}
|
||||
|
||||
// Helper class to forward the WillReleaseScriptContext message to the client.
|
||||
class AtomRenderFrameObserver : public content::RenderFrameObserver {
|
||||
public:
|
||||
AtomRenderFrameObserver(content::RenderFrame* frame,
|
||||
AtomRendererClient* renderer_client)
|
||||
: content::RenderFrameObserver(frame),
|
||||
renderer_client_(renderer_client) {}
|
||||
|
||||
// content::RenderFrameObserver:
|
||||
virtual void WillReleaseScriptContext(v8::Handle<v8::Context> context,
|
||||
int world_id) OVERRIDE {
|
||||
renderer_client_->WillReleaseScriptContext(
|
||||
render_frame()->GetWebFrame(), context, world_id);
|
||||
}
|
||||
|
||||
private:
|
||||
AtomRendererClient* renderer_client_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomRenderFrameObserver);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomRendererClient::AtomRendererClient()
|
||||
: node_bindings_(NodeBindings::Create(false)),
|
||||
atom_bindings_(new AtomRendererBindings),
|
||||
main_frame_(NULL) {
|
||||
atom_bindings_(new AtomBindings),
|
||||
main_frame_(nullptr) {
|
||||
}
|
||||
|
||||
AtomRendererClient::~AtomRendererClient() {
|
||||
|
@ -99,11 +74,6 @@ void AtomRendererClient::RenderThreadStarted() {
|
|||
content::RenderThread::Get()->AddObserver(this);
|
||||
}
|
||||
|
||||
void AtomRendererClient::RenderFrameCreated(
|
||||
content::RenderFrame* render_frame) {
|
||||
new AtomRenderFrameObserver(render_frame, this);
|
||||
}
|
||||
|
||||
void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) {
|
||||
new printing::PrintWebViewHelper(render_view);
|
||||
new AtomRenderViewObserver(render_view, this);
|
||||
|
@ -132,17 +102,13 @@ void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame,
|
|||
v8::Handle<v8::Context> context,
|
||||
int extension_group,
|
||||
int world_id) {
|
||||
// The first web frame is the main frame.
|
||||
if (main_frame_ == NULL)
|
||||
main_frame_ = frame;
|
||||
|
||||
v8::Context::Scope scope(context);
|
||||
|
||||
// Check the existance of process object to prevent duplicate initialization.
|
||||
if (context->Global()->Has(
|
||||
mate::StringToV8(context->GetIsolate(), "process")))
|
||||
// Only attach node bindings in main frame.
|
||||
if (main_frame_)
|
||||
return;
|
||||
|
||||
// The first web frame is the main frame.
|
||||
main_frame_ = frame;
|
||||
|
||||
// Give the node loop a run to make sure everything is ready.
|
||||
node_bindings_->RunMessageLoop();
|
||||
|
||||
|
@ -150,46 +116,14 @@ void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame,
|
|||
node::Environment* env = node_bindings_->CreateEnvironment(context);
|
||||
|
||||
// Add atom-shell extended APIs.
|
||||
atom_bindings_->BindToFrame(frame);
|
||||
|
||||
// Store the created environment.
|
||||
web_page_envs_.push_back(env);
|
||||
atom_bindings_->BindTo(env->isolate(), env->process_object());
|
||||
|
||||
// Make uv loop being wrapped by window context.
|
||||
if (node_bindings_->uv_env() == NULL)
|
||||
if (node_bindings_->uv_env() == nullptr)
|
||||
node_bindings_->set_uv_env(env);
|
||||
}
|
||||
|
||||
void AtomRendererClient::WillReleaseScriptContext(
|
||||
blink::WebLocalFrame* frame,
|
||||
v8::Handle<v8::Context> context,
|
||||
int world_id) {
|
||||
node::Environment* env = node::Environment::GetCurrent(context);
|
||||
if (env == NULL) {
|
||||
LOG(ERROR) << "Encounter a non-node context when releasing script context";
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the environment.
|
||||
web_page_envs_.erase(
|
||||
std::remove(web_page_envs_.begin(), web_page_envs_.end(), env),
|
||||
web_page_envs_.end());
|
||||
|
||||
// Notice that we are not disposing the environment object here, because there
|
||||
// may still be pending uv operations in the uv loop, and when they got done
|
||||
// they would be needing the original environment.
|
||||
// So we are leaking the environment object here, just like Chrome leaking the
|
||||
// memory :) . Since it's only leaked when refreshing or unloading, so as long
|
||||
// as we make sure renderer process is restared then the memory would not be
|
||||
// leaked.
|
||||
// env->Dispose();
|
||||
|
||||
// Wrap the uv loop with another environment.
|
||||
if (env == node_bindings_->uv_env()) {
|
||||
node::Environment* env = web_page_envs_.size() > 0 ? web_page_envs_[0] :
|
||||
NULL;
|
||||
node_bindings_->set_uv_env(env);
|
||||
}
|
||||
// Load everything.
|
||||
node_bindings_->LoadEnvironment(env);
|
||||
}
|
||||
|
||||
bool AtomRendererClient::ShouldFork(blink::WebFrame* frame,
|
||||
|
|
|
@ -6,18 +6,13 @@
|
|||
#define ATOM_RENDERER_ATOM_RENDERER_CLIENT_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "content/public/renderer/content_renderer_client.h"
|
||||
#include "content/public/renderer/render_process_observer.h"
|
||||
|
||||
namespace node {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomRendererBindings;
|
||||
class AtomBindings;
|
||||
class NodeBindings;
|
||||
|
||||
class AtomRendererClient : public content::ContentRendererClient,
|
||||
|
@ -26,13 +21,6 @@ class AtomRendererClient : public content::ContentRendererClient,
|
|||
AtomRendererClient();
|
||||
virtual ~AtomRendererClient();
|
||||
|
||||
// Forwarded by RenderFrameObserver.
|
||||
void WillReleaseScriptContext(blink::WebLocalFrame* frame,
|
||||
v8::Handle<v8::Context> context,
|
||||
int world_id);
|
||||
|
||||
AtomRendererBindings* atom_bindings() const { return atom_bindings_.get(); }
|
||||
|
||||
private:
|
||||
enum NodeIntegration {
|
||||
ALL,
|
||||
|
@ -42,11 +30,10 @@ class AtomRendererClient : public content::ContentRendererClient,
|
|||
};
|
||||
|
||||
// content::RenderProcessObserver:
|
||||
virtual void WebKitInitialized() OVERRIDE;
|
||||
void WebKitInitialized() override;
|
||||
|
||||
// content::ContentRendererClient:
|
||||
void RenderThreadStarted() override;
|
||||
void RenderFrameCreated(content::RenderFrame* render_frame) override;
|
||||
void RenderViewCreated(content::RenderView*) override;
|
||||
blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer(
|
||||
blink::WebSpeechSynthesizerClient* client) override;
|
||||
|
@ -67,10 +54,8 @@ class AtomRendererClient : public content::ContentRendererClient,
|
|||
|
||||
void EnableWebRuntimeFeatures();
|
||||
|
||||
std::vector<node::Environment*> web_page_envs_;
|
||||
|
||||
scoped_ptr<NodeBindings> node_bindings_;
|
||||
scoped_ptr<AtomRendererBindings> atom_bindings_;
|
||||
scoped_ptr<AtomBindings> atom_bindings_;
|
||||
|
||||
// The main frame.
|
||||
blink::WebFrame* main_frame_;
|
||||
|
|
|
@ -19,7 +19,7 @@ globalPaths.push path.join(process.resourcesPath, 'atom', 'renderer', 'api', 'li
|
|||
globalPaths.push path.join(process.resourcesPath, 'app')
|
||||
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js')
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
|
||||
|
||||
# Process command line arguments.
|
||||
nodeIntegration = 'false'
|
||||
|
@ -86,9 +86,12 @@ if nodeIntegration in ['true', 'all', 'except-iframe', 'manual-enable-iframe']
|
|||
window.addEventListener 'unload', ->
|
||||
process.emit 'exit'
|
||||
else
|
||||
# There still some native initialization codes needs "process", delete the
|
||||
# global reference after they are done.
|
||||
process.once 'BIND_DONE', ->
|
||||
# The Module.runMain will run process._tickCallck() immediately, so we are
|
||||
# able to delete the symbols in this tick even though we used process.nextTick
|
||||
# to schedule it.
|
||||
# It is important that we put this in process.nextTick, if we delete them now
|
||||
# some code in node.js will complain about "process not defined".
|
||||
process.nextTick ->
|
||||
delete global.process
|
||||
delete global.setImmediate
|
||||
delete global.clearImmediate
|
||||
|
|
|
@ -65,3 +65,19 @@ describe 'chromium feature', ->
|
|||
assert.equal event.data, message
|
||||
done()
|
||||
worker.port.postMessage message
|
||||
|
||||
describe 'iframe', ->
|
||||
iframe = null
|
||||
|
||||
beforeEach ->
|
||||
iframe = document.createElement 'iframe'
|
||||
|
||||
afterEach ->
|
||||
document.body.removeChild iframe
|
||||
|
||||
it 'does not have node integration', (done) ->
|
||||
iframe.src = "file://#{fixtures}/pages/set-global.html"
|
||||
document.body.appendChild iframe
|
||||
iframe.onload = ->
|
||||
assert.equal iframe.contentWindow.test, 'undefined undefined undefined'
|
||||
done()
|
||||
|
|
7
spec/fixtures/pages/set-global.html
vendored
Normal file
7
spec/fixtures/pages/set-global.html
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.test = [typeof require, typeof module, typeof process].join(' ')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -54,11 +54,6 @@ describe 'node feature', ->
|
|||
fs.readFile __filename, ->
|
||||
setTimeout done, 0
|
||||
|
||||
describe 'setTimeout in pure uv callback', ->
|
||||
it 'does not crash', (done) ->
|
||||
process.scheduleCallback ->
|
||||
setTimeout done, 0
|
||||
|
||||
describe 'throw error in node context', ->
|
||||
it 'gets caught', (done) ->
|
||||
error = new Error('boo!')
|
||||
|
|
Loading…
Reference in a new issue