diff --git a/app/win/atom.rc b/app/win/atom.rc index 08e3fb99a148..0d19056930c6 100644 --- a/app/win/atom.rc +++ b/app/win/atom.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,8,1,0 - PRODUCTVERSION 0,8,1,0 + FILEVERSION 0,8,5,0 + PRODUCTVERSION 0,8,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Atom-Shell" - VALUE "FileVersion", "0.8.1" + VALUE "FileVersion", "0.8.5" VALUE "InternalName", "atom.exe" VALUE "LegalCopyright", "Copyright (C) 2013 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "atom.exe" VALUE "ProductName", "Atom-Shell" - VALUE "ProductVersion", "0.8.1" + VALUE "ProductVersion", "0.8.5" END END BLOCK "VarFileInfo" diff --git a/atom.gyp b/atom.gyp index 34acde3c8627..f75aac85c256 100644 --- a/atom.gyp +++ b/atom.gyp @@ -12,6 +12,7 @@ ], 'coffee_sources': [ 'browser/api/lib/app.coffee', + 'browser/api/lib/atom-delegate.coffee', 'browser/api/lib/auto-updater.coffee', 'browser/api/lib/browser-window.coffee', 'browser/api/lib/dialog.coffee', @@ -27,7 +28,9 @@ 'common/api/lib/clipboard.coffee', 'common/api/lib/crash-reporter.coffee', 'common/api/lib/id-weak-map.coffee', + 'common/api/lib/screen.coffee', 'common/api/lib/shell.coffee', + 'common/lib/init.coffee', 'renderer/lib/init.coffee', 'renderer/api/lib/ipc.coffee', 'renderer/api/lib/remote.coffee', @@ -46,8 +49,6 @@ 'browser/api/atom_api_dialog.h', 'browser/api/atom_api_event.cc', 'browser/api/atom_api_event.h', - 'browser/api/atom_api_event_emitter.cc', - 'browser/api/atom_api_event_emitter.h', 'browser/api/atom_api_menu.cc', 'browser/api/atom_api_menu.h', 'browser/api/atom_api_menu_mac.h', @@ -133,8 +134,12 @@ 'common/api/atom_api_clipboard.h', 'common/api/atom_api_crash_reporter.cc', 'common/api/atom_api_crash_reporter.h', + 'common/api/atom_api_event_emitter.cc', + 'common/api/atom_api_event_emitter.h', 'common/api/atom_api_id_weak_map.cc', 'common/api/atom_api_id_weak_map.h', + 'common/api/atom_api_screen.cc', + 'common/api/atom_api_screen.h', 'common/api/atom_api_shell.cc', 'common/api/atom_api_shell.h', 'common/api/atom_api_v8_util.cc', diff --git a/browser/api/atom_api_app.h b/browser/api/atom_api_app.h index 7bfa6d091aac..7ca6d0817af2 100644 --- a/browser/api/atom_api_app.h +++ b/browser/api/atom_api_app.h @@ -6,8 +6,8 @@ #define ATOM_BROWSER_API_ATOM_API_APP_H_ #include "base/compiler_specific.h" -#include "browser/api/atom_api_event_emitter.h" #include "browser/browser_observer.h" +#include "common/api/atom_api_event_emitter.h" namespace atom { diff --git a/browser/api/atom_api_auto_updater.h b/browser/api/atom_api_auto_updater.h index b75d55212c21..03d649e2ba66 100644 --- a/browser/api/atom_api_auto_updater.h +++ b/browser/api/atom_api_auto_updater.h @@ -7,8 +7,8 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" -#include "browser/api/atom_api_event_emitter.h" #include "browser/auto_updater_delegate.h" +#include "common/api/atom_api_event_emitter.h" namespace atom { diff --git a/browser/api/atom_api_dialog.cc b/browser/api/atom_api_dialog.cc index a0b9fcf281c5..f42eb1b6eefa 100644 --- a/browser/api/atom_api_dialog.cc +++ b/browser/api/atom_api_dialog.cc @@ -19,6 +19,7 @@ namespace { template void CallV8Function(const RefCountedV8Function& callback, T arg) { + v8::HandleScope handle_scope(node_isolate); v8::Handle value = ToV8Value(arg); callback->NewHandle(node_isolate)->Call( v8::Context::GetCurrent()->Global(), 1, &value); diff --git a/browser/api/atom_api_menu.h b/browser/api/atom_api_menu.h index 9dac74cc10f1..d642892f576a 100644 --- a/browser/api/atom_api_menu.h +++ b/browser/api/atom_api_menu.h @@ -6,7 +6,7 @@ #define ATOM_BROWSER_API_ATOM_API_MENU_H_ #include "base/memory/scoped_ptr.h" -#include "browser/api/atom_api_event_emitter.h" +#include "common/api/atom_api_event_emitter.h" #include "ui/base/models/simple_menu_model.h" namespace atom { diff --git a/browser/api/atom_api_power_monitor.h b/browser/api/atom_api_power_monitor.h index 88c68168b853..3febb438a61d 100644 --- a/browser/api/atom_api_power_monitor.h +++ b/browser/api/atom_api_power_monitor.h @@ -5,10 +5,9 @@ #ifndef ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_ #define ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_ -#include "browser/api/atom_api_event_emitter.h" - #include "base/compiler_specific.h" #include "base/power_monitor/power_observer.h" +#include "common/api/atom_api_event_emitter.h" namespace atom { diff --git a/browser/api/atom_api_window.h b/browser/api/atom_api_window.h index cc069bd70ddd..1526e4802a09 100644 --- a/browser/api/atom_api_window.h +++ b/browser/api/atom_api_window.h @@ -8,8 +8,8 @@ #include #include "base/memory/scoped_ptr.h" -#include "browser/api/atom_api_event_emitter.h" #include "browser/native_window_observer.h" +#include "common/api/atom_api_event_emitter.h" #include "common/v8/scoped_persistent.h" namespace base { diff --git a/browser/api/lib/atom-delegate.coffee b/browser/api/lib/atom-delegate.coffee new file mode 100644 index 000000000000..2e1e6334470b --- /dev/null +++ b/browser/api/lib/atom-delegate.coffee @@ -0,0 +1,6 @@ +module.exports = + browserMainParts: + preMainMessageLoopRun: -> + +setImmediate -> + module.exports.browserMainParts.preMainMessageLoopRun() diff --git a/browser/atom_browser_client.cc b/browser/atom_browser_client.cc index 3c38721e52ff..19617002a816 100644 --- a/browser/atom_browser_client.cc +++ b/browser/atom_browser_client.cc @@ -50,8 +50,8 @@ bool AtomBrowserClient::ShouldSwapProcessesForNavigation( content::SiteInstance* site_instance, const GURL& current_url, const GURL& new_url) { - // Restart renderer process if navigating to the same url. - return current_url == new_url; + // Restart renderer process for all navigations. + return true; } brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( diff --git a/browser/lib/init.coffee b/browser/lib/init.coffee index d16450f701f4..950d4671ac56 100644 --- a/browser/lib/init.coffee +++ b/browser/lib/init.coffee @@ -1,4 +1,4 @@ -fs = require 'fs' +fs = require 'fs' path = require 'path' # Expose information of current process. @@ -14,12 +14,12 @@ process.argv.splice 1, 1 globalPaths = require('module').globalPaths globalPaths.push path.join process.resourcesPath, 'browser', 'api', 'lib' -# And also common/api/lib -globalPaths.push path.join process.resourcesPath, 'common', 'api', 'lib' - # Do loading in next tick since we still need some initialize work before # native bindings can work. setImmediate -> + # Import common settings. + require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js') + 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. diff --git a/browser/mac/Info.plist b/browser/mac/Info.plist index fa4efe8f0c74..c931513b60f3 100644 --- a/browser/mac/Info.plist +++ b/browser/mac/Info.plist @@ -11,7 +11,7 @@ CFBundleIconFile atom.icns CFBundleVersion - 0.8.1 + 0.8.5 NSMainNibFile MainMenu NSPrincipalClass diff --git a/browser/native_window.cc b/browser/native_window.cc index 8aa0a266e3aa..7b923adf2c3b 100644 --- a/browser/native_window.cc +++ b/browser/native_window.cc @@ -283,6 +283,30 @@ void NativeWindow::NotifyWindowBlur() { FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowBlur()); } +// In atom-shell all reloads and navigations started by renderer process would +// be redirected to this method, so we can have precise control of how we +// would open the url (in our case, is to restart the renderer process). See +// AtomRendererClient::ShouldFork for how this is done. +content::WebContents* NativeWindow::OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) { + if (params.disposition != CURRENT_TAB) + return NULL; + + content::NavigationController::LoadURLParams load_url_params(params.url); + load_url_params.referrer = params.referrer; + load_url_params.transition_type = params.transition; + load_url_params.extra_headers = params.extra_headers; + load_url_params.should_replace_current_entry = + params.should_replace_current_entry; + load_url_params.is_renderer_initiated = params.is_renderer_initiated; + load_url_params.transferred_global_request_id = + params.transferred_global_request_id; + + source->GetController().LoadURLWithParams(load_url_params); + return source; +} + content::JavaScriptDialogManager* NativeWindow::GetJavaScriptDialogManager() { if (!dialog_manager_) dialog_manager_.reset(new AtomJavaScriptDialogManager); diff --git a/browser/native_window.h b/browser/native_window.h index 4978ef9bdb77..54cb539d755d 100644 --- a/browser/native_window.h +++ b/browser/native_window.h @@ -157,6 +157,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, const std::vector& regions) = 0; // Implementations of content::WebContentsDelegate. + virtual content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) OVERRIDE; virtual content::JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE; virtual void BeforeUnloadFired(content::WebContents* tab, diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm index 3d55ecb65e21..93ad1ff19dce 100644 --- a/browser/native_window_mac.mm +++ b/browser/native_window_mac.mm @@ -142,7 +142,7 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, : NativeWindow(web_contents, options), is_kiosk_(false), attention_request_id_(0) { - int width, height; + int width = 800, height = 600; options->GetInteger(switches::kWidth, &width); options->GetInteger(switches::kHeight, &height); diff --git a/common.gypi b/common.gypi index fd6b90d345e4..b6cc25a1cde6 100644 --- a/common.gypi +++ b/common.gypi @@ -35,7 +35,7 @@ # Settings to compile node under Windows. 'target_defaults': { 'target_conditions': [ - ['_target_name in ["libuv", "http_parser", "cares", "openssl", "node_lib", "zlib"]', { + ['_target_name in ["libuv", "http_parser", "cares", "openssl", "openssl-cli", "node_lib", "zlib"]', { 'msvs_disabled_warnings': [ 4013, # 'free' undefined; assuming extern returning int 4054, # @@ -49,13 +49,14 @@ 4204, # non-constant aggregate initializer 4214, # bit field types other than int 4232, # address of dllimport 'free' is not static, identity not guaranteed + 4291, # no matching operator delete found 4295, # array is too small to include a terminating null character 4389, # '==' : signed/unsigned mismatch 4505, # unreferenced local function has been removed 4701, # potentially uninitialized local variable 'sizew' used 4706, # assignment within conditional expression - 4804, # unsafe use of type 'bool' in operation - 4996, # + 4804, # unsafe use of type 'bool' in operation + 4996, # this function or variable may be unsafe. ], 'msvs_settings': { 'VCCLCompilerTool': { diff --git a/browser/api/atom_api_event_emitter.cc b/common/api/atom_api_event_emitter.cc similarity index 97% rename from browser/api/atom_api_event_emitter.cc rename to common/api/atom_api_event_emitter.cc index 15dc5ddedaf9..f175810ad45f 100644 --- a/browser/api/atom_api_event_emitter.cc +++ b/common/api/atom_api_event_emitter.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "browser/api/atom_api_event_emitter.h" +#include "common/api/atom_api_event_emitter.h" #include diff --git a/browser/api/atom_api_event_emitter.h b/common/api/atom_api_event_emitter.h similarity index 85% rename from browser/api/atom_api_event_emitter.h rename to common/api/atom_api_event_emitter.h index dd2292238601..81689d23bf0a 100644 --- a/browser/api/atom_api_event_emitter.h +++ b/common/api/atom_api_event_emitter.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ATOM_BROWSER_API_ATOM_API_EVENT_EMITTER_H_ -#define ATOM_BROWSER_API_ATOM_API_EVENT_EMITTER_H_ +#ifndef ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_ +#define ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_ #include @@ -40,4 +40,4 @@ class EventEmitter : public node::ObjectWrap { } // namespace atom -#endif // ATOM_BROWSER_API_ATOM_API_EVENT_EMITTER_H_ +#endif // ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_ diff --git a/common/api/atom_api_screen.cc b/common/api/atom_api_screen.cc new file mode 100644 index 000000000000..329767271f08 --- /dev/null +++ b/common/api/atom_api_screen.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "common/api/atom_api_screen.h" + +#include "common/v8/native_type_conversions.h" +#include "ui/gfx/screen.h" + +#include "common/v8/node_common.h" + +#define UNWRAP_SCREEN_AND_CHECK \ + Screen* self = ObjectWrap::Unwrap(args.This()); \ + if (self == NULL) \ + return node::ThrowError("Screen is already destroyed") + +namespace atom { + +namespace api { + +namespace { + +v8::Handle DisplayToV8Value(const gfx::Display& display) { + v8::Handle obj = v8::Object::New(); + obj->Set(ToV8Value("bounds"), ToV8Value(display.bounds())); + obj->Set(ToV8Value("workArea"), ToV8Value(display.work_area())); + obj->Set(ToV8Value("size"), ToV8Value(display.size())); + obj->Set(ToV8Value("workAreaSize"), ToV8Value(display.work_area_size())); + obj->Set(ToV8Value("scaleFactor"), ToV8Value(display.device_scale_factor())); + return obj; +} + +} // namespace + +Screen::Screen(v8::Handle wrapper) + : EventEmitter(wrapper), + screen_(gfx::Screen::GetNativeScreen()) { +} + +Screen::~Screen() { +} + +// static +void Screen::New(const v8::FunctionCallbackInfo& args) { + v8::HandleScope scope(args.GetIsolate()); + + if (!args.IsConstructCall()) + return node::ThrowError("Require constructor call"); + + new Screen(args.This()); +} + +// static +void Screen::GetCursorScreenPoint( + const v8::FunctionCallbackInfo& args) { + UNWRAP_SCREEN_AND_CHECK; + args.GetReturnValue().Set(ToV8Value(self->screen_->GetCursorScreenPoint())); +} + +// static +void Screen::GetPrimaryDisplay( + const v8::FunctionCallbackInfo& args) { + UNWRAP_SCREEN_AND_CHECK; + gfx::Display display = self->screen_->GetPrimaryDisplay(); + args.GetReturnValue().Set(DisplayToV8Value(display)); +} + +// static +void Screen::Initialize(v8::Handle target) { + v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); + + v8::Local t = v8::FunctionTemplate::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + t->SetClassName(v8::String::NewSymbol("Screen")); + + NODE_SET_PROTOTYPE_METHOD(t, "getCursorScreenPoint", GetCursorScreenPoint); + NODE_SET_PROTOTYPE_METHOD(t, "getPrimaryDisplay", GetPrimaryDisplay); + + target->Set(v8::String::NewSymbol("Screen"), t->GetFunction()); +} + +} // namespace api + +} // namespace atom + +NODE_MODULE(atom_common_screen, atom::api::Screen::Initialize) diff --git a/common/api/atom_api_screen.h b/common/api/atom_api_screen.h new file mode 100644 index 000000000000..df762f3e8d60 --- /dev/null +++ b/common/api/atom_api_screen.h @@ -0,0 +1,44 @@ +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_API_ATOM_API_SCREEN_H_ +#define ATOM_COMMON_API_ATOM_API_SCREEN_H_ + +#include "common/api/atom_api_event_emitter.h" + +namespace gfx { +class Screen; +} + +namespace atom { + +namespace api { + +class Screen : public EventEmitter { + public: + virtual ~Screen(); + + static void Initialize(v8::Handle target); + + protected: + explicit Screen(v8::Handle wrapper); + + private: + static void New(const v8::FunctionCallbackInfo& args); + + static void GetCursorScreenPoint( + const v8::FunctionCallbackInfo& args); + static void GetPrimaryDisplay( + const v8::FunctionCallbackInfo& args); + + gfx::Screen* screen_; + + DISALLOW_COPY_AND_ASSIGN(Screen); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_COMMON_API_ATOM_API_SCREEN_H_ diff --git a/common/api/atom_bindings.cc b/common/api/atom_bindings.cc index f8fbccc81198..8dc3b85a45a1 100644 --- a/common/api/atom_bindings.cc +++ b/common/api/atom_bindings.cc @@ -16,11 +16,41 @@ namespace { static int kMaxCallStackSize = 200; // Same with WebKit. -static uv_async_t dummy_uv_handle; +// Async handle to wake up uv loop. +static uv_async_t g_next_tick_uv_handle; +// Async handle to execute the stored v8 callback. +static uv_async_t g_callback_uv_handle; + +// Stored v8 callback, to be called by the async handler. +RefCountedV8Function g_v8_callback; + +// Dummy class type that used for crashing the program. struct DummyClass { bool crash; }; -void UvNoOp(uv_async_t* handle, int status) { +// Async handler to call next process.nextTick callbacks. +void UvCallNextTick(uv_async_t* handle, int status) { + node::Environment* env = node::Environment::GetCurrent(node_isolate); + node::Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->in_tick()) + return; + + if (tick_info->length() == 0) { + tick_info->set_index(0); + return; + } + + tick_info->set_in_tick(true); + env->tick_callback_function()->Call(env->process_object(), 0, NULL); + tick_info->set_in_tick(false); +} + +// Async handler to execute the stored v8 callback. +void UvOnCallback(uv_async_t* handle, int status) { + v8::HandleScope handle_scope(node_isolate); + v8::Handle global = v8::Context::GetCurrent()->Global(); + g_v8_callback->NewHandle()->Call(global, 0, NULL); } v8::Handle DumpStackFrame(v8::Handle stack_frame) { @@ -44,7 +74,8 @@ v8::Handle DumpStackFrame(v8::Handle stack_frame) { node::node_module_struct* GetBuiltinModule(const char *name, bool is_browser); AtomBindings::AtomBindings() { - uv_async_init(uv_default_loop(), &dummy_uv_handle, UvNoOp); + uv_async_init(uv_default_loop(), &g_next_tick_uv_handle, UvCallNextTick); + uv_async_init(uv_default_loop(), &g_callback_uv_handle, UvOnCallback); } AtomBindings::~AtomBindings() { @@ -58,6 +89,7 @@ void AtomBindings::BindTo(v8::Handle process) { NODE_SET_METHOD(process, "activateUvLoop", ActivateUVLoop); NODE_SET_METHOD(process, "log", Log); NODE_SET_METHOD(process, "getCurrentStackTrace", GetCurrentStackTrace); + NODE_SET_METHOD(process, "scheduleCallback", ScheduleCallback); process->Get(v8::String::New("versions"))->ToObject()-> Set(v8::String::New("atom-shell"), v8::String::New(ATOM_VERSION_STRING)); @@ -115,7 +147,7 @@ void AtomBindings::Crash(const v8::FunctionCallbackInfo& args) { // static void AtomBindings::ActivateUVLoop( const v8::FunctionCallbackInfo& args) { - uv_async_send(&dummy_uv_handle); + uv_async_send(&g_next_tick_uv_handle); } // static @@ -146,4 +178,12 @@ void AtomBindings::GetCurrentStackTrace( args.GetReturnValue().Set(result); } +// static +void AtomBindings::ScheduleCallback( + const v8::FunctionCallbackInfo& args) { + if (!FromV8Arguments(args, &g_v8_callback)) + return node::ThrowTypeError("Bad arguments"); + uv_async_send(&g_callback_uv_handle); +} + } // namespace atom diff --git a/common/api/atom_bindings.h b/common/api/atom_bindings.h index a414bea22566..27b2cee908c0 100644 --- a/common/api/atom_bindings.h +++ b/common/api/atom_bindings.h @@ -26,6 +26,7 @@ class AtomBindings { static void Log(const v8::FunctionCallbackInfo& args); static void GetCurrentStackTrace( const v8::FunctionCallbackInfo& args); + static void ScheduleCallback(const v8::FunctionCallbackInfo& args); DISALLOW_COPY_AND_ASSIGN(AtomBindings); }; diff --git a/common/api/atom_extensions.h b/common/api/atom_extensions.h index c373d49a3f98..5bba348c0ed4 100644 --- a/common/api/atom_extensions.h +++ b/common/api/atom_extensions.h @@ -27,6 +27,7 @@ NODE_EXT_LIST_ITEM(atom_renderer_ipc) NODE_EXT_LIST_ITEM(atom_common_clipboard) NODE_EXT_LIST_ITEM(atom_common_crash_reporter) NODE_EXT_LIST_ITEM(atom_common_id_weak_map) +NODE_EXT_LIST_ITEM(atom_common_screen) NODE_EXT_LIST_ITEM(atom_common_shell) NODE_EXT_LIST_ITEM(atom_common_v8_util) diff --git a/common/api/lib/screen.coffee b/common/api/lib/screen.coffee new file mode 100644 index 000000000000..fa859f1ad626 --- /dev/null +++ b/common/api/lib/screen.coffee @@ -0,0 +1,3 @@ +{Screen} = process.atomBinding 'screen' + +module.exports = new Screen diff --git a/common/atom_version.h b/common/atom_version.h index a41763fc8b5b..f6786ed1b944 100644 --- a/common/atom_version.h +++ b/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 0 #define ATOM_MINOR_VERSION 8 -#define ATOM_PATCH_VERSION 1 +#define ATOM_PATCH_VERSION 5 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/common/lib/init.coffee b/common/lib/init.coffee new file mode 100644 index 000000000000..48c1b29424ea --- /dev/null +++ b/common/lib/init.coffee @@ -0,0 +1,28 @@ +path = require 'path' +timers = require 'timers' +Module = require 'module' + +# Add common/api/lib to module search paths. +globalPaths = Module.globalPaths +globalPaths.push path.join(process.resourcesPath, 'common', 'api', 'lib') + +# setImmediate and process.nextTick makes use of uv_check and uv_prepare to +# run the callbacks, however since we only run uv loop on requests, the +# callbacks wouldn't be called until something else activated the uv loop, +# which would delay the callbacks for arbitrary long time. So we should +# initiatively activate the uv loop once setImmediate and process.nextTick is +# called. +wrapWithActivateUvLoop = (func) -> + -> + process.activateUvLoop() + func.apply this, arguments +process.nextTick = wrapWithActivateUvLoop process.nextTick +global.setImmediate = wrapWithActivateUvLoop timers.setImmediate +global.clearImmediate = timers.clearImmediate + +# The child_process module also needs to activate the uv loop to make the ipc +# channel setup. +# TODO(zcbenz): Find out why this is needed. +childProcess = require 'child_process' +childProcess.spawn = wrapWithActivateUvLoop childProcess.spawn +childProcess.fork = wrapWithActivateUvLoop childProcess.fork diff --git a/common/node_bindings.cc b/common/node_bindings.cc index a349aef011f9..e36ba9baff4d 100644 --- a/common/node_bindings.cc +++ b/common/node_bindings.cc @@ -65,7 +65,9 @@ NodeBindings::NodeBindings(bool is_browser) : is_browser_(is_browser), message_loop_(NULL), uv_loop_(uv_default_loop()), - embed_closed_(false) { + embed_closed_(false), + uv_env_(NULL), + weak_factory_(this) { } NodeBindings::~NodeBindings() { @@ -76,7 +78,6 @@ NodeBindings::~NodeBindings() { // Wait for everything to be done. uv_thread_join(&embed_thread_); - message_loop_->RunUntilIdle(); // Clear uv. uv_sem_destroy(&embed_sem_); @@ -193,9 +194,13 @@ void NodeBindings::RunMessageLoop() { void NodeBindings::UvRunOnce() { DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - // Enter node context while dealing with uv events. v8::HandleScope handle_scope(node_isolate); - v8::Context::Scope context_scope(global_env->context()); + + // Enter node context while dealing with uv events, by default the global + // env would be used unless user specified another one (this happens for + // renderer process, which wraps the uv loop with web page context). + node::Environment* env = uv_env() ? uv_env() : global_env; + v8::Context::Scope context_scope(env->context()); // Deal with uv events. int r = uv_run(uv_loop_, (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT)); @@ -209,7 +214,7 @@ void NodeBindings::UvRunOnce() { void NodeBindings::WakeupMainThread() { DCHECK(message_loop_); message_loop_->PostTask(FROM_HERE, base::Bind(&NodeBindings::UvRunOnce, - base::Unretained(this))); + weak_factory_.GetWeakPtr())); } void NodeBindings::WakeupEmbedThread() { @@ -220,11 +225,20 @@ void NodeBindings::WakeupEmbedThread() { void NodeBindings::EmbedThreadRunner(void *arg) { NodeBindings* self = static_cast(arg); - while (!self->embed_closed_) { + while (true) { // Wait for the main loop to deal with events. uv_sem_wait(&self->embed_sem_); + if (self->embed_closed_) + break; + // Wait for something to happen in uv loop. + // Note that the PollEvents() is implemented by derived classes, so when + // this class is being destructed the PollEvents() would not be available + // anymore. Because of it we must make sure we only invoke PollEvents() + // when this class is alive. self->PollEvents(); + if (self->embed_closed_) + break; // Deal with event in main thread. self->WakeupMainThread(); diff --git a/common/node_bindings.h b/common/node_bindings.h index 7ed22806d115..7f61ed4832ed 100644 --- a/common/node_bindings.h +++ b/common/node_bindings.h @@ -6,6 +6,7 @@ #define ATOM_COMMON_NODE_BINDINGS_H_ #include "base/basictypes.h" +#include "base/memory/weak_ptr.h" #include "v8/include/v8.h" #include "vendor/node/deps/uv/include/uv.h" @@ -37,6 +38,10 @@ class NodeBindings { // Do message loop integration. virtual void RunMessageLoop(); + // Gets/sets the environment to wrap uv loop. + void set_uv_env(node::Environment* env) { uv_env_ = env; } + node::Environment* uv_env() const { return uv_env_; } + protected: explicit NodeBindings(bool is_browser); @@ -83,6 +88,11 @@ class NodeBindings { // Semaphore to wait for main loop in the embed thread. uv_sem_t embed_sem_; + // Environment that to wrap the uv loop. + node::Environment* uv_env_; + + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(NodeBindings); }; diff --git a/common/v8/native_type_conversions.h b/common/v8/native_type_conversions.h index 4949b6147e51..2beed8befc3d 100644 --- a/common/v8/native_type_conversions.h +++ b/common/v8/native_type_conversions.h @@ -17,8 +17,9 @@ #include "common/swap_or_assign.h" #include "common/v8/scoped_persistent.h" #include "common/v8/v8_value_converter.h" -#include "content/public/renderer/v8_value_converter.h" +#include "ui/gfx/point.h" #include "ui/gfx/rect.h" +#include "ui/gfx/size.h" #include "url/gurl.h" // Convert V8 value to arbitrary supported types. @@ -71,8 +72,9 @@ struct FromV8Value { converter->FromV8Value(value_, v8::Context::GetCurrent())); } - operator std::vector() { - std::vector array; + template + operator std::vector() { + std::vector array; v8::Handle v8_array = v8::Handle::Cast(value_); for (uint32_t i = 0; i < v8_array->Length(); ++i) array.push_back(FromV8Value(v8_array->Get(i))); @@ -80,14 +82,15 @@ struct FromV8Value { return array; } - operator std::map() { - std::map dict; + template + operator std::map() { + std::map dict; v8::Handle v8_dict = value_->ToObject(); v8::Handle v8_keys = v8_dict->GetOwnPropertyNames(); for (uint32_t i = 0; i < v8_keys->Length(); ++i) { v8::Handle v8_key = v8_keys->Get(i); - std::string key = FromV8Value(v8_key); - dict[key] = std::string(FromV8Value(v8_dict->Get(v8_key))); + K key = FromV8Value(v8_key); + dict[key] = V(FromV8Value(v8_dict->Get(v8_key))); } return dict; @@ -121,6 +124,14 @@ inline v8::Handle ToV8Value(bool b) { return v8::Boolean::New(b); } +inline v8::Handle ToV8Value(float f) { + return v8::Number::New(f); +} + +inline v8::Handle ToV8Value(double f) { + return v8::Number::New(f); +} + inline v8::Handle ToV8Value(const char* s) { return v8::String::New(s); } @@ -146,14 +157,37 @@ inline v8::Handle ToV8Value(void* whatever) { return v8::Undefined(); } -inline -v8::Handle ToV8Value(const std::vector& paths) { - v8::Handle result = v8::Array::New(paths.size()); - for (size_t i = 0; i < paths.size(); ++i) - result->Set(i, ToV8Value(paths[i])); +template inline +v8::Handle ToV8Value(const std::vector& arr) { + v8::Handle result = v8::Array::New(arr.size()); + for (size_t i = 0; i < arr.size(); ++i) + result->Set(i, ToV8Value(arr[i])); return result; } +inline v8::Handle ToV8Value(const gfx::Point& point) { + v8::Handle obj = v8::Object::New(); + obj->Set(ToV8Value("x"), ToV8Value(point.x())); + obj->Set(ToV8Value("y"), ToV8Value(point.y())); + return obj; +} + +inline v8::Handle ToV8Value(const gfx::Rect& rect) { + v8::Handle obj = v8::Object::New(); + obj->Set(ToV8Value("x"), ToV8Value(rect.x())); + obj->Set(ToV8Value("y"), ToV8Value(rect.y())); + obj->Set(ToV8Value("width"), ToV8Value(rect.width())); + obj->Set(ToV8Value("height"), ToV8Value(rect.height())); + return obj; +} + +inline v8::Handle ToV8Value(const gfx::Size& size) { + v8::Handle obj = v8::Object::New(); + obj->Set(ToV8Value("width"), ToV8Value(size.width())); + obj->Set(ToV8Value("height"), ToV8Value(size.height())); + return obj; +} + // Check if a V8 Value is of specified type. template inline bool V8ValueCanBeConvertedTo(v8::Handle value) { diff --git a/common/v8/node_common.h b/common/v8/node_common.h index 679ec348573d..88a7505f37ce 100644 --- a/common/v8/node_common.h +++ b/common/v8/node_common.h @@ -8,6 +8,8 @@ // Include common headers for using node APIs. #undef CHECK +#undef CHECK_EQ +#undef CHECK_NE #undef DISALLOW_COPY_AND_ASSIGN #include "vendor/node/src/env.h" #include "vendor/node/src/env-inl.h" diff --git a/docs/README.md b/docs/README.md index 6433da3cf160..cf0cc9905ff2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -35,4 +35,5 @@ Common modules: * [clipboard](api/common/clipboard.md) * [crash-reporter](api/common/crash-reporter.md) +* [screen](api/common/screen.md) * [shell](api/common/shell.md) diff --git a/docs/api/common/screen.md b/docs/api/common/screen.md new file mode 100644 index 000000000000..a5e8c9cb9372 --- /dev/null +++ b/docs/api/common/screen.md @@ -0,0 +1,11 @@ +# screen + +Gets various info about screen size, displays, cursor position, etc. + +## screen.getCursorScreenPoint() + +Returns the current absolute position of the mouse pointer. + +## screen.getPrimaryDisplay() + +Returns the primary display. diff --git a/package.json b/package.json index 50695c80bbd1..021684cd0b30 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,17 @@ { "name": "atom-shell", - "version": "0.8.1", + "version": "0.8.5", "devDependencies": { "coffee-script": "~1.6.3", "coffeelint": "~0.6.1", "mocha": "~1.13.0", + "pathwatcher": "0.14.0", + "q": "0.9.7", "walkdir": "~0.0.7", "runas": "0.3.0", - "formidable": "~1.0.14" + "formidable": "~1.0.14", + "temp": "~0.6.0" }, "private": true, diff --git a/renderer/atom_renderer_client.cc b/renderer/atom_renderer_client.cc index cd2074696678..c89b6c256e68 100644 --- a/renderer/atom_renderer_client.cc +++ b/renderer/atom_renderer_client.cc @@ -4,6 +4,8 @@ #include "renderer/atom_renderer_client.h" +#include + #include "common/node_bindings.h" #include "renderer/api/atom_renderer_bindings.h" #include "renderer/atom_render_view_observer.h" @@ -51,10 +53,17 @@ void AtomRendererClient::DidCreateScriptContext(WebKit::WebFrame* frame, node_bindings_->RunMessageLoop(); // Setup node environment for each window. - node_bindings_->CreateEnvironment(context); + 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); + + // Make uv loop being wrapped by window context. + if (node_bindings_->uv_env() == NULL) + node_bindings_->set_uv_env(env); } void AtomRendererClient::WillReleaseScriptContext( @@ -67,7 +76,36 @@ void AtomRendererClient::WillReleaseScriptContext( return; } - env->Dispose(); + // 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); + } +} + +bool AtomRendererClient::ShouldFork(WebKit::WebFrame* frame, + const GURL& url, + const std::string& http_method, + bool is_initial_navigation, + bool is_server_redirect, + bool* send_referrer) { + // Handle all the navigations and reloads in browser. + return true; } } // namespace atom diff --git a/renderer/atom_renderer_client.h b/renderer/atom_renderer_client.h index e66ea87000a4..3dde5eb1d950 100644 --- a/renderer/atom_renderer_client.h +++ b/renderer/atom_renderer_client.h @@ -5,8 +5,14 @@ #ifndef ATOM_RENDERER_ATOM_RENDERER_CLIENT_H_ #define ATOM_RENDERER_ATOM_RENDERER_CLIENT_H_ +#include + #include "content/public/renderer/content_renderer_client.h" +namespace node { +class Environment; +} + namespace atom { class AtomRendererBindings; @@ -29,6 +35,14 @@ class AtomRendererClient : public content::ContentRendererClient { virtual void WillReleaseScriptContext(WebKit::WebFrame* frame, v8::Handle, int world_id) OVERRIDE; + virtual bool ShouldFork(WebKit::WebFrame* frame, + const GURL& url, + const std::string& http_method, + bool is_initial_navigation, + bool is_server_redirect, + bool* send_referrer); + + std::vector web_page_envs_; scoped_ptr node_bindings_; scoped_ptr atom_bindings_; diff --git a/renderer/lib/init.coffee b/renderer/lib/init.coffee index 30e1de4e88e3..230529d39baa 100644 --- a/renderer/lib/init.coffee +++ b/renderer/lib/init.coffee @@ -1,5 +1,4 @@ path = require 'path' -timers = require 'timers' Module = require 'module' # Expose information of current process. @@ -14,29 +13,16 @@ process.argv.splice 1, 1 # of Atom's built-in libraries. globalPaths = 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') # And also app. globalPaths.push path.join(process.resourcesPath, 'app') +# Import common settings. +require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js') + # Expose global variables. global.require = require global.module = module -# setImmediate and process.nextTick makes use of uv_check and uv_prepare to -# run the callbacks, however since we only run uv loop on requests, the -# callbacks wouldn't be called until something else activated the uv loop, -# which would delay the callbacks for arbitrary long time. So we should -# initiatively activate the uv loop once setImmediate and process.nextTick is -# called. -wrapWithActivateUvLoop = (func) -> - -> - process.activateUvLoop() - func.apply this, arguments -process.nextTick = wrapWithActivateUvLoop process.nextTick -global.setImmediate = wrapWithActivateUvLoop timers.setImmediate -global.clearImmediate = timers.clearImmediate - # Set the __filename to the path of html file if it's file:// protocol. if window.location.protocol is 'file:' global.__filename = @@ -46,6 +32,9 @@ if window.location.protocol is 'file:' window.location.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 diff --git a/script/create-dist.py b/script/create-dist.py index 521665886fc3..5d96a8f0a1f8 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -12,7 +12,7 @@ from lib.util import scoped_cwd, rm_rf, get_atom_shell_version, make_zip, \ ATOM_SHELL_VRESION = get_atom_shell_version() -NODE_VERSION = 'v0.11.9' +NODE_VERSION = 'v0.11.10' BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent' SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) diff --git a/script/upload.py b/script/upload.py index 3698bb0527b3..c51cc22eaea5 100755 --- a/script/upload.py +++ b/script/upload.py @@ -21,7 +21,7 @@ TARGET_PLATFORM = { ATOM_SHELL_REPO = 'atom/atom-shell' ATOM_SHELL_VRESION = get_atom_shell_version() -NODE_VERSION = 'v0.11.9' +NODE_VERSION = 'v0.11.10' SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release') diff --git a/spec/api-screen-spec.coffee b/spec/api-screen-spec.coffee new file mode 100644 index 000000000000..341524fca81b --- /dev/null +++ b/spec/api-screen-spec.coffee @@ -0,0 +1,16 @@ +assert = require 'assert' +screen = require 'screen' + +describe 'screen module', -> + describe 'screen.getCursorScreenPoint()', -> + it 'returns a point object', -> + point = screen.getCursorScreenPoint() + assert.equal typeof(point.x), 'number' + assert.equal typeof(point.y), 'number' + + describe 'screen.getPrimaryDisplay()', -> + it 'returns a display object', -> + display = screen.getPrimaryDisplay() + assert.equal typeof(display.scaleFactor), 'number' + assert display.size.width > 0 + assert display.size.height > 0 diff --git a/spec/modules-spec.coffee b/spec/modules-spec.coffee index 0eab1e62fbd4..39d0181df12b 100644 --- a/spec/modules-spec.coffee +++ b/spec/modules-spec.coffee @@ -1,9 +1,11 @@ assert = require 'assert' fs = require 'fs' path = require 'path' +temp = require 'temp' describe 'third-party module', -> fixtures = path.join __dirname, 'fixtures' + temp.track() describe 'runas', -> it 'can be required in renderer', -> @@ -15,3 +17,25 @@ describe 'third-party module', -> child.on 'message', (msg) -> assert.equal msg, 'ok' done() + + describe 'pathwatcher', -> + it 'emits file events correctly', (done) -> + pathwatcher = require 'pathwatcher' + temp.mkdir 'dir', (err, dir) -> + assert err == null + file = path.join dir, 'file' + fs.writeFileSync file, 'content' + watcher = pathwatcher.watch file, (event) -> + assert.equal event, 'change' + watcher.close() + done() + fs.writeFileSync file, 'content2' + + describe 'q', -> + Q = require 'q' + + describe 'Q.when', -> + it 'emits the fullfil callback', (done) -> + Q(true).then (val) -> + assert.equal val, true + done() diff --git a/spec/node-spec.coffee b/spec/node-spec.coffee index 90ac22b4e51d..7eb84593f511 100644 --- a/spec/node-spec.coffee +++ b/spec/node-spec.coffee @@ -37,6 +37,11 @@ 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!') diff --git a/vendor/apm b/vendor/apm index 89f678cc3485..35edbb07fb4a 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit 89f678cc34850ffa0163c4ac485336d40c72bb86 +Subproject commit 35edbb07fb4abba49dd97d12a1ad8c4adb71625f diff --git a/vendor/node b/vendor/node index b5bbfb6a7d66..184a341c2b95 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit b5bbfb6a7d66750c21bf654e973f47f1178adced +Subproject commit 184a341c2b959a092f64b168e3d55b11d13583bf