Merge pull request #1044 from atom/load-environment-later

Fix race condition when initializing node integration
This commit is contained in:
Cheng Zhao 2015-01-25 13:04:33 -08:00
commit 6c62895898
20 changed files with 198 additions and 430 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -16,7 +16,7 @@ class NodeBindingsWin : public NodeBindings {
virtual ~NodeBindingsWin();
private:
virtual void PollEvents() OVERRIDE;
void PollEvents() override;
DISALLOW_COPY_AND_ASSIGN(NodeBindingsWin);
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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