1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/logging.h" 6 #include "gin/arguments.h" 7 #include "gin/handle.h" 8 #include "gin/object_template_builder.h" 9 #include "gin/per_isolate_data.h" 10 #include "gin/public/isolate_holder.h" 11 #include "gin/test/v8_test.h" 12 #include "gin/try_catch.h" 13 #include "gin/wrappable.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 16 namespace gin { 17 18 class BaseClass { 19 public: 20 BaseClass() : value_(23) {} 21 virtual ~BaseClass() {} 22 23 private: 24 int value_; 25 26 DISALLOW_COPY_AND_ASSIGN(BaseClass); 27 }; 28 29 class MyObject : public BaseClass, 30 public Wrappable<MyObject> { 31 public: 32 static WrapperInfo kWrapperInfo; 33 34 static gin::Handle<MyObject> Create(v8::Isolate* isolate) { 35 return CreateHandle(isolate, new MyObject()); 36 } 37 38 int value() const { return value_; } 39 void set_value(int value) { value_ = value; } 40 41 protected: 42 MyObject() : value_(0) {} 43 virtual ObjectTemplateBuilder GetObjectTemplateBuilder( 44 v8::Isolate* isolate) OVERRIDE; 45 virtual ~MyObject() {} 46 47 private: 48 int value_; 49 }; 50 51 class MyObjectSubclass : public MyObject { 52 public: 53 static gin::Handle<MyObjectSubclass> Create(v8::Isolate* isolate) { 54 return CreateHandle(isolate, new MyObjectSubclass()); 55 } 56 57 void SayHello(const std::string& name) { 58 result = std::string("Hello, ") + name; 59 } 60 61 std::string result; 62 63 private: 64 virtual ObjectTemplateBuilder GetObjectTemplateBuilder( 65 v8::Isolate* isolate) OVERRIDE { 66 return MyObject::GetObjectTemplateBuilder(isolate) 67 .SetMethod("sayHello", &MyObjectSubclass::SayHello); 68 } 69 70 MyObjectSubclass() { 71 } 72 73 virtual ~MyObjectSubclass() { 74 } 75 }; 76 77 class MyCallableObject : public Wrappable<MyCallableObject> { 78 public: 79 static WrapperInfo kWrapperInfo; 80 81 static gin::Handle<MyCallableObject> Create(v8::Isolate* isolate) { 82 return CreateHandle(isolate, new MyCallableObject()); 83 } 84 85 int result() { return result_; } 86 87 private: 88 virtual ObjectTemplateBuilder GetObjectTemplateBuilder( 89 v8::Isolate* isolate) OVERRIDE { 90 return Wrappable<MyCallableObject>::GetObjectTemplateBuilder(isolate) 91 .SetCallAsFunctionHandler(&MyCallableObject::Call); 92 } 93 94 MyCallableObject() : result_(0) { 95 } 96 97 virtual ~MyCallableObject() { 98 } 99 100 void Call(int val, const gin::Arguments& arguments) { 101 if (arguments.IsConstructCall()) 102 arguments.ThrowTypeError("Cannot be called as constructor."); 103 else 104 result_ = val; 105 } 106 107 int result_; 108 }; 109 110 class MyObject2 : public Wrappable<MyObject2> { 111 public: 112 static WrapperInfo kWrapperInfo; 113 }; 114 115 class MyObjectBlink : public Wrappable<MyObjectBlink> { 116 public: 117 static WrapperInfo kWrapperInfo; 118 }; 119 120 WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin }; 121 ObjectTemplateBuilder MyObject::GetObjectTemplateBuilder(v8::Isolate* isolate) { 122 return Wrappable<MyObject>::GetObjectTemplateBuilder(isolate) 123 .SetProperty("value", &MyObject::value, &MyObject::set_value); 124 } 125 126 WrapperInfo MyCallableObject::kWrapperInfo = { kEmbedderNativeGin }; 127 WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin }; 128 WrapperInfo MyObjectBlink::kWrapperInfo = { kEmbedderNativeGin }; 129 130 typedef V8Test WrappableTest; 131 132 TEST_F(WrappableTest, WrapAndUnwrap) { 133 v8::Isolate* isolate = instance_->isolate(); 134 v8::HandleScope handle_scope(isolate); 135 136 Handle<MyObject> obj = MyObject::Create(isolate); 137 138 v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get()); 139 EXPECT_FALSE(wrapper.IsEmpty()); 140 141 MyObject* unwrapped = NULL; 142 EXPECT_TRUE(ConvertFromV8(isolate, wrapper, &unwrapped)); 143 EXPECT_EQ(obj.get(), unwrapped); 144 } 145 146 TEST_F(WrappableTest, UnwrapFailures) { 147 v8::Isolate* isolate = instance_->isolate(); 148 v8::HandleScope handle_scope(isolate); 149 150 // Something that isn't an object. 151 v8::Handle<v8::Value> thing = v8::Number::New(isolate, 42); 152 MyObject* unwrapped = NULL; 153 EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); 154 EXPECT_FALSE(unwrapped); 155 156 // An object that's not wrapping anything. 157 thing = v8::Object::New(isolate); 158 EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); 159 EXPECT_FALSE(unwrapped); 160 161 // An object that's wrapping a C++ object from Blink. 162 thing.Clear(); 163 thing = ConvertToV8(isolate, new MyObjectBlink()); 164 EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); 165 EXPECT_FALSE(unwrapped); 166 167 // An object that's wrapping a C++ object of the wrong type. 168 thing.Clear(); 169 thing = ConvertToV8(isolate, new MyObject2()); 170 EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); 171 EXPECT_FALSE(unwrapped); 172 } 173 174 TEST_F(WrappableTest, GetAndSetProperty) { 175 v8::Isolate* isolate = instance_->isolate(); 176 v8::HandleScope handle_scope(isolate); 177 178 gin::Handle<MyObject> obj = MyObject::Create(isolate); 179 180 obj->set_value(42); 181 EXPECT_EQ(42, obj->value()); 182 183 v8::Handle<v8::String> source = StringToV8(isolate, 184 "(function (obj) {" 185 " if (obj.value !== 42) throw 'FAIL';" 186 " else obj.value = 191; })"); 187 EXPECT_FALSE(source.IsEmpty()); 188 189 gin::TryCatch try_catch; 190 v8::Handle<v8::Script> script = v8::Script::Compile(source); 191 EXPECT_FALSE(script.IsEmpty()); 192 v8::Handle<v8::Value> val = script->Run(); 193 EXPECT_FALSE(val.IsEmpty()); 194 v8::Handle<v8::Function> func; 195 EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); 196 v8::Handle<v8::Value> argv[] = { 197 ConvertToV8(isolate, obj.get()), 198 }; 199 func->Call(v8::Undefined(isolate), 1, argv); 200 EXPECT_FALSE(try_catch.HasCaught()); 201 EXPECT_EQ("", try_catch.GetStackTrace()); 202 203 EXPECT_EQ(191, obj->value()); 204 } 205 206 TEST_F(WrappableTest, WrappableSubclass) { 207 v8::Isolate* isolate = instance_->isolate(); 208 v8::HandleScope handle_scope(isolate); 209 210 gin::Handle<MyObjectSubclass> object(MyObjectSubclass::Create(isolate)); 211 v8::Handle<v8::String> source = StringToV8(isolate, 212 "(function(obj) {" 213 "obj.sayHello('Lily');" 214 "})"); 215 gin::TryCatch try_catch; 216 v8::Handle<v8::Script> script = v8::Script::Compile(source); 217 v8::Handle<v8::Value> val = script->Run(); 218 v8::Handle<v8::Function> func; 219 EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); 220 v8::Handle<v8::Value> argv[] = { 221 ConvertToV8(isolate, object.get()) 222 }; 223 func->Call(v8::Undefined(isolate), 1, argv); 224 EXPECT_FALSE(try_catch.HasCaught()); 225 EXPECT_EQ("Hello, Lily", object->result); 226 } 227 228 TEST_F(WrappableTest, CallAsFunction) { 229 v8::Isolate* isolate = instance_->isolate(); 230 v8::HandleScope handle_scope(isolate); 231 232 gin::Handle<MyCallableObject> object(MyCallableObject::Create(isolate)); 233 EXPECT_EQ(0, object->result()); 234 v8::Handle<v8::String> source = StringToV8(isolate, 235 "(function(obj) {" 236 "obj(42);" 237 "})"); 238 gin::TryCatch try_catch; 239 v8::Handle<v8::Script> script = v8::Script::Compile(source); 240 v8::Handle<v8::Value> val = script->Run(); 241 v8::Handle<v8::Function> func; 242 EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); 243 v8::Handle<v8::Value> argv[] = { 244 ConvertToV8(isolate, object.get()) 245 }; 246 func->Call(v8::Undefined(isolate), 1, argv); 247 EXPECT_FALSE(try_catch.HasCaught()); 248 EXPECT_EQ(42, object->result()); 249 } 250 251 TEST_F(WrappableTest, CallAsConstructor) { 252 v8::Isolate* isolate = instance_->isolate(); 253 v8::HandleScope handle_scope(isolate); 254 255 gin::Handle<MyCallableObject> object(MyCallableObject::Create(isolate)); 256 EXPECT_EQ(0, object->result()); 257 v8::Handle<v8::String> source = StringToV8(isolate, 258 "(function(obj) {" 259 "new obj(42);" 260 "})"); 261 gin::TryCatch try_catch; 262 v8::Handle<v8::Script> script = v8::Script::Compile(source); 263 v8::Handle<v8::Value> val = script->Run(); 264 v8::Handle<v8::Function> func; 265 EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); 266 v8::Handle<v8::Value> argv[] = { 267 ConvertToV8(isolate, object.get()) 268 }; 269 func->Call(v8::Undefined(isolate), 1, argv); 270 EXPECT_TRUE(try_catch.HasCaught()); 271 } 272 273 } // namespace gin 274