Avi Drissman | 4e1b7bc | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
jam@chromium.org | 20790a22 | 2013-07-25 02:23:05 | [diff] [blame] | 5 | #include "content/renderer/pepper/v8_var_converter.h" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 6 | |
avi | 1023d01 | 2015-12-25 02:39:14 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | #include <stdint.h> |
| 9 | |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 10 | #include <cmath> |
dcheng | cedca561 | 2016-04-09 01:40:15 | [diff] [blame] | 11 | #include <memory> |
Takuto Ikuta | adf31eb | 2019-01-05 00:32:48 | [diff] [blame] | 12 | #include <unordered_map> |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 13 | |
Hans Wennborg | 0917de89 | 2020-04-28 20:21:15 | [diff] [blame] | 14 | #include "base/check.h" |
Avi Drissman | adac2199 | 2023-01-11 23:46:39 | [diff] [blame] | 15 | #include "base/functional/bind.h" |
Kalvin Lee | bc3754ae | 2023-10-01 22:37:34 | [diff] [blame] | 16 | #include "base/memory/raw_ptr.h" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 17 | #include "base/memory/ref_counted.h" |
Hans Wennborg | 0917de89 | 2020-04-28 20:21:15 | [diff] [blame] | 18 | #include "base/notreached.h" |
dmichael@chromium.org | dd6d221 | 2013-09-16 17:09:14 | [diff] [blame] | 19 | #include "base/run_loop.h" |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 20 | #include "base/synchronization/waitable_event.h" |
Gabriel Charette | c710874 | 2019-08-23 03:31:40 | [diff] [blame] | 21 | #include "base/test/task_environment.h" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 22 | #include "base/values.h" |
raymes@chromium.org | ba36957a | 2013-08-23 05:26:39 | [diff] [blame] | 23 | #include "content/renderer/pepper/resource_converter.h" |
Etienne Pierre-doray | d952e09 | 2021-11-01 21:56:21 | [diff] [blame] | 24 | #include "gin/public/isolate_holder.h" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 25 | #include "ppapi/c/pp_bool.h" |
| 26 | #include "ppapi/c/pp_var.h" |
| 27 | #include "ppapi/shared_impl/array_var.h" |
| 28 | #include "ppapi/shared_impl/dictionary_var.h" |
| 29 | #include "ppapi/shared_impl/ppapi_globals.h" |
| 30 | #include "ppapi/shared_impl/proxy_lock.h" |
| 31 | #include "ppapi/shared_impl/scoped_pp_var.h" |
| 32 | #include "ppapi/shared_impl/test_globals.h" |
tommycli | a88b609 | 2015-05-14 17:54:01 | [diff] [blame] | 33 | #include "ppapi/shared_impl/test_utils.h" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 34 | #include "ppapi/shared_impl/var.h" |
| 35 | #include "ppapi/shared_impl/var_tracker.h" |
| 36 | #include "testing/gtest/include/gtest/gtest.h" |
Dan Elphick | c925673600 | 2021-09-16 09:30:11 | [diff] [blame] | 37 | #include "v8/include/v8-container.h" |
| 38 | #include "v8/include/v8-context.h" |
| 39 | #include "v8/include/v8-isolate.h" |
| 40 | #include "v8/include/v8-microtask-queue.h" |
| 41 | #include "v8/include/v8-object.h" |
| 42 | #include "v8/include/v8-persistent-handle.h" |
| 43 | #include "v8/include/v8-primitive.h" |
| 44 | #include "v8/include/v8-script.h" |
| 45 | #include "v8/include/v8-template.h" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 46 | |
| 47 | using ppapi::ArrayBufferVar; |
| 48 | using ppapi::ArrayVar; |
| 49 | using ppapi::DictionaryVar; |
| 50 | using ppapi::PpapiGlobals; |
| 51 | using ppapi::ProxyLock; |
| 52 | using ppapi::ScopedPPVar; |
| 53 | using ppapi::StringVar; |
| 54 | using ppapi::TestGlobals; |
| 55 | using ppapi::TestEqual; |
| 56 | using ppapi::VarTracker; |
| 57 | |
jam@chromium.org | adab233 | 2013-07-25 18:04:32 | [diff] [blame] | 58 | namespace content { |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 59 | |
| 60 | namespace { |
| 61 | |
raymes@chromium.org | 6d48e707 | 2014-06-16 07:23:40 | [diff] [blame] | 62 | void FromV8ValueComplete(const ScopedPPVar& scoped_var, |
| 63 | bool success) { |
| 64 | NOTREACHED(); |
| 65 | } |
| 66 | |
raymes@chromium.org | ba36957a | 2013-08-23 05:26:39 | [diff] [blame] | 67 | class MockResourceConverter : public content::ResourceConverter { |
| 68 | public: |
dcheng | 6d18e40 | 2014-10-21 12:32:52 | [diff] [blame] | 69 | ~MockResourceConverter() override {} |
| 70 | void Reset() override {} |
| 71 | bool NeedsFlush() override { return false; } |
danakj | 9d03ab6 | 2019-05-21 16:34:12 | [diff] [blame] | 72 | void Flush(base::OnceCallback<void(bool)> callback) override { NOTREACHED(); } |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 73 | bool FromV8Value(v8::Local<v8::Object> val, |
| 74 | v8::Local<v8::Context> context, |
dcheng | 6d18e40 | 2014-10-21 12:32:52 | [diff] [blame] | 75 | PP_Var* result, |
| 76 | bool* was_resource) override { |
mgiuca@chromium.org | 7b21fb43 | 2013-09-30 09:48:59 | [diff] [blame] | 77 | *was_resource = false; |
| 78 | return true; |
| 79 | } |
dcheng | 6d18e40 | 2014-10-21 12:32:52 | [diff] [blame] | 80 | bool ToV8Value(const PP_Var& var, |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 81 | v8::Local<v8::Context> context, |
| 82 | v8::Local<v8::Value>* result) override { |
mgiuca@chromium.org | 0624275 | 2014-02-24 02:13:21 | [diff] [blame] | 83 | return false; |
| 84 | } |
raymes@chromium.org | ba36957a | 2013-08-23 05:26:39 | [diff] [blame] | 85 | }; |
| 86 | |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 87 | // Maps PP_Var IDs to the V8 value handle they correspond to. |
Takuto Ikuta | adf31eb | 2019-01-05 00:32:48 | [diff] [blame] | 88 | typedef std::unordered_map<int64_t, v8::Local<v8::Value>> VarHandleMap; |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 89 | |
| 90 | bool Equals(const PP_Var& var, |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 91 | v8::Local<v8::Value> val, |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 92 | v8::Isolate* isolate, |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 93 | VarHandleMap* visited_ids) { |
jam@chromium.org | 49323b3 | 2013-08-09 16:05:06 | [diff] [blame] | 94 | if (ppapi::VarTracker::IsVarTypeRefcounted(var.type)) { |
jdoerrie | 5a73d0f | 2018-10-02 23:54:05 | [diff] [blame] | 95 | auto it = visited_ids->find(var.value.as_id); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 96 | if (it != visited_ids->end()) |
| 97 | return it->second == val; |
| 98 | (*visited_ids)[var.value.as_id] = val; |
| 99 | } |
| 100 | |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 101 | v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 102 | if (val->IsUndefined()) { |
| 103 | return var.type == PP_VARTYPE_UNDEFINED; |
| 104 | } else if (val->IsNull()) { |
| 105 | return var.type == PP_VARTYPE_NULL; |
| 106 | } else if (val->IsBoolean() || val->IsBooleanObject()) { |
| 107 | return var.type == PP_VARTYPE_BOOL && |
dcarney | 04cd964 | 2014-11-21 13:58:11 | [diff] [blame] | 108 | PP_FromBool(val->ToBoolean(isolate)->Value()) == var.value.as_bool; |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 109 | } else if (val->IsInt32()) { |
| 110 | return var.type == PP_VARTYPE_INT32 && |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 111 | val.As<v8::Int32>()->Value() == var.value.as_int; |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 112 | } else if (val->IsNumber() || val->IsNumberObject()) { |
| 113 | return var.type == PP_VARTYPE_DOUBLE && |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 114 | fabs(val->NumberValue(context).ToChecked() - var.value.as_double) <= |
dcarney | 04cd964 | 2014-11-21 13:58:11 | [diff] [blame] | 115 | 1.0e-4; |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 116 | } else if (val->IsString() || val->IsStringObject()) { |
| 117 | if (var.type != PP_VARTYPE_STRING) |
| 118 | return false; |
| 119 | StringVar* string_var = StringVar::FromPPVar(var); |
| 120 | DCHECK(string_var); |
Adam Klein | 686c0e5 | 2018-01-17 23:42:33 | [diff] [blame] | 121 | v8::String::Utf8Value utf8(isolate, val); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 122 | return std::string(*utf8, utf8.length()) == string_var->value(); |
| 123 | } else if (val->IsArray()) { |
| 124 | if (var.type != PP_VARTYPE_ARRAY) |
| 125 | return false; |
| 126 | ArrayVar* array_var = ArrayVar::FromPPVar(var); |
| 127 | DCHECK(array_var); |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 128 | v8::Local<v8::Array> v8_array = val.As<v8::Array>(); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 129 | if (v8_array->Length() != array_var->elements().size()) |
| 130 | return false; |
avi | 1023d01 | 2015-12-25 02:39:14 | [diff] [blame] | 131 | for (uint32_t i = 0; i < v8_array->Length(); ++i) { |
Dan Elphick | cc7538d0 | 2019-02-01 13:41:47 | [diff] [blame] | 132 | v8::Local<v8::Value> child_v8 = |
| 133 | v8_array->Get(context, i).ToLocalChecked(); |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 134 | if (!Equals(array_var->elements()[i].get(), child_v8, isolate, |
| 135 | visited_ids)) { |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 136 | return false; |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 137 | } |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 138 | } |
| 139 | return true; |
| 140 | } else if (val->IsObject()) { |
| 141 | if (var.type == PP_VARTYPE_ARRAY_BUFFER) { |
| 142 | // TODO(raymes): Implement this when we have tests for array buffers. |
| 143 | NOTIMPLEMENTED(); |
| 144 | return false; |
| 145 | } else { |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 146 | v8::Local<v8::Object> v8_object = val.As<v8::Object>(); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 147 | if (var.type != PP_VARTYPE_DICTIONARY) |
| 148 | return false; |
| 149 | DictionaryVar* dict_var = DictionaryVar::FromPPVar(var); |
| 150 | DCHECK(dict_var); |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 151 | v8::Local<v8::Array> property_names( |
| 152 | v8_object->GetOwnPropertyNames(context).ToLocalChecked()); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 153 | if (property_names->Length() != dict_var->key_value_map().size()) |
| 154 | return false; |
avi | 1023d01 | 2015-12-25 02:39:14 | [diff] [blame] | 155 | for (uint32_t i = 0; i < property_names->Length(); ++i) { |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 156 | v8::Local<v8::Value> key( |
| 157 | property_names->Get(context, i).ToLocalChecked()); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 158 | |
| 159 | if (!key->IsString() && !key->IsNumber()) |
| 160 | return false; |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 161 | v8::Local<v8::Value> child_v8 = |
| 162 | v8_object->Get(context, key).ToLocalChecked(); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 163 | |
Adam Klein | 686c0e5 | 2018-01-17 23:42:33 | [diff] [blame] | 164 | v8::String::Utf8Value name_utf8(isolate, key); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 165 | ScopedPPVar release_key(ScopedPPVar::PassRef(), |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 166 | StringVar::StringToPPVar(std::string( |
| 167 | *name_utf8, name_utf8.length()))); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 168 | if (!dict_var->HasKey(release_key.get())) |
| 169 | return false; |
| 170 | ScopedPPVar release_value(ScopedPPVar::PassRef(), |
| 171 | dict_var->Get(release_key.get())); |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 172 | if (!Equals(release_value.get(), child_v8, isolate, visited_ids)) { |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 173 | return false; |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 174 | } |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 175 | } |
| 176 | return true; |
| 177 | } |
| 178 | } |
| 179 | return false; |
| 180 | } |
| 181 | |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 182 | bool Equals(const PP_Var& var, v8::Local<v8::Value> val, v8::Isolate* isolate) { |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 183 | VarHandleMap var_handle_map; |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 184 | return Equals(var, val, isolate, &var_handle_map); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | class V8VarConverterTest : public testing::Test { |
| 188 | public: |
marja@chromium.org | 52d6c320 | 2013-06-11 04:28:12 | [diff] [blame] | 189 | V8VarConverterTest() |
Etienne Pierre-doray | d952e09 | 2021-11-01 21:56:21 | [diff] [blame] | 190 | : isolate_holder_(task_environment_.GetMainThreadTaskRunner(), |
| 191 | gin::IsolateHolder::IsolateType::kTest), |
| 192 | isolate_scope_(isolate_holder_.isolate()) { |
| 193 | isolate_ = isolate_holder_.isolate(); |
raymes@chromium.org | be98185 | 2013-08-27 08:07:10 | [diff] [blame] | 194 | PP_Instance dummy = 1234; |
Peter Boström | dd7e40ec | 2021-04-05 20:40:10 | [diff] [blame] | 195 | converter_ = std::make_unique<V8VarConverter>( |
| 196 | dummy, std::unique_ptr<ResourceConverter>(new MockResourceConverter)); |
raymes@chromium.org | 4a52cf25 | 2013-08-22 09:29:08 | [diff] [blame] | 197 | } |
dcheng | f576215 | 2014-10-29 02:12:06 | [diff] [blame] | 198 | ~V8VarConverterTest() override {} |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 199 | |
| 200 | // testing::Test implementation. |
dcheng | f576215 | 2014-10-29 02:12:06 | [diff] [blame] | 201 | void SetUp() override { |
dmichael@chromium.org | aafb1d4 | 2013-09-12 20:01:14 | [diff] [blame] | 202 | ProxyLock::Acquire(); |
marja@chromium.org | 52d6c320 | 2013-06-11 04:28:12 | [diff] [blame] | 203 | v8::HandleScope handle_scope(isolate_); |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 204 | v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_); |
Ivan Kotenkov | 2c0d2bb3 | 2017-11-01 15:41:28 | [diff] [blame] | 205 | context_.Reset(isolate_, v8::Context::New(isolate_, nullptr, global)); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 206 | } |
dcheng | f576215 | 2014-10-29 02:12:06 | [diff] [blame] | 207 | void TearDown() override { |
punithnayak | 24fe229 | 2023-10-17 12:21:45 | [diff] [blame] | 208 | isolate_ = nullptr; |
jochen@chromium.org | d3c97d1 | 2013-12-02 12:19:23 | [diff] [blame] | 209 | context_.Reset(); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 210 | ASSERT_TRUE(PpapiGlobals::Get()->GetVarTracker()->GetLiveVars().empty()); |
| 211 | ProxyLock::Release(); |
| 212 | } |
| 213 | |
| 214 | protected: |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 215 | bool FromV8ValueSync(v8::Local<v8::Value> val, |
| 216 | v8::Local<v8::Context> context, |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 217 | PP_Var* result) { |
danakj | 9d03ab6 | 2019-05-21 16:34:12 | [diff] [blame] | 218 | V8VarConverter::VarResult conversion_result = converter_->FromV8Value( |
| 219 | val, context, base::BindOnce(&FromV8ValueComplete)); |
raymes@chromium.org | 6d48e707 | 2014-06-16 07:23:40 | [diff] [blame] | 220 | DCHECK(conversion_result.completed_synchronously); |
| 221 | if (conversion_result.success) |
| 222 | *result = conversion_result.var.Release(); |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 223 | |
raymes@chromium.org | 6d48e707 | 2014-06-16 07:23:40 | [diff] [blame] | 224 | return conversion_result.success; |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 225 | } |
| 226 | |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 227 | bool RoundTrip(const PP_Var& var, PP_Var* result) { |
marja@chromium.org | 52d6c320 | 2013-06-11 04:28:12 | [diff] [blame] | 228 | v8::HandleScope handle_scope(isolate_); |
marja@chromium.org | 52d6c320 | 2013-06-11 04:28:12 | [diff] [blame] | 229 | v8::Local<v8::Context> context = |
| 230 | v8::Local<v8::Context>::New(isolate_, context_); |
jochen@chromium.org | d3c97d1 | 2013-12-02 12:19:23 | [diff] [blame] | 231 | v8::Context::Scope context_scope(context); |
Camillo Bruni | d0313f7a | 2023-07-03 17:44:52 | [diff] [blame] | 232 | v8::MicrotasksScope microtasks(context, |
Jochen Eisinger | d00cc71 | 2021-04-17 07:23:13 | [diff] [blame] | 233 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 234 | v8::Local<v8::Value> v8_result; |
raymes@chromium.org | ba36957a | 2013-08-23 05:26:39 | [diff] [blame] | 235 | if (!converter_->ToV8Value(var, context, &v8_result)) |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 236 | return false; |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 237 | if (!Equals(var, v8_result, isolate_)) { |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 238 | return false; |
Dave Tapuska | 28169c9 | 2023-09-13 14:50:00 | [diff] [blame] | 239 | } |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 240 | if (!FromV8ValueSync(v8_result, context, result)) |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 241 | return false; |
| 242 | return true; |
| 243 | } |
| 244 | |
| 245 | // Assumes a ref for var. |
| 246 | bool RoundTripAndCompare(const PP_Var& var) { |
| 247 | ScopedPPVar expected(ScopedPPVar::PassRef(), var); |
| 248 | PP_Var actual_var; |
| 249 | if (!RoundTrip(expected.get(), &actual_var)) |
| 250 | return false; |
| 251 | ScopedPPVar actual(ScopedPPVar::PassRef(), actual_var); |
raymes@chromium.org | 2c6b74d7 | 2014-03-26 05:04:16 | [diff] [blame] | 252 | return TestEqual(expected.get(), actual.get(), false); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 253 | } |
| 254 | |
punithnayak | 24fe229 | 2023-10-17 12:21:45 | [diff] [blame] | 255 | raw_ptr<v8::Isolate> isolate_; |
marja@chromium.org | 52d6c320 | 2013-06-11 04:28:12 | [diff] [blame] | 256 | |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 257 | // Context for the JavaScript in the test. |
| 258 | v8::Persistent<v8::Context> context_; |
| 259 | |
dcheng | cedca561 | 2016-04-09 01:40:15 | [diff] [blame] | 260 | std::unique_ptr<V8VarConverter> converter_; |
raymes@chromium.org | ba36957a | 2013-08-23 05:26:39 | [diff] [blame] | 261 | |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 262 | private: |
Tomasz Moniuszko | 8fbe5d88 | 2019-09-23 14:49:49 | [diff] [blame] | 263 | // Required to receive callbacks. |
| 264 | base::test::TaskEnvironment task_environment_; |
Etienne Pierre-doray | d952e09 | 2021-11-01 21:56:21 | [diff] [blame] | 265 | gin::IsolateHolder isolate_holder_; |
| 266 | v8::Isolate::Scope isolate_scope_; |
raymes@chromium.org | 4a52cf25 | 2013-08-22 09:29:08 | [diff] [blame] | 267 | |
skyostil | 23490d4 | 2015-06-12 12:54:26 | [diff] [blame] | 268 | TestGlobals globals_; |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 269 | }; |
| 270 | |
| 271 | } // namespace |
| 272 | |
| 273 | TEST_F(V8VarConverterTest, SimpleRoundTripTest) { |
| 274 | EXPECT_TRUE(RoundTripAndCompare(PP_MakeUndefined())); |
| 275 | EXPECT_TRUE(RoundTripAndCompare(PP_MakeNull())); |
| 276 | EXPECT_TRUE(RoundTripAndCompare(PP_MakeInt32(100))); |
| 277 | EXPECT_TRUE(RoundTripAndCompare(PP_MakeBool(PP_TRUE))); |
| 278 | EXPECT_TRUE(RoundTripAndCompare(PP_MakeDouble(53.75))); |
| 279 | } |
| 280 | |
| 281 | TEST_F(V8VarConverterTest, StringRoundTripTest) { |
| 282 | EXPECT_TRUE(RoundTripAndCompare(StringVar::StringToPPVar(""))); |
| 283 | EXPECT_TRUE(RoundTripAndCompare(StringVar::StringToPPVar("hello world!"))); |
| 284 | } |
| 285 | |
| 286 | TEST_F(V8VarConverterTest, ArrayBufferRoundTripTest) { |
| 287 | // TODO(raymes): Testing this here requires spinning up some of WebKit. |
| 288 | // Work out how to do this. |
| 289 | } |
| 290 | |
| 291 | TEST_F(V8VarConverterTest, DictionaryArrayRoundTripTest) { |
| 292 | // Empty array. |
| 293 | scoped_refptr<ArrayVar> array(new ArrayVar); |
| 294 | ScopedPPVar release_array(ScopedPPVar::PassRef(), array->GetPPVar()); |
| 295 | EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); |
| 296 | |
| 297 | size_t index = 0; |
| 298 | |
| 299 | // Array with primitives. |
| 300 | array->Set(index++, PP_MakeUndefined()); |
| 301 | array->Set(index++, PP_MakeNull()); |
| 302 | array->Set(index++, PP_MakeInt32(100)); |
| 303 | array->Set(index++, PP_MakeBool(PP_FALSE)); |
| 304 | array->Set(index++, PP_MakeDouble(0.123)); |
| 305 | EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); |
| 306 | |
| 307 | // Array with 2 references to the same string. |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 308 | ScopedPPVar release_string(ScopedPPVar::PassRef(), |
| 309 | StringVar::StringToPPVar("abc")); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 310 | array->Set(index++, release_string.get()); |
| 311 | array->Set(index++, release_string.get()); |
| 312 | EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); |
| 313 | |
| 314 | // Array with nested array that references the same string. |
| 315 | scoped_refptr<ArrayVar> array2(new ArrayVar); |
| 316 | ScopedPPVar release_array2(ScopedPPVar::PassRef(), array2->GetPPVar()); |
| 317 | array2->Set(0, release_string.get()); |
| 318 | array->Set(index++, release_array2.get()); |
| 319 | EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); |
| 320 | |
| 321 | // Empty dictionary. |
| 322 | scoped_refptr<DictionaryVar> dictionary(new DictionaryVar); |
| 323 | ScopedPPVar release_dictionary(ScopedPPVar::PassRef(), |
| 324 | dictionary->GetPPVar()); |
| 325 | EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); |
| 326 | |
| 327 | // Dictionary with primitives. |
| 328 | dictionary->SetWithStringKey("1", PP_MakeUndefined()); |
| 329 | dictionary->SetWithStringKey("2", PP_MakeNull()); |
| 330 | dictionary->SetWithStringKey("3", PP_MakeInt32(-100)); |
| 331 | dictionary->SetWithStringKey("4", PP_MakeBool(PP_TRUE)); |
| 332 | dictionary->SetWithStringKey("5", PP_MakeDouble(-103.52)); |
| 333 | EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); |
| 334 | |
| 335 | // Dictionary with 2 references to the same string. |
| 336 | dictionary->SetWithStringKey("6", release_string.get()); |
| 337 | dictionary->SetWithStringKey("7", release_string.get()); |
| 338 | EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); |
| 339 | |
| 340 | // Dictionary with nested dictionary that references the same string. |
| 341 | scoped_refptr<DictionaryVar> dictionary2(new DictionaryVar); |
| 342 | ScopedPPVar release_dictionary2(ScopedPPVar::PassRef(), |
| 343 | dictionary2->GetPPVar()); |
| 344 | dictionary2->SetWithStringKey("abc", release_string.get()); |
| 345 | dictionary->SetWithStringKey("8", release_dictionary2.get()); |
| 346 | EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); |
| 347 | |
| 348 | // Array with dictionary. |
| 349 | array->Set(index++, release_dictionary.get()); |
| 350 | EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); |
| 351 | |
| 352 | // Array with dictionary with array. |
| 353 | array2->Set(0, PP_MakeInt32(100)); |
| 354 | dictionary->SetWithStringKey("9", release_array2.get()); |
| 355 | EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 356 | } |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 357 | |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 358 | TEST_F(V8VarConverterTest, Cycles) { |
| 359 | // Check that cycles aren't converted. |
| 360 | v8::HandleScope handle_scope(isolate_); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 361 | v8::Local<v8::Context> context = |
| 362 | v8::Local<v8::Context>::New(isolate_, context_); |
jochen@chromium.org | d3c97d1 | 2013-12-02 12:19:23 | [diff] [blame] | 363 | v8::Context::Scope context_scope(context); |
Camillo Bruni | d0313f7a | 2023-07-03 17:44:52 | [diff] [blame] | 364 | v8::MicrotasksScope microtasks(context, |
Jochen Eisinger | d00cc71 | 2021-04-17 07:23:13 | [diff] [blame] | 365 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 366 | |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 367 | // Var->V8 conversion. |
| 368 | { |
| 369 | scoped_refptr<DictionaryVar> dictionary(new DictionaryVar); |
| 370 | ScopedPPVar release_dictionary(ScopedPPVar::PassRef(), |
| 371 | dictionary->GetPPVar()); |
| 372 | scoped_refptr<ArrayVar> array(new ArrayVar); |
| 373 | ScopedPPVar release_array(ScopedPPVar::PassRef(), array->GetPPVar()); |
| 374 | |
| 375 | dictionary->SetWithStringKey("1", release_array.get()); |
| 376 | array->Set(0, release_dictionary.get()); |
| 377 | |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 378 | v8::Local<v8::Value> v8_result; |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 379 | |
| 380 | // Array <-> dictionary cycle. |
| 381 | dictionary->SetWithStringKey("1", release_array.get()); |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 382 | ASSERT_FALSE( |
| 383 | converter_->ToV8Value(release_dictionary.get(), context, &v8_result)); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 384 | // Break the cycle. |
| 385 | // TODO(raymes): We need some better machinery for releasing vars with |
| 386 | // cycles. Remove the code below once we have that. |
| 387 | dictionary->DeleteWithStringKey("1"); |
| 388 | |
| 389 | // Array with self reference. |
| 390 | array->Set(0, release_array.get()); |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 391 | ASSERT_FALSE( |
| 392 | converter_->ToV8Value(release_array.get(), context, &v8_result)); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 393 | // Break the self reference. |
| 394 | array->Set(0, PP_MakeUndefined()); |
| 395 | } |
| 396 | |
| 397 | // V8->Var conversion. |
| 398 | { |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 399 | v8::Local<v8::Object> object = v8::Object::New(isolate_); |
| 400 | v8::Local<v8::Array> array = v8::Array::New(isolate_); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 401 | |
| 402 | PP_Var var_result; |
| 403 | |
| 404 | // Array <-> dictionary cycle. |
| 405 | std::string key = "1"; |
Ross McIlroy | ceb8e98 | 2018-11-28 11:06:50 | [diff] [blame] | 406 | object |
| 407 | ->Set(context, |
| 408 | v8::String::NewFromUtf8(isolate_, key.c_str(), |
| 409 | v8::NewStringType::kInternalized, |
| 410 | key.length()) |
| 411 | .ToLocalChecked(), |
| 412 | array) |
| 413 | .ToChecked(); |
| 414 | array->Set(context, 0, object).ToChecked(); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 415 | |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 416 | ASSERT_FALSE(FromV8ValueSync(object, context, &var_result)); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 417 | |
| 418 | // Array with self reference. |
Dan Elphick | cc7538d0 | 2019-02-01 13:41:47 | [diff] [blame] | 419 | array->Set(context, 0, array).Check(); |
raymes@chromium.org | 36dcbed | 2013-08-16 05:19:22 | [diff] [blame] | 420 | ASSERT_FALSE(FromV8ValueSync(array, context, &var_result)); |
raymes@chromium.org | b3e3595a9d | 2013-06-19 03:10:36 | [diff] [blame] | 421 | } |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 422 | } |
| 423 | |
| 424 | TEST_F(V8VarConverterTest, StrangeDictionaryKeyTest) { |
| 425 | { |
| 426 | // Test keys with '.'. |
| 427 | scoped_refptr<DictionaryVar> dictionary(new DictionaryVar); |
| 428 | dictionary->SetWithStringKey(".", PP_MakeUndefined()); |
| 429 | dictionary->SetWithStringKey("x.y", PP_MakeUndefined()); |
| 430 | EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); |
| 431 | } |
| 432 | |
| 433 | { |
| 434 | // Test non-string key types. They should be cast to strings. |
marja@chromium.org | 52d6c320 | 2013-06-11 04:28:12 | [diff] [blame] | 435 | v8::HandleScope handle_scope(isolate_); |
jochen@chromium.org | d3c97d1 | 2013-12-02 12:19:23 | [diff] [blame] | 436 | v8::Local<v8::Context> context = |
| 437 | v8::Local<v8::Context>::New(isolate_, context_); |
| 438 | v8::Context::Scope context_scope(context); |
Camillo Bruni | d0313f7a | 2023-07-03 17:44:52 | [diff] [blame] | 439 | v8::MicrotasksScope microtasks(context, |
| 440 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 441 | |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 442 | const char* source = |
| 443 | "(function() {" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 444 | "return {" |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 445 | "1: 'foo'," |
| 446 | "'2': 'bar'," |
| 447 | "true: 'baz'," |
| 448 | "false: 'qux'," |
| 449 | "null: 'quux'," |
| 450 | "undefined: 'oops'" |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 451 | "};" |
| 452 | "})();"; |
| 453 | |
deepak.s | 750d68f | 2015-04-30 07:32:41 | [diff] [blame] | 454 | v8::Local<v8::Script> script( |
Camillo Bruni | e5440c76 | 2020-09-10 11:53:35 | [diff] [blame] | 455 | v8::Script::Compile( |
| 456 | context, v8::String::NewFromUtf8(isolate_, source).ToLocalChecked()) |
Adam Klein | 8a35b5b | 2018-01-17 00:51:00 | [diff] [blame] | 457 | .ToLocalChecked()); |
| 458 | v8::Local<v8::Object> object = |
| 459 | script->Run(context).ToLocalChecked().As<v8::Object>(); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 460 | |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 461 | PP_Var actual; |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 462 | ASSERT_TRUE(FromV8ValueSync( |
| 463 | object, v8::Local<v8::Context>::New(isolate_, context_), &actual)); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 464 | ScopedPPVar release_actual(ScopedPPVar::PassRef(), actual); |
| 465 | |
| 466 | scoped_refptr<DictionaryVar> expected(new DictionaryVar); |
| 467 | ScopedPPVar foo(ScopedPPVar::PassRef(), StringVar::StringToPPVar("foo")); |
| 468 | expected->SetWithStringKey("1", foo.get()); |
| 469 | ScopedPPVar bar(ScopedPPVar::PassRef(), StringVar::StringToPPVar("bar")); |
| 470 | expected->SetWithStringKey("2", bar.get()); |
| 471 | ScopedPPVar baz(ScopedPPVar::PassRef(), StringVar::StringToPPVar("baz")); |
| 472 | expected->SetWithStringKey("true", baz.get()); |
| 473 | ScopedPPVar qux(ScopedPPVar::PassRef(), StringVar::StringToPPVar("qux")); |
| 474 | expected->SetWithStringKey("false", qux.get()); |
| 475 | ScopedPPVar quux(ScopedPPVar::PassRef(), StringVar::StringToPPVar("quux")); |
| 476 | expected->SetWithStringKey("null", quux.get()); |
| 477 | ScopedPPVar oops(ScopedPPVar::PassRef(), StringVar::StringToPPVar("oops")); |
| 478 | expected->SetWithStringKey("undefined", oops.get()); |
dmichael@chromium.org | ad63b5c | 2014-04-11 21:12:36 | [diff] [blame] | 479 | ScopedPPVar release_expected(ScopedPPVar::PassRef(), expected->GetPPVar()); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 480 | |
raymes@chromium.org | 2c6b74d7 | 2014-03-26 05:04:16 | [diff] [blame] | 481 | ASSERT_TRUE(TestEqual(release_expected.get(), release_actual.get(), true)); |
raymes@chromium.org | 7e34610 | 2013-05-16 18:03:57 | [diff] [blame] | 482 | } |
| 483 | } |
| 484 | |
jam@chromium.org | adab233 | 2013-07-25 18:04:32 | [diff] [blame] | 485 | } // namespace content |