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:
BaseClass()20   BaseClass() : value_(23) {}
~BaseClass()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 
Create(v8::Isolate * isolate)34   static gin::Handle<MyObject> Create(v8::Isolate* isolate) {
35     return CreateHandle(isolate, new MyObject());
36   }
37 
value() const38   int value() const { return value_; }
set_value(int value)39   void set_value(int value) { value_ = value; }
40 
41  protected:
MyObject()42   MyObject() : value_(0) {}
43   virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
44       v8::Isolate* isolate) OVERRIDE;
~MyObject()45   virtual ~MyObject() {}
46 
47  private:
48   int value_;
49 };
50 
51 class MyObjectSubclass : public MyObject {
52  public:
Create(v8::Isolate * isolate)53   static gin::Handle<MyObjectSubclass> Create(v8::Isolate* isolate) {
54     return CreateHandle(isolate, new MyObjectSubclass());
55   }
56 
SayHello(const std::string & name)57   void SayHello(const std::string& name) {
58     result = std::string("Hello, ") + name;
59   }
60 
61   std::string result;
62 
63  private:
GetObjectTemplateBuilder(v8::Isolate * isolate)64   virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
65       v8::Isolate* isolate) OVERRIDE {
66     return MyObject::GetObjectTemplateBuilder(isolate)
67         .SetMethod("sayHello", &MyObjectSubclass::SayHello);
68   }
69 
MyObjectSubclass()70   MyObjectSubclass() {
71   }
72 
~MyObjectSubclass()73   virtual ~MyObjectSubclass() {
74   }
75 };
76 
77 class MyCallableObject : public Wrappable<MyCallableObject> {
78  public:
79   static WrapperInfo kWrapperInfo;
80 
Create(v8::Isolate * isolate)81   static gin::Handle<MyCallableObject> Create(v8::Isolate* isolate) {
82     return CreateHandle(isolate, new MyCallableObject());
83   }
84 
result()85   int result() { return result_; }
86 
87  private:
GetObjectTemplateBuilder(v8::Isolate * isolate)88   virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
89       v8::Isolate* isolate) OVERRIDE {
90     return Wrappable<MyCallableObject>::GetObjectTemplateBuilder(isolate)
91         .SetCallAsFunctionHandler(&MyCallableObject::Call);
92   }
93 
MyCallableObject()94   MyCallableObject() : result_(0) {
95   }
96 
~MyCallableObject()97   virtual ~MyCallableObject() {
98   }
99 
Call(int val,const gin::Arguments & arguments)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 };
GetObjectTemplateBuilder(v8::Isolate * isolate)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 
TEST_F(WrappableTest,WrapAndUnwrap)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 
TEST_F(WrappableTest,UnwrapFailures)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 
TEST_F(WrappableTest,GetAndSetProperty)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 
TEST_F(WrappableTest,WrappableSubclass)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 
TEST_F(WrappableTest,CallAsFunction)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 
TEST_F(WrappableTest,CallAsConstructor)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