Home | History | Annotate | Download | only in gin
      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) {
    101     result_ = val;
    102   }
    103 
    104   int result_;
    105 };
    106 
    107 class MyObject2 : public Wrappable<MyObject2> {
    108  public:
    109   static WrapperInfo kWrapperInfo;
    110 };
    111 
    112 class MyObjectBlink : public Wrappable<MyObjectBlink> {
    113  public:
    114   static WrapperInfo kWrapperInfo;
    115 };
    116 
    117 WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin };
    118 ObjectTemplateBuilder MyObject::GetObjectTemplateBuilder(v8::Isolate* isolate) {
    119   return Wrappable<MyObject>::GetObjectTemplateBuilder(isolate)
    120       .SetProperty("value", &MyObject::value, &MyObject::set_value);
    121 }
    122 
    123 WrapperInfo MyCallableObject::kWrapperInfo = { kEmbedderNativeGin };
    124 WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin };
    125 WrapperInfo MyObjectBlink::kWrapperInfo = { kEmbedderNativeGin };
    126 
    127 typedef V8Test WrappableTest;
    128 
    129 TEST_F(WrappableTest, WrapAndUnwrap) {
    130   v8::Isolate* isolate = instance_->isolate();
    131   v8::HandleScope handle_scope(isolate);
    132 
    133   Handle<MyObject> obj = MyObject::Create(isolate);
    134 
    135   v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get());
    136   EXPECT_FALSE(wrapper.IsEmpty());
    137 
    138   MyObject* unwrapped = NULL;
    139   EXPECT_TRUE(ConvertFromV8(isolate, wrapper, &unwrapped));
    140   EXPECT_EQ(obj.get(), unwrapped);
    141 }
    142 
    143 TEST_F(WrappableTest, UnwrapFailures) {
    144   v8::Isolate* isolate = instance_->isolate();
    145   v8::HandleScope handle_scope(isolate);
    146 
    147   // Something that isn't an object.
    148   v8::Handle<v8::Value> thing = v8::Number::New(isolate, 42);
    149   MyObject* unwrapped = NULL;
    150   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
    151   EXPECT_FALSE(unwrapped);
    152 
    153   // An object that's not wrapping anything.
    154   thing = v8::Object::New(isolate);
    155   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
    156   EXPECT_FALSE(unwrapped);
    157 
    158   // An object that's wrapping a C++ object from Blink.
    159   thing.Clear();
    160   thing = ConvertToV8(isolate, new MyObjectBlink());
    161   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
    162   EXPECT_FALSE(unwrapped);
    163 
    164   // An object that's wrapping a C++ object of the wrong type.
    165   thing.Clear();
    166   thing = ConvertToV8(isolate, new MyObject2());
    167   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
    168   EXPECT_FALSE(unwrapped);
    169 }
    170 
    171 TEST_F(WrappableTest, GetAndSetProperty) {
    172   v8::Isolate* isolate = instance_->isolate();
    173   v8::HandleScope handle_scope(isolate);
    174 
    175   gin::Handle<MyObject> obj = MyObject::Create(isolate);
    176 
    177   obj->set_value(42);
    178   EXPECT_EQ(42, obj->value());
    179 
    180   v8::Handle<v8::String> source = StringToV8(isolate,
    181       "(function (obj) {"
    182       "   if (obj.value !== 42) throw 'FAIL';"
    183       "   else obj.value = 191; })");
    184   EXPECT_FALSE(source.IsEmpty());
    185 
    186   gin::TryCatch try_catch;
    187   v8::Handle<v8::Script> script = v8::Script::Compile(source);
    188   EXPECT_FALSE(script.IsEmpty());
    189   v8::Handle<v8::Value> val = script->Run();
    190   EXPECT_FALSE(val.IsEmpty());
    191   v8::Handle<v8::Function> func;
    192   EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
    193   v8::Handle<v8::Value> argv[] = {
    194     ConvertToV8(isolate, obj.get()),
    195   };
    196   func->Call(v8::Undefined(isolate), 1, argv);
    197   EXPECT_FALSE(try_catch.HasCaught());
    198   EXPECT_EQ("", try_catch.GetStackTrace());
    199 
    200   EXPECT_EQ(191, obj->value());
    201 }
    202 
    203 TEST_F(WrappableTest, WrappableSubclass) {
    204   v8::Isolate* isolate = instance_->isolate();
    205   v8::HandleScope handle_scope(isolate);
    206 
    207   gin::Handle<MyObjectSubclass> object(MyObjectSubclass::Create(isolate));
    208   v8::Handle<v8::String> source = StringToV8(isolate,
    209                                              "(function(obj) {"
    210                                              "obj.sayHello('Lily');"
    211                                              "})");
    212   gin::TryCatch try_catch;
    213   v8::Handle<v8::Script> script = v8::Script::Compile(source);
    214   v8::Handle<v8::Value> val = script->Run();
    215   v8::Handle<v8::Function> func;
    216   EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
    217   v8::Handle<v8::Value> argv[] = {
    218     ConvertToV8(isolate, object.get())
    219   };
    220   func->Call(v8::Undefined(isolate), 1, argv);
    221   EXPECT_FALSE(try_catch.HasCaught());
    222   EXPECT_EQ("Hello, Lily", object->result);
    223 }
    224 
    225 TEST_F(WrappableTest, ErrorInObjectConstructorProperty) {
    226   v8::Isolate* isolate = instance_->isolate();
    227   v8::HandleScope handle_scope(isolate);
    228 
    229   v8::Handle<v8::String> source = StringToV8(
    230       isolate,
    231       "(function() {"
    232       "  Object.defineProperty(Object.prototype, 'constructor', {"
    233       "    get: function() { throw 'Error'; },"
    234       "    set: function() { throw 'Error'; }"
    235       "  });"
    236       "})();");
    237   EXPECT_FALSE(source.IsEmpty());
    238   v8::Handle<v8::Script> script = v8::Script::Compile(source);
    239   script->Run();
    240 
    241   gin::TryCatch try_catch;
    242   gin::Handle<MyObject> obj = MyObject::Create(isolate);
    243   EXPECT_TRUE(obj.IsEmpty());
    244   EXPECT_TRUE(try_catch.HasCaught());
    245 }
    246 
    247 TEST_F(WrappableTest, CallAsFunction) {
    248   v8::Isolate* isolate = instance_->isolate();
    249   v8::HandleScope handle_scope(isolate);
    250 
    251   gin::Handle<MyCallableObject> object(MyCallableObject::Create(isolate));
    252   EXPECT_EQ(0, object->result());
    253   v8::Handle<v8::String> source = StringToV8(isolate,
    254                                              "(function(obj) {"
    255                                              "obj(42);"
    256                                              "})");
    257   gin::TryCatch try_catch;
    258   v8::Handle<v8::Script> script = v8::Script::Compile(source);
    259   v8::Handle<v8::Value> val = script->Run();
    260   v8::Handle<v8::Function> func;
    261   EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
    262   v8::Handle<v8::Value> argv[] = {
    263     ConvertToV8(isolate, object.get())
    264   };
    265   func->Call(v8::Undefined(isolate), 1, argv);
    266   EXPECT_FALSE(try_catch.HasCaught());
    267   EXPECT_EQ(42, object->result());
    268 }
    269 
    270 }  // namespace gin
    271