// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/logging.h" #include "gin/arguments.h" #include "gin/handle.h" #include "gin/interceptor.h" #include "gin/object_template_builder.h" #include "gin/per_isolate_data.h" #include "gin/public/isolate_holder.h" #include "gin/test/v8_test.h" #include "gin/try_catch.h" #include "gin/wrappable.h" #include "testing/gtest/include/gtest/gtest.h" #include "v8/include/v8-util.h" namespace gin { class MyInterceptor : public Wrappable, public NamedPropertyInterceptor, public IndexedPropertyInterceptor { public: static WrapperInfo kWrapperInfo; static gin::Handle Create(v8::Isolate* isolate) { return CreateHandle(isolate, new MyInterceptor(isolate)); } int value() const { return value_; } void set_value(int value) { value_ = value; } // gin::NamedPropertyInterceptor virtual v8::Local GetNamedProperty(v8::Isolate* isolate, const std::string& property) OVERRIDE { if (property == "value") { return ConvertToV8(isolate, value_); } else if (property == "func") { return GetFunctionTemplate(isolate, "func")->GetFunction(); } else { return v8::Local(); } } virtual bool SetNamedProperty(v8::Isolate* isolate, const std::string& property, v8::Local value) OVERRIDE { if (property == "value") { ConvertFromV8(isolate, value, &value_); return true; } return false; } virtual std::vector EnumerateNamedProperties( v8::Isolate* isolate) OVERRIDE { std::vector result; result.push_back("func"); result.push_back("value"); return result; } // gin::IndexedPropertyInterceptor virtual v8::Local GetIndexedProperty(v8::Isolate* isolate, uint32_t index) OVERRIDE { if (index == 0) return ConvertToV8(isolate, value_); return v8::Local(); } virtual bool SetIndexedProperty(v8::Isolate* isolate, uint32_t index, v8::Local value) OVERRIDE { if (index == 0) { ConvertFromV8(isolate, value, &value_); return true; } // Don't allow bypassing the interceptor. return true; } virtual std::vector EnumerateIndexedProperties(v8::Isolate* isolate) OVERRIDE { std::vector result; result.push_back(0); return result; } private: explicit MyInterceptor(v8::Isolate* isolate) : NamedPropertyInterceptor(isolate, this), IndexedPropertyInterceptor(isolate, this), value_(0), template_cache_(isolate) {} virtual ~MyInterceptor() {} // gin::Wrappable virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) OVERRIDE { return Wrappable::GetObjectTemplateBuilder(isolate) .AddNamedPropertyInterceptor() .AddIndexedPropertyInterceptor(); } int Call(int value) { int tmp = value_; value_ = value; return tmp; } v8::Local GetFunctionTemplate(v8::Isolate* isolate, const std::string& name) { v8::Local function_template = template_cache_.Get(name); if (!function_template.IsEmpty()) return function_template; function_template = CreateFunctionTemplate( isolate, base::Bind(&MyInterceptor::Call), HolderIsFirstArgument); template_cache_.Set(name, function_template); return function_template; } int value_; v8::StdPersistentValueMap template_cache_; DISALLOW_COPY_AND_ASSIGN(MyInterceptor); }; WrapperInfo MyInterceptor::kWrapperInfo = {kEmbedderNativeGin}; class InterceptorTest : public V8Test { public: void RunInterceptorTest(const std::string& script_source) { v8::Isolate* isolate = instance_->isolate(); v8::HandleScope handle_scope(isolate); gin::Handle obj = MyInterceptor::Create(isolate); obj->set_value(42); EXPECT_EQ(42, obj->value()); v8::Handle source = StringToV8(isolate, script_source); EXPECT_FALSE(source.IsEmpty()); gin::TryCatch try_catch; v8::Handle script = v8::Script::Compile(source); EXPECT_FALSE(script.IsEmpty()); v8::Handle val = script->Run(); EXPECT_FALSE(val.IsEmpty()); v8::Handle func; EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); v8::Handle argv[] = {ConvertToV8(isolate, obj.get()), }; func->Call(v8::Undefined(isolate), 1, argv); EXPECT_FALSE(try_catch.HasCaught()); EXPECT_EQ("", try_catch.GetStackTrace()); EXPECT_EQ(191, obj->value()); } }; TEST_F(InterceptorTest, NamedInterceptor) { RunInterceptorTest( "(function (obj) {" " if (obj.value !== 42) throw 'FAIL';" " else obj.value = 191; })"); } TEST_F(InterceptorTest, NamedInterceptorCall) { RunInterceptorTest( "(function (obj) {" " if (obj.func(191) !== 42) throw 'FAIL';" " })"); } TEST_F(InterceptorTest, IndexedInterceptor) { RunInterceptorTest( "(function (obj) {" " if (obj[0] !== 42) throw 'FAIL';" " else obj[0] = 191; })"); } TEST_F(InterceptorTest, BypassInterceptorAllowed) { RunInterceptorTest( "(function (obj) {" " obj.value = 191 /* make test happy */;" " obj.foo = 23;" " if (obj.foo !== 23) throw 'FAIL'; })"); } TEST_F(InterceptorTest, BypassInterceptorForbidden) { RunInterceptorTest( "(function (obj) {" " obj.value = 191 /* make test happy */;" " obj[1] = 23;" " if (obj[1] === 23) throw 'FAIL'; })"); } } // namespace gin