1// Copyright 2014 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/interceptor.h" 9#include "gin/object_template_builder.h" 10#include "gin/per_isolate_data.h" 11#include "gin/public/isolate_holder.h" 12#include "gin/test/v8_test.h" 13#include "gin/try_catch.h" 14#include "gin/wrappable.h" 15#include "testing/gtest/include/gtest/gtest.h" 16#include "v8/include/v8-util.h" 17 18namespace gin { 19 20class MyInterceptor : public Wrappable<MyInterceptor>, 21 public NamedPropertyInterceptor, 22 public IndexedPropertyInterceptor { 23 public: 24 static WrapperInfo kWrapperInfo; 25 26 static gin::Handle<MyInterceptor> Create(v8::Isolate* isolate) { 27 return CreateHandle(isolate, new MyInterceptor(isolate)); 28 } 29 30 int value() const { return value_; } 31 void set_value(int value) { value_ = value; } 32 33 // gin::NamedPropertyInterceptor 34 virtual v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate, 35 const std::string& property) 36 OVERRIDE { 37 if (property == "value") { 38 return ConvertToV8(isolate, value_); 39 } else if (property == "func") { 40 return GetFunctionTemplate(isolate, "func")->GetFunction(); 41 } else { 42 return v8::Local<v8::Value>(); 43 } 44 } 45 virtual bool SetNamedProperty(v8::Isolate* isolate, 46 const std::string& property, 47 v8::Local<v8::Value> value) OVERRIDE { 48 if (property == "value") { 49 ConvertFromV8(isolate, value, &value_); 50 return true; 51 } 52 return false; 53 } 54 virtual std::vector<std::string> EnumerateNamedProperties( 55 v8::Isolate* isolate) OVERRIDE { 56 std::vector<std::string> result; 57 result.push_back("func"); 58 result.push_back("value"); 59 return result; 60 } 61 62 // gin::IndexedPropertyInterceptor 63 virtual v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate, 64 uint32_t index) OVERRIDE { 65 if (index == 0) 66 return ConvertToV8(isolate, value_); 67 return v8::Local<v8::Value>(); 68 } 69 virtual bool SetIndexedProperty(v8::Isolate* isolate, 70 uint32_t index, 71 v8::Local<v8::Value> value) OVERRIDE { 72 if (index == 0) { 73 ConvertFromV8(isolate, value, &value_); 74 return true; 75 } 76 // Don't allow bypassing the interceptor. 77 return true; 78 } 79 virtual std::vector<uint32_t> EnumerateIndexedProperties(v8::Isolate* isolate) 80 OVERRIDE { 81 std::vector<uint32_t> result; 82 result.push_back(0); 83 return result; 84 } 85 86 private: 87 explicit MyInterceptor(v8::Isolate* isolate) 88 : NamedPropertyInterceptor(isolate, this), 89 IndexedPropertyInterceptor(isolate, this), 90 value_(0), 91 template_cache_(isolate) {} 92 virtual ~MyInterceptor() {} 93 94 // gin::Wrappable 95 virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) 96 OVERRIDE { 97 return Wrappable<MyInterceptor>::GetObjectTemplateBuilder(isolate) 98 .AddNamedPropertyInterceptor() 99 .AddIndexedPropertyInterceptor(); 100 } 101 102 int Call(int value) { 103 int tmp = value_; 104 value_ = value; 105 return tmp; 106 } 107 108 v8::Local<v8::FunctionTemplate> GetFunctionTemplate(v8::Isolate* isolate, 109 const std::string& name) { 110 v8::Local<v8::FunctionTemplate> function_template = 111 template_cache_.Get(name); 112 if (!function_template.IsEmpty()) 113 return function_template; 114 function_template = CreateFunctionTemplate( 115 isolate, base::Bind(&MyInterceptor::Call), HolderIsFirstArgument); 116 template_cache_.Set(name, function_template); 117 return function_template; 118 } 119 120 int value_; 121 122 v8::StdPersistentValueMap<std::string, v8::FunctionTemplate> template_cache_; 123 124 DISALLOW_COPY_AND_ASSIGN(MyInterceptor); 125}; 126 127WrapperInfo MyInterceptor::kWrapperInfo = {kEmbedderNativeGin}; 128 129class InterceptorTest : public V8Test { 130 public: 131 void RunInterceptorTest(const std::string& script_source) { 132 v8::Isolate* isolate = instance_->isolate(); 133 v8::HandleScope handle_scope(isolate); 134 135 gin::Handle<MyInterceptor> obj = MyInterceptor::Create(isolate); 136 137 obj->set_value(42); 138 EXPECT_EQ(42, obj->value()); 139 140 v8::Handle<v8::String> source = StringToV8(isolate, script_source); 141 EXPECT_FALSE(source.IsEmpty()); 142 143 gin::TryCatch try_catch; 144 v8::Handle<v8::Script> script = v8::Script::Compile(source); 145 EXPECT_FALSE(script.IsEmpty()); 146 v8::Handle<v8::Value> val = script->Run(); 147 EXPECT_FALSE(val.IsEmpty()); 148 v8::Handle<v8::Function> func; 149 EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); 150 v8::Handle<v8::Value> argv[] = {ConvertToV8(isolate, obj.get()), }; 151 func->Call(v8::Undefined(isolate), 1, argv); 152 EXPECT_FALSE(try_catch.HasCaught()); 153 EXPECT_EQ("", try_catch.GetStackTrace()); 154 155 EXPECT_EQ(191, obj->value()); 156 } 157}; 158 159TEST_F(InterceptorTest, NamedInterceptor) { 160 RunInterceptorTest( 161 "(function (obj) {" 162 " if (obj.value !== 42) throw 'FAIL';" 163 " else obj.value = 191; })"); 164} 165 166TEST_F(InterceptorTest, NamedInterceptorCall) { 167 RunInterceptorTest( 168 "(function (obj) {" 169 " if (obj.func(191) !== 42) throw 'FAIL';" 170 " })"); 171} 172 173TEST_F(InterceptorTest, IndexedInterceptor) { 174 RunInterceptorTest( 175 "(function (obj) {" 176 " if (obj[0] !== 42) throw 'FAIL';" 177 " else obj[0] = 191; })"); 178} 179 180TEST_F(InterceptorTest, BypassInterceptorAllowed) { 181 RunInterceptorTest( 182 "(function (obj) {" 183 " obj.value = 191 /* make test happy */;" 184 " obj.foo = 23;" 185 " if (obj.foo !== 23) throw 'FAIL'; })"); 186} 187 188TEST_F(InterceptorTest, BypassInterceptorForbidden) { 189 RunInterceptorTest( 190 "(function (obj) {" 191 " obj.value = 191 /* make test happy */;" 192 " obj[1] = 23;" 193 " if (obj[1] === 23) throw 'FAIL'; })"); 194} 195 196} // namespace gin 197