Make BrowserWindow inheritable. Fixed #23.

This commit is contained in:
Cheng Zhao 2013-06-18 18:35:08 +08:00
parent eaa03facee
commit 5a0aab2e4f
5 changed files with 87 additions and 28 deletions

View file

@ -71,6 +71,26 @@ bool EventEmitter::Emit(const std::string& name, base::ListValue* args) {
return prevent_default; return prevent_default;
} }
// static
v8::Handle<v8::Value> EventEmitter::FromConstructorTemplate(
v8::Persistent<v8::FunctionTemplate> t,
const v8::Arguments& args) {
v8::Handle<v8::Value> new_object = node::FromConstructorTemplate(t, args);
args.This()->SetHiddenValue(v8::String::New("native_object"), new_object);
return new_object;
}
// static
v8::Handle<v8::Object> EventEmitter::SearchNativeObject(
v8::Handle<v8::Object> handle) {
if (handle->InternalFieldCount() > 0)
return handle;
// If the object is not a native object, we assume the native object is
// stored in it.
return handle->GetHiddenValue(v8::String::New("native_object"))->ToObject();
}
} // namespace api } // namespace api
} // namespace atom } // namespace atom

View file

@ -29,12 +29,37 @@ class EventEmitter : public node::ObjectWrap {
bool Emit(const std::string& name); bool Emit(const std::string& name);
bool Emit(const std::string& name, base::ListValue* args); bool Emit(const std::string& name, base::ListValue* args);
// Same with Unwrap but if the handle is not a native object, which happened
// when user inherits a native class, we would search for the native object,
// otherwise we won't get the correct C++ object.
template <typename T>
static inline bool SafeUnwrap(v8::Handle<v8::Object> handle, T** pointer) {
v8::Handle<v8::Object> real_handle = SearchNativeObject(handle);
if (real_handle->InternalFieldCount() == 0)
return false;
*pointer = Unwrap<T>(real_handle);
return true;
}
// Helper function to call "new" when constructor is called as normal
// function, this usually happens when inheriting native class.
//
// This function also does some special hacks to make sure the inheritance
// would work, because normal inheritance would not inherit internal fields.
static v8::Handle<v8::Value> FromConstructorTemplate(
v8::Persistent<v8::FunctionTemplate> t,
const v8::Arguments& args);
// Small accessor to return handle_, this follows Google C++ Style. // Small accessor to return handle_, this follows Google C++ Style.
v8::Persistent<v8::Object> handle() const { return handle_; } v8::Persistent<v8::Object> handle() const { return handle_; }
protected: protected:
explicit EventEmitter(v8::Handle<v8::Object> wrapper); explicit EventEmitter(v8::Handle<v8::Object> wrapper);
static v8::Handle<v8::Object> SearchNativeObject(
v8::Handle<v8::Object> handle);
private: private:
DISALLOW_COPY_AND_ASSIGN(EventEmitter); DISALLOW_COPY_AND_ASSIGN(EventEmitter);
}; };

View file

@ -19,9 +19,13 @@ using content::NavigationController;
using node::ObjectWrap; using node::ObjectWrap;
#define UNWRAP_WINDOW_AND_CHECK \ #define UNWRAP_WINDOW_AND_CHECK \
Window* self = ObjectWrap::Unwrap<Window>(args.This()); \ Window* self; \
if (self == NULL) \ if (SafeUnwrap<Window>(args.This(), &self)) { \
return node::ThrowError("Window is already destroyed") if (self == NULL) \
return node::ThrowError("Window is already destroyed"); \
} else { \
return node::ThrowTypeError("Window methods are not generic");\
}
namespace atom { namespace atom {
@ -36,6 +40,9 @@ v8::Handle<v8::String> UTF16ToV8String(const string16& s) {
} // namespace } // namespace
// static
v8::Persistent<v8::FunctionTemplate> Window::constructor_template_;
Window::Window(v8::Handle<v8::Object> wrapper, base::DictionaryValue* options) Window::Window(v8::Handle<v8::Object> wrapper, base::DictionaryValue* options)
: EventEmitter(wrapper), : EventEmitter(wrapper),
window_(NativeWindow::Create(options)) { window_(NativeWindow::Create(options)) {
@ -85,10 +92,10 @@ void Window::OnRendererCrashed() {
// static // static
v8::Handle<v8::Value> Window::New(const v8::Arguments &args) { v8::Handle<v8::Value> Window::New(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args.IsConstructCall()) if (!args.IsConstructCall())
return node::ThrowError("Require constructor call"); return FromConstructorTemplate(constructor_template_, args);
v8::HandleScope scope;
if (!args[0]->IsObject()) if (!args[0]->IsObject())
return node::ThrowTypeError("Need options creating Window"); return node::ThrowTypeError("Need options creating Window");
@ -643,7 +650,10 @@ void Window::Initialize(v8::Handle<v8::Object> target) {
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(Window::New); v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(Window::New);
t->InstanceTemplate()->SetInternalFieldCount(1); t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(v8::String::NewSymbol("BrowserWindow")); t->SetClassName(v8::String::NewSymbol("Window"));
constructor_template_ = v8::Persistent<v8::FunctionTemplate>::New(
node::node_isolate, t);
NODE_SET_PROTOTYPE_METHOD(t, "destroy", Destroy); NODE_SET_PROTOTYPE_METHOD(t, "destroy", Destroy);
NODE_SET_PROTOTYPE_METHOD(t, "close", Close); NODE_SET_PROTOTYPE_METHOD(t, "close", Close);
@ -703,7 +713,7 @@ void Window::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "reload", Reload); NODE_SET_PROTOTYPE_METHOD(t, "reload", Reload);
NODE_SET_PROTOTYPE_METHOD(t, "reloadIgnoringCache", ReloadIgnoringCache); NODE_SET_PROTOTYPE_METHOD(t, "reloadIgnoringCache", ReloadIgnoringCache);
target->Set(v8::String::NewSymbol("BrowserWindow"), t->GetFunction()); target->Set(v8::String::NewSymbol("Window"), t->GetFunction());
} }
} // namespace api } // namespace api

View file

@ -105,6 +105,8 @@ class Window : public EventEmitter,
static v8::Handle<v8::Value> Reload(const v8::Arguments &args); static v8::Handle<v8::Value> Reload(const v8::Arguments &args);
static v8::Handle<v8::Value> ReloadIgnoringCache(const v8::Arguments &args); static v8::Handle<v8::Value> ReloadIgnoringCache(const v8::Arguments &args);
static v8::Persistent<v8::FunctionTemplate> constructor_template_;
scoped_ptr<NativeWindow> window_; scoped_ptr<NativeWindow> window_;
DISALLOW_COPY_AND_ASSIGN(Window); DISALLOW_COPY_AND_ASSIGN(Window);

View file

@ -2,28 +2,30 @@ EventEmitter = require('events').EventEmitter
v8Util = process.atomBinding 'v8_util' v8Util = process.atomBinding 'v8_util'
objectsRegistry = require '../../atom/objects-registry.js' objectsRegistry = require '../../atom/objects-registry.js'
BrowserWindow = process.atomBinding('window').BrowserWindow Window = process.atomBinding('window').Window
BrowserWindow::__proto__ = EventEmitter.prototype Window::__proto__ = EventEmitter.prototype
BrowserWindow::toggleDevTools = -> module.exports = class BrowserWindow extends Window
opened = v8Util.getHiddenValue this, 'devtoolsOpened' constructor: ->
if opened super
@closeDevTools()
v8Util.setHiddenValue this, 'devtoolsOpened', false
else
@openDevTools()
v8Util.setHiddenValue this, 'devtoolsOpened', true
BrowserWindow::restart = -> toggleDevTools: ->
@loadUrl(@getUrl()) opened = v8Util.getHiddenValue this, 'devtoolsOpened'
if opened
@closeDevTools()
v8Util.setHiddenValue this, 'devtoolsOpened', false
else
@openDevTools()
v8Util.setHiddenValue this, 'devtoolsOpened', true
BrowserWindow.getFocusedWindow = -> restart: ->
windows = objectsRegistry.getAllWindows() @loadUrl @getUrl()
return window for window in windows when window.isFocused()
BrowserWindow.fromProcessIdAndRoutingId = (processId, routingId) -> @getFocusedWindow: ->
windows = objectsRegistry.getAllWindows() windows = objectsRegistry.getAllWindows()
return window for window in windows when window.getProcessId() == processId and return window for window in windows when window.isFocused()
window.getRoutingId() == routingId
module.exports = BrowserWindow @fromProcessIdAndRoutingId = (processId, routingId) ->
windows = objectsRegistry.getAllWindows()
return window for window in windows when window.getProcessId() == processId and
window.getRoutingId() == routingId