Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: extensions/renderer/argument_spec.cc

Issue 2583273002: [Extensions Bindings] Allow for argument validation without conversion (Closed)
Patch Set: lazyboy's Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/renderer/argument_spec.h ('k') | extensions/renderer/argument_spec_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/argument_spec.h" 5 #include "extensions/renderer/argument_spec.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/values.h" 8 #include "base/values.h"
9 #include "content/public/child/v8_value_converter.h" 9 #include "content/public/child/v8_value_converter.h"
10 #include "gin/converter.h" 10 #include "gin/converter.h"
11 #include "gin/dictionary.h" 11 #include "gin/dictionary.h"
12 12
13 namespace extensions { 13 namespace extensions {
14 14
15 namespace { 15 namespace {
16 16
17 template <class T> 17 template <class T>
18 std::unique_ptr<base::Value> GetFundamentalConvertedValueHelper( 18 bool ParseFundamentalValueHelper(v8::Local<v8::Value> arg,
19 v8::Local<v8::Value> arg, 19 v8::Local<v8::Context> context,
20 v8::Local<v8::Context> context, 20 const base::Optional<int>& minimum,
21 const base::Optional<int>& minimum) { 21 std::unique_ptr<base::Value>* out_value) {
22 T val; 22 T val;
23 if (!gin::Converter<T>::FromV8(context->GetIsolate(), arg, &val)) 23 if (!gin::Converter<T>::FromV8(context->GetIsolate(), arg, &val))
24 return nullptr; 24 return false;
25 if (minimum && val < minimum.value()) 25 if (minimum && val < minimum.value())
26 return nullptr; 26 return false;
27 return base::MakeUnique<base::FundamentalValue>(val); 27 if (out_value)
28 *out_value = base::MakeUnique<base::FundamentalValue>(val);
29 return true;
28 } 30 }
29 31
30 } // namespace 32 } // namespace
31 33
32 ArgumentSpec::ArgumentSpec(const base::Value& value) 34 ArgumentSpec::ArgumentSpec(const base::Value& value)
33 : type_(ArgumentType::INTEGER), optional_(false) { 35 : type_(ArgumentType::INTEGER), optional_(false) {
34 const base::DictionaryValue* dict = nullptr; 36 const base::DictionaryValue* dict = nullptr;
35 CHECK(value.GetAsDictionary(&dict)); 37 CHECK(value.GetAsDictionary(&dict));
36 dict->GetBoolean("optional", &optional_); 38 dict->GetBoolean("optional", &optional_);
37 dict->GetString("name", &name_); 39 dict->GetString("name", &name_);
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 CHECK(enum_value_dictionary->GetString("name", &enum_value)); 115 CHECK(enum_value_dictionary->GetString("name", &enum_value));
114 } 116 }
115 enum_values_.insert(std::move(enum_value)); 117 enum_values_.insert(std::move(enum_value));
116 } 118 }
117 } 119 }
118 } 120 }
119 } 121 }
120 122
121 ArgumentSpec::~ArgumentSpec() {} 123 ArgumentSpec::~ArgumentSpec() {}
122 124
123 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgument( 125 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context,
124 v8::Local<v8::Context> context, 126 v8::Local<v8::Value> value,
125 v8::Local<v8::Value> value, 127 const RefMap& refs,
126 const RefMap& refs, 128 std::unique_ptr<base::Value>* out_value,
127 std::string* error) const { 129 std::string* error) const {
128 // TODO(devlin): Support functions? 130 if (type_ == ArgumentType::FUNCTION) {
129 DCHECK_NE(type_, ArgumentType::FUNCTION); 131 // We can't serialize functions. We shouldn't be asked to.
132 DCHECK(!out_value);
133 return value->IsFunction();
134 }
135
130 if (type_ == ArgumentType::REF) { 136 if (type_ == ArgumentType::REF) {
131 DCHECK(ref_); 137 DCHECK(ref_);
132 auto iter = refs.find(ref_.value()); 138 auto iter = refs.find(ref_.value());
133 DCHECK(iter != refs.end()) << ref_.value(); 139 DCHECK(iter != refs.end()) << ref_.value();
134 return iter->second->ConvertArgument(context, value, refs, error); 140 return iter->second->ParseArgument(context, value, refs, out_value, error);
135 } 141 }
136 142
137 if (type_ == ArgumentType::CHOICES) { 143 if (type_ == ArgumentType::CHOICES) {
138 for (const auto& choice : choices_) { 144 for (const auto& choice : choices_) {
139 std::unique_ptr<base::Value> result = 145 if (choice->ParseArgument(context, value, refs, out_value, error))
140 choice->ConvertArgument(context, value, refs, error); 146 return true;
141 if (result)
142 return result;
143 } 147 }
144 *error = "Did not match any of the choices"; 148 *error = "Did not match any of the choices";
145 return nullptr; 149 return false;
146 } 150 }
147 151
148 if (IsFundamentalType()) 152 if (IsFundamentalType())
149 return ConvertArgumentToFundamental(context, value, error); 153 return ParseArgumentToFundamental(context, value, out_value, error);
150 if (type_ == ArgumentType::OBJECT) { 154 if (type_ == ArgumentType::OBJECT) {
151 // TODO(devlin): Currently, this would accept an array (if that array had 155 // TODO(devlin): Currently, this would accept an array (if that array had
152 // all the requisite properties). Is that the right thing to do? 156 // all the requisite properties). Is that the right thing to do?
153 if (!value->IsObject()) { 157 if (!value->IsObject()) {
154 *error = "Wrong type"; 158 *error = "Wrong type";
155 return nullptr; 159 return false;
156 } 160 }
157 v8::Local<v8::Object> object = value.As<v8::Object>(); 161 v8::Local<v8::Object> object = value.As<v8::Object>();
158 return ConvertArgumentToObject(context, object, refs, error); 162 return ParseArgumentToObject(context, object, refs, out_value, error);
159 } 163 }
160 if (type_ == ArgumentType::LIST) { 164 if (type_ == ArgumentType::LIST) {
161 if (!value->IsArray()) { 165 if (!value->IsArray()) {
162 *error = "Wrong type"; 166 *error = "Wrong type";
163 return nullptr; 167 return false;
164 } 168 }
165 v8::Local<v8::Array> array = value.As<v8::Array>(); 169 v8::Local<v8::Array> array = value.As<v8::Array>();
166 return ConvertArgumentToArray(context, array, refs, error); 170 return ParseArgumentToArray(context, array, refs, out_value, error);
167 } 171 }
168 if (type_ == ArgumentType::ANY) 172 if (type_ == ArgumentType::ANY)
169 return ConvertArgumentToAny(context, value, error); 173 return ParseArgumentToAny(context, value, out_value, error);
170 NOTREACHED(); 174 NOTREACHED();
171 return nullptr; 175 return false;
172 } 176 }
173 177
174 bool ArgumentSpec::IsFundamentalType() const { 178 bool ArgumentSpec::IsFundamentalType() const {
175 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE || 179 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE ||
176 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING; 180 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING;
177 } 181 }
178 182
179 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToFundamental( 183 bool ArgumentSpec::ParseArgumentToFundamental(
180 v8::Local<v8::Context> context, 184 v8::Local<v8::Context> context,
181 v8::Local<v8::Value> value, 185 v8::Local<v8::Value> value,
186 std::unique_ptr<base::Value>* out_value,
182 std::string* error) const { 187 std::string* error) const {
183 DCHECK(IsFundamentalType()); 188 DCHECK(IsFundamentalType());
184 switch (type_) { 189 switch (type_) {
185 case ArgumentType::INTEGER: 190 case ArgumentType::INTEGER:
186 return GetFundamentalConvertedValueHelper<int32_t>(value, context, 191 return ParseFundamentalValueHelper<int32_t>(value, context, minimum_,
187 minimum_); 192 out_value);
188 case ArgumentType::DOUBLE: 193 case ArgumentType::DOUBLE:
189 return GetFundamentalConvertedValueHelper<double>(value, context, 194 return ParseFundamentalValueHelper<double>(value, context, minimum_,
190 minimum_); 195 out_value);
191 case ArgumentType::STRING: { 196 case ArgumentType::STRING: {
197 if (!value->IsString())
198 return false;
199 // If we don't need to match enum values and don't need to convert, we're
200 // done...
201 if (!out_value && enum_values_.empty())
202 return true;
203 // ...Otherwise, we need to convert to a std::string.
192 std::string s; 204 std::string s;
193 // TODO(devlin): If base::StringValue ever takes a std::string&&, we could 205 // We already checked that this is a string, so this should never fail.
194 // use std::move to construct. 206 CHECK(gin::Converter<std::string>::FromV8(context->GetIsolate(), value,
195 if (!gin::Converter<std::string>::FromV8(context->GetIsolate(), 207 &s));
196 value, &s) || 208 if (!enum_values_.empty() && enum_values_.count(s) == 0)
197 (!enum_values_.empty() && enum_values_.count(s) == 0)) { 209 return false;
198 return nullptr; 210 if (out_value) {
211 // TODO(devlin): If base::StringValue ever takes a std::string&&, we
212 // could use std::move to construct.
213 *out_value = base::MakeUnique<base::StringValue>(s);
199 } 214 }
200 return base::MakeUnique<base::StringValue>(s); 215 return true;
201 } 216 }
202 case ArgumentType::BOOLEAN: { 217 case ArgumentType::BOOLEAN: {
203 bool b = false; 218 if (!value->IsBoolean())
204 if (value->IsBoolean() && 219 return false;
205 gin::Converter<bool>::FromV8(context->GetIsolate(), value, &b)) { 220 if (out_value) {
206 return base::MakeUnique<base::FundamentalValue>(b); 221 *out_value = base::MakeUnique<base::FundamentalValue>(
222 value.As<v8::Boolean>()->Value());
207 } 223 }
208 return nullptr; 224 return true;
209 } 225 }
210 default: 226 default:
211 NOTREACHED(); 227 NOTREACHED();
212 } 228 }
213 return nullptr; 229 return false;
214 } 230 }
215 231
216 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToObject( 232 bool ArgumentSpec::ParseArgumentToObject(
217 v8::Local<v8::Context> context, 233 v8::Local<v8::Context> context,
218 v8::Local<v8::Object> object, 234 v8::Local<v8::Object> object,
219 const RefMap& refs, 235 const RefMap& refs,
236 std::unique_ptr<base::Value>* out_value,
220 std::string* error) const { 237 std::string* error) const {
221 DCHECK_EQ(ArgumentType::OBJECT, type_); 238 DCHECK_EQ(ArgumentType::OBJECT, type_);
222 auto result = base::MakeUnique<base::DictionaryValue>(); 239 std::unique_ptr<base::DictionaryValue> result;
240 // Only construct the result if we have an |out_value| to populate.
241 if (out_value)
242 result = base::MakeUnique<base::DictionaryValue>();
223 gin::Dictionary dictionary(context->GetIsolate(), object); 243 gin::Dictionary dictionary(context->GetIsolate(), object);
224 for (const auto& kv : properties_) { 244 for (const auto& kv : properties_) {
225 v8::Local<v8::Value> subvalue; 245 v8::Local<v8::Value> subvalue;
226 // See comment in ConvertArgumentToArray() about passing in custom crazy 246 // See comment in ParseArgumentToArray() about passing in custom crazy
227 // values here. 247 // values here.
228 // TODO(devlin): gin::Dictionary::Get() uses Isolate::GetCurrentContext() - 248 // TODO(devlin): gin::Dictionary::Get() uses Isolate::GetCurrentContext() -
229 // is that always right here, or should we use the v8::Object APIs and 249 // is that always right here, or should we use the v8::Object APIs and
230 // pass in |context|? 250 // pass in |context|?
231 // TODO(devlin): Hyper-optimization - Dictionary::Get() also creates a new 251 // TODO(devlin): Hyper-optimization - Dictionary::Get() also creates a new
232 // v8::String for each call. Hypothetically, we could cache these, or at 252 // v8::String for each call. Hypothetically, we could cache these, or at
233 // least use an internalized string. 253 // least use an internalized string.
234 if (!dictionary.Get(kv.first, &subvalue)) 254 if (!dictionary.Get(kv.first, &subvalue))
235 return nullptr; 255 return false;
236 256
237 if (subvalue.IsEmpty() || subvalue->IsNull() || subvalue->IsUndefined()) { 257 if (subvalue.IsEmpty() || subvalue->IsNull() || subvalue->IsUndefined()) {
238 if (!kv.second->optional_) { 258 if (!kv.second->optional_) {
239 *error = "Missing key: " + kv.first; 259 *error = "Missing key: " + kv.first;
240 return nullptr; 260 return false;
241 } 261 }
242 continue; 262 continue;
243 } 263 }
244 std::unique_ptr<base::Value> property = 264 std::unique_ptr<base::Value> property;
245 kv.second->ConvertArgument(context, subvalue, refs, error); 265 if (!kv.second->ParseArgument(context, subvalue, refs,
246 if (!property) 266 out_value ? &property : nullptr, error)) {
247 return nullptr; 267 return false;
248 result->Set(kv.first, std::move(property)); 268 }
269 if (result)
270 result->Set(kv.first, std::move(property));
249 } 271 }
250 return std::move(result); 272 if (out_value)
273 *out_value = std::move(result);
274 return true;
251 } 275 }
252 276
253 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToArray( 277 bool ArgumentSpec::ParseArgumentToArray(v8::Local<v8::Context> context,
254 v8::Local<v8::Context> context, 278 v8::Local<v8::Array> value,
255 v8::Local<v8::Array> value, 279 const RefMap& refs,
256 const RefMap& refs, 280 std::unique_ptr<base::Value>* out_value,
257 std::string* error) const { 281 std::string* error) const {
258 DCHECK_EQ(ArgumentType::LIST, type_); 282 DCHECK_EQ(ArgumentType::LIST, type_);
259 auto result = base::MakeUnique<base::ListValue>(); 283 std::unique_ptr<base::ListValue> result;
284 // Only construct the result if we have an |out_value| to populate.
285 if (out_value)
286 result = base::MakeUnique<base::ListValue>();
260 uint32_t length = value->Length(); 287 uint32_t length = value->Length();
261 for (uint32_t i = 0; i < length; ++i) { 288 for (uint32_t i = 0; i < length; ++i) {
262 v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i); 289 v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i);
263 v8::Local<v8::Value> subvalue; 290 v8::Local<v8::Value> subvalue;
264 // Note: This can fail in the case of a developer passing in the following: 291 // Note: This can fail in the case of a developer passing in the following:
265 // var a = []; 292 // var a = [];
266 // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } }); 293 // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } });
267 // Currently, this will cause the developer-specified error ('foo') to be 294 // Currently, this will cause the developer-specified error ('foo') to be
268 // thrown. 295 // thrown.
269 // TODO(devlin): This is probably fine, but it's worth contemplating 296 // TODO(devlin): This is probably fine, but it's worth contemplating
270 // catching the error and throwing our own. 297 // catching the error and throwing our own.
271 if (!maybe_subvalue.ToLocal(&subvalue)) 298 if (!maybe_subvalue.ToLocal(&subvalue))
272 return nullptr; 299 return false;
273 std::unique_ptr<base::Value> item = 300 std::unique_ptr<base::Value> item;
274 list_element_type_->ConvertArgument(context, subvalue, refs, error); 301 if (!list_element_type_->ParseArgument(context, subvalue, refs,
275 if (!item) 302 result ? &item : nullptr, error)) {
276 return nullptr; 303 return false;
277 result->Append(std::move(item)); 304 }
305 if (result)
306 result->Append(std::move(item));
278 } 307 }
279 return std::move(result); 308 if (out_value)
309 *out_value = std::move(result);
310 return true;
280 } 311 }
281 312
282 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToAny( 313 bool ArgumentSpec::ParseArgumentToAny(v8::Local<v8::Context> context,
283 v8::Local<v8::Context> context, 314 v8::Local<v8::Value> value,
284 v8::Local<v8::Value> value, 315 std::unique_ptr<base::Value>* out_value,
285 std::string* error) const { 316 std::string* error) const {
286 DCHECK_EQ(ArgumentType::ANY, type_); 317 DCHECK_EQ(ArgumentType::ANY, type_);
287 std::unique_ptr<content::V8ValueConverter> converter( 318 if (out_value) {
288 content::V8ValueConverter::create()); 319 std::unique_ptr<content::V8ValueConverter> converter(
289 std::unique_ptr<base::Value> converted( 320 content::V8ValueConverter::create());
290 converter->FromV8Value(value, context)); 321 std::unique_ptr<base::Value> converted(
291 if (!converted) 322 converter->FromV8Value(value, context));
292 *error = "Could not convert to 'any'."; 323 if (!converted) {
293 return converted; 324 *error = "Could not convert to 'any'.";
325 return false;
326 }
327 *out_value = std::move(converted);
328 }
329 return true;
294 } 330 }
295 331
296 } // namespace extensions 332 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/argument_spec.h ('k') | extensions/renderer/argument_spec_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698