Index: test/cctest/test-object-observe.cc |
diff --git a/test/cctest/test-object-observe.cc b/test/cctest/test-object-observe.cc |
index e7cf1e5993746179ade2ad6e26caf23b61d89973..b129ff3af4ceb2e2fa4f78b0e60ce7f3765180aa 100644 |
--- a/test/cctest/test-object-observe.cc |
+++ b/test/cctest/test-object-observe.cc |
@@ -313,11 +313,13 @@ static void ExpectRecords(Handle<Value> records, |
recordObj->Get(String::New("object")))); |
CHECK(String::New(expectations[i].type)->Equals( |
recordObj->Get(String::New("type")))); |
- CHECK(String::New(expectations[i].name)->Equals( |
- recordObj->Get(String::New("name")))); |
- if (!expectations[i].old_value.IsEmpty()) { |
- CHECK(expectations[i].old_value->Equals( |
- recordObj->Get(String::New("oldValue")))); |
+ if (strcmp("splice", expectations[i].type) != 0) { |
+ CHECK(String::New(expectations[i].name)->Equals( |
+ recordObj->Get(String::New("name")))); |
+ if (!expectations[i].old_value.IsEmpty()) { |
+ CHECK(expectations[i].old_value->Equals( |
+ recordObj->Get(String::New("oldValue")))); |
+ } |
} |
} |
} |
@@ -446,3 +448,270 @@ TEST(ObservationWeakMap) { |
CHECK_EQ(0, NumberOfElements(objectInfoMap)); |
CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap)); |
} |
+ |
+ |
+static bool NamedAccessAlwaysAllowed(Local<Object>, Local<Value>, AccessType, |
+ Local<Value>) { |
+ return true; |
+} |
+ |
+ |
+static bool IndexedAccessAlwaysAllowed(Local<Object>, uint32_t, AccessType, |
+ Local<Value>) { |
+ return true; |
+} |
+ |
+ |
+static AccessType g_access_block_type = ACCESS_GET; |
+ |
+ |
+static bool NamedAccessAllowUnlessBlocked(Local<Object> host, |
+ Local<Value> key, |
+ AccessType type, |
+ Local<Value>) { |
+ if (type != g_access_block_type) return true; |
+ Handle<Object> global = Context::GetCurrent()->Global(); |
+ Handle<Value> blacklist = global->Get(String::New("blacklist")); |
+ if (!blacklist->IsObject()) return true; |
+ if (key->IsString()) return !blacklist.As<Object>()->Has(key); |
+ return true; |
+} |
+ |
+ |
+static bool IndexedAccessAllowUnlessBlocked(Local<Object> host, |
+ uint32_t index, |
+ AccessType type, |
+ Local<Value>) { |
+ if (type != ACCESS_GET) return true; |
+ Handle<Object> global = Context::GetCurrent()->Global(); |
+ Handle<Value> blacklist = global->Get(String::New("blacklist")); |
+ if (!blacklist->IsObject()) return true; |
+ return !blacklist.As<Object>()->Has(index); |
+} |
+ |
+ |
+static bool BlockAccessKeys(Local<Object> host, Local<Value> key, |
+ AccessType type, Local<Value>) { |
+ Handle<Object> global = Context::GetCurrent()->Global(); |
+ Handle<Value> blacklist = global->Get(String::New("blacklist")); |
+ if (!blacklist->IsObject()) return true; |
+ return type != ACCESS_KEYS || |
+ !blacklist.As<Object>()->Has(String::New("__block_access_keys")); |
+} |
+ |
+ |
+static Handle<Object> CreateAccessCheckedObject( |
+ NamedSecurityCallback namedCallback, |
+ IndexedSecurityCallback indexedCallback) { |
+ Handle<ObjectTemplate> tmpl = ObjectTemplate::New(); |
+ tmpl->SetAccessCheckCallbacks(namedCallback, indexedCallback); |
+ Handle<Object> instance = tmpl->NewInstance(); |
+ instance->CreationContext()->Global()->Set(String::New("obj"), instance); |
+ return instance; |
+} |
+ |
+ |
+TEST(NamedAccessCheck) { |
+ HarmonyIsolate isolate; |
+ const AccessType types[] = { ACCESS_GET, ACCESS_HAS }; |
+ for (size_t i = 0; i < ARRAY_SIZE(types); ++i) { |
+ HandleScope scope(isolate.GetIsolate()); |
+ LocalContext context; |
+ g_access_block_type = types[i]; |
+ Handle<Object> instance = CreateAccessCheckedObject( |
+ NamedAccessAllowUnlessBlocked, IndexedAccessAlwaysAllowed); |
+ CompileRun("var records = null;" |
+ "var objNoCheck = {};" |
+ "var blacklist = {foo: true};" |
+ "var observer = function(r) { records = r };" |
+ "Object.observe(obj, observer);" |
+ "Object.observe(objNoCheck, observer);"); |
+ Handle<Value> obj_no_check = CompileRun("objNoCheck"); |
+ { |
+ LocalContext context2; |
+ context2->Global()->Set(String::New("obj"), instance); |
+ context2->Global()->Set(String::New("objNoCheck"), obj_no_check); |
+ CompileRun("var records2 = null;" |
+ "var observer2 = function(r) { records2 = r };" |
+ "Object.observe(obj, observer2);" |
+ "Object.observe(objNoCheck, observer2);" |
+ "obj.foo = 'bar';" |
+ "Object.defineProperty(obj, 'foo', {value: 5});" |
+ "Object.defineProperty(obj, 'foo', {get: function(){}});" |
+ "obj.bar = 'baz';" |
+ "objNoCheck.baz = 'quux'"); |
+ const RecordExpectation expected_records2[] = { |
+ { instance, "new", "foo", Handle<Value>() }, |
+ { instance, "updated", "foo", String::New("bar") }, |
+ { instance, "reconfigured", "foo", Number::New(5) }, |
+ { instance, "new", "bar", Handle<Value>() }, |
+ { obj_no_check, "new", "baz", Handle<Value>() }, |
+ }; |
+ EXPECT_RECORDS(CompileRun("records2"), expected_records2); |
+ } |
+ const RecordExpectation expected_records[] = { |
+ { instance, "new", "bar", Handle<Value>() }, |
+ { obj_no_check, "new", "baz", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records"), expected_records); |
+ } |
+} |
+ |
+ |
+TEST(IndexedAccessCheck) { |
+ HarmonyIsolate isolate; |
+ const AccessType types[] = { ACCESS_GET, ACCESS_HAS }; |
+ for (size_t i = 0; i < ARRAY_SIZE(types); ++i) { |
+ HandleScope scope(isolate.GetIsolate()); |
+ LocalContext context; |
+ g_access_block_type = types[i]; |
+ Handle<Object> instance = CreateAccessCheckedObject( |
+ NamedAccessAlwaysAllowed, IndexedAccessAllowUnlessBlocked); |
+ CompileRun("var records = null;" |
+ "var objNoCheck = {};" |
+ "var blacklist = {7: true};" |
+ "var observer = function(r) { records = r };" |
+ "Object.observe(obj, observer);" |
+ "Object.observe(objNoCheck, observer);"); |
+ Handle<Value> obj_no_check = CompileRun("objNoCheck"); |
+ { |
+ LocalContext context2; |
+ context2->Global()->Set(String::New("obj"), instance); |
+ context2->Global()->Set(String::New("objNoCheck"), obj_no_check); |
+ CompileRun("var records2 = null;" |
+ "var observer2 = function(r) { records2 = r };" |
+ "Object.observe(obj, observer2);" |
+ "Object.observe(objNoCheck, observer2);" |
+ "obj[7] = 'foo';" |
+ "Object.defineProperty(obj, '7', {value: 5});" |
+ "Object.defineProperty(obj, '7', {get: function(){}});" |
+ "obj[8] = 'bar';" |
+ "objNoCheck[42] = 'quux'"); |
+ const RecordExpectation expected_records2[] = { |
+ { instance, "new", "7", Handle<Value>() }, |
+ { instance, "updated", "7", String::New("foo") }, |
+ { instance, "reconfigured", "7", Number::New(5) }, |
+ { instance, "new", "8", Handle<Value>() }, |
+ { obj_no_check, "new", "42", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records2"), expected_records2); |
+ } |
+ const RecordExpectation expected_records[] = { |
+ { instance, "new", "8", Handle<Value>() }, |
+ { obj_no_check, "new", "42", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records"), expected_records); |
+ } |
+} |
+ |
+ |
+TEST(SpliceAccessCheck) { |
+ HarmonyIsolate isolate; |
+ HandleScope scope(isolate.GetIsolate()); |
+ LocalContext context; |
+ g_access_block_type = ACCESS_GET; |
+ Handle<Object> instance = CreateAccessCheckedObject( |
+ NamedAccessAlwaysAllowed, IndexedAccessAllowUnlessBlocked); |
+ CompileRun("var records = null;" |
+ "obj[1] = 'foo';" |
+ "obj.length = 2;" |
+ "var objNoCheck = {1: 'bar', length: 2};" |
+ "var blacklist = {1: true};" |
+ "observer = function(r) { records = r };" |
+ "Array.observe(obj, observer);" |
+ "Array.observe(objNoCheck, observer);"); |
+ Handle<Value> obj_no_check = CompileRun("objNoCheck"); |
+ { |
+ LocalContext context2; |
+ context2->Global()->Set(String::New("obj"), instance); |
+ context2->Global()->Set(String::New("objNoCheck"), obj_no_check); |
+ CompileRun("var records2 = null;" |
+ "var observer2 = function(r) { records2 = r };" |
+ "Array.observe(obj, observer2);" |
+ "Array.observe(objNoCheck, observer2);" |
+ // No one should hear about this: no splice records are emitted |
+ // for access-checked objects |
+ "[].push.call(obj, 5);" |
+ "[].splice.call(obj, 1, 1);" |
+ "[].pop.call(obj);" |
+ "[].pop.call(objNoCheck);"); |
+ // TODO(adamk): Extend EXPECT_RECORDS to be able to assert more things |
+ // about splice records. For this test it's not so important since |
+ // we just want to guarantee the machinery is in operation at all. |
+ const RecordExpectation expected_records2[] = { |
+ { obj_no_check, "splice", "", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records2"), expected_records2); |
+ } |
+ const RecordExpectation expected_records[] = { |
+ { obj_no_check, "splice", "", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records"), expected_records); |
+} |
+ |
+ |
+TEST(DisallowAllForAccessKeys) { |
+ HarmonyIsolate isolate; |
+ HandleScope scope(isolate.GetIsolate()); |
+ LocalContext context; |
+ Handle<Object> instance = CreateAccessCheckedObject( |
+ BlockAccessKeys, IndexedAccessAlwaysAllowed); |
+ CompileRun("var records = null;" |
+ "var objNoCheck = {};" |
+ "var observer = function(r) { records = r };" |
+ "var blacklist = {__block_access_keys: true};" |
+ "Object.observe(obj, observer);" |
+ "Object.observe(objNoCheck, observer);"); |
+ Handle<Value> obj_no_check = CompileRun("objNoCheck"); |
+ { |
+ LocalContext context2; |
+ context2->Global()->Set(String::New("obj"), instance); |
+ context2->Global()->Set(String::New("objNoCheck"), obj_no_check); |
+ CompileRun("var records2 = null;" |
+ "var observer2 = function(r) { records2 = r };" |
+ "Object.observe(obj, observer2);" |
+ "Object.observe(objNoCheck, observer2);" |
+ "obj.foo = 'bar';" |
+ "obj[5] = 'baz';" |
+ "objNoCheck.baz = 'quux'"); |
+ const RecordExpectation expected_records2[] = { |
+ { instance, "new", "foo", Handle<Value>() }, |
+ { instance, "new", "5", Handle<Value>() }, |
+ { obj_no_check, "new", "baz", Handle<Value>() }, |
+ }; |
+ EXPECT_RECORDS(CompileRun("records2"), expected_records2); |
+ } |
+ const RecordExpectation expected_records[] = { |
+ { obj_no_check, "new", "baz", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records"), expected_records); |
+} |
+ |
+ |
+TEST(AccessCheckDisallowApiModifications) { |
+ HarmonyIsolate isolate; |
+ HandleScope scope(isolate.GetIsolate()); |
+ LocalContext context; |
+ Handle<Object> instance = CreateAccessCheckedObject( |
+ BlockAccessKeys, IndexedAccessAlwaysAllowed); |
+ CompileRun("var records = null;" |
+ "var observer = function(r) { records = r };" |
+ "var blacklist = {__block_access_keys: true};" |
+ "Object.observe(obj, observer);"); |
+ { |
+ LocalContext context2; |
+ context2->Global()->Set(String::New("obj"), instance); |
+ CompileRun("var records2 = null;" |
+ "var observer2 = function(r) { records2 = r };" |
+ "Object.observe(obj, observer2);"); |
+ instance->Set(5, String::New("bar")); |
+ instance->Set(String::New("foo"), String::New("bar")); |
+ CompileRun(""); // trigger delivery |
+ const RecordExpectation expected_records2[] = { |
+ { instance, "new", "5", Handle<Value>() }, |
+ { instance, "new", "foo", Handle<Value>() } |
+ }; |
+ EXPECT_RECORDS(CompileRun("records2"), expected_records2); |
+ } |
+ CHECK(CompileRun("records")->IsNull()); |
+} |