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

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

Issue 2583273002: [Extensions Bindings] Allow for argument validation without conversion (Closed)
Patch Set: format Created 4 years 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
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 // TODO(devlin): Support functions?
129 DCHECK_NE(type_, ArgumentType::FUNCTION); 131 DCHECK_NE(type_, ArgumentType::FUNCTION);
130 if (type_ == ArgumentType::REF) { 132 if (type_ == ArgumentType::REF) {
131 DCHECK(ref_); 133 DCHECK(ref_);
132 auto iter = refs.find(ref_.value()); 134 auto iter = refs.find(ref_.value());
133 DCHECK(iter != refs.end()) << ref_.value(); 135 DCHECK(iter != refs.end()) << ref_.value();
134 return iter->second->ConvertArgument(context, value, refs, error); 136 return iter->second->ParseArgument(context, value, refs, out_value, error);
135 } 137 }
136 138
137 if (type_ == ArgumentType::CHOICES) { 139 if (type_ == ArgumentType::CHOICES) {
138 for (const auto& choice : choices_) { 140 for (const auto& choice : choices_) {
139 std::unique_ptr<base::Value> result = 141 if (choice->ParseArgument(context, value, refs, out_value, error))
140 choice->ConvertArgument(context, value, refs, error); 142 return true;
141 if (result)
142 return result;
143 } 143 }
144 *error = "Did not match any of the choices"; 144 *error = "Did not match any of the choices";
145 return nullptr; 145 return false;
146 } 146 }
147 147
148 if (IsFundamentalType()) 148 if (IsFundamentalType())
149 return ConvertArgumentToFundamental(context, value, error); 149 return ParseArgumentToFundamental(context, value, out_value, error);
150 if (type_ == ArgumentType::OBJECT) { 150 if (type_ == ArgumentType::OBJECT) {
151 // TODO(devlin): Currently, this would accept an array (if that array had 151 // TODO(devlin): Currently, this would accept an array (if that array had
152 // all the requisite properties). Is that the right thing to do? 152 // all the requisite properties). Is that the right thing to do?
153 if (!value->IsObject()) { 153 if (!value->IsObject()) {
154 *error = "Wrong type"; 154 *error = "Wrong type";
155 return nullptr; 155 return false;
156 } 156 }
157 v8::Local<v8::Object> object = value.As<v8::Object>(); 157 v8::Local<v8::Object> object = value.As<v8::Object>();
158 return ConvertArgumentToObject(context, object, refs, error); 158 return ParseArgumentToObject(context, object, refs, out_value, error);
159 } 159 }
160 if (type_ == ArgumentType::LIST) { 160 if (type_ == ArgumentType::LIST) {
161 if (!value->IsArray()) { 161 if (!value->IsArray()) {
162 *error = "Wrong type"; 162 *error = "Wrong type";
163 return nullptr; 163 return false;
164 } 164 }
165 v8::Local<v8::Array> array = value.As<v8::Array>(); 165 v8::Local<v8::Array> array = value.As<v8::Array>();
166 return ConvertArgumentToArray(context, array, refs, error); 166 return ParseArgumentToArray(context, array, refs, out_value, error);
167 } 167 }
168 if (type_ == ArgumentType::ANY) 168 if (type_ == ArgumentType::ANY)
169 return ConvertArgumentToAny(context, value, error); 169 return ParseArgumentToAny(context, value, out_value, error);
170 NOTREACHED(); 170 NOTREACHED();
171 return nullptr; 171 return false;
172 } 172 }
173 173
174 bool ArgumentSpec::IsFundamentalType() const { 174 bool ArgumentSpec::IsFundamentalType() const {
175 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE || 175 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE ||
176 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING; 176 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING;
177 } 177 }
178 178
179 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToFundamental( 179 bool ArgumentSpec::ParseArgumentToFundamental(
180 v8::Local<v8::Context> context, 180 v8::Local<v8::Context> context,
181 v8::Local<v8::Value> value, 181 v8::Local<v8::Value> value,
182 std::unique_ptr<base::Value>* out_value,
182 std::string* error) const { 183 std::string* error) const {
183 DCHECK(IsFundamentalType()); 184 DCHECK(IsFundamentalType());
184 switch (type_) { 185 switch (type_) {
185 case ArgumentType::INTEGER: 186 case ArgumentType::INTEGER:
186 return GetFundamentalConvertedValueHelper<int32_t>(value, context, 187 return ParseFundamentalValueHelper<int32_t>(value, context, minimum_,
187 minimum_); 188 out_value);
188 case ArgumentType::DOUBLE: 189 case ArgumentType::DOUBLE:
189 return GetFundamentalConvertedValueHelper<double>(value, context, 190 return ParseFundamentalValueHelper<double>(value, context, minimum_,
190 minimum_); 191 out_value);
191 case ArgumentType::STRING: { 192 case ArgumentType::STRING: {
193 if (!value->IsString())
194 return false;
195 // If we don't need to match enum values and don't need to convert, we're
196 // done...
197 if (!out_value && enum_values_.empty())
198 return true;
199 // ...Otherwise, we need to convert to a std::string.
192 std::string s; 200 std::string s;
193 // TODO(devlin): If base::StringValue ever takes a std::string&&, we could 201 // We already checked that this is a string, so this should never fail.
194 // use std::move to construct. 202 CHECK(gin::Converter<std::string>::FromV8(context->GetIsolate(), value,
195 if (!gin::Converter<std::string>::FromV8(context->GetIsolate(), 203 &s));
196 value, &s) || 204 if (!enum_values_.empty() && enum_values_.count(s) == 0)
197 (!enum_values_.empty() && enum_values_.count(s) == 0)) { 205 return false;
198 return nullptr; 206 if (out_value) {
207 // TODO(devlin): If base::StringValue ever takes a std::string&&, we
208 // could use std::move to construct.
209 *out_value = base::MakeUnique<base::StringValue>(s);
199 } 210 }
200 return base::MakeUnique<base::StringValue>(s); 211 return true;
201 } 212 }
202 case ArgumentType::BOOLEAN: { 213 case ArgumentType::BOOLEAN: {
203 bool b = false; 214 if (!value->IsBoolean())
204 if (value->IsBoolean() && 215 return false;
205 gin::Converter<bool>::FromV8(context->GetIsolate(), value, &b)) { 216 if (out_value) {
206 return base::MakeUnique<base::FundamentalValue>(b); 217 *out_value = base::MakeUnique<base::FundamentalValue>(
218 value.As<v8::Boolean>()->Value());
207 } 219 }
208 return nullptr; 220 return true;
209 } 221 }
210 default: 222 default:
211 NOTREACHED(); 223 NOTREACHED();
212 } 224 }
213 return nullptr; 225 return false;
214 } 226 }
215 227
216 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToObject( 228 bool ArgumentSpec::ParseArgumentToObject(
217 v8::Local<v8::Context> context, 229 v8::Local<v8::Context> context,
218 v8::Local<v8::Object> object, 230 v8::Local<v8::Object> object,
219 const RefMap& refs, 231 const RefMap& refs,
232 std::unique_ptr<base::Value>* out_value,
220 std::string* error) const { 233 std::string* error) const {
221 DCHECK_EQ(ArgumentType::OBJECT, type_); 234 DCHECK_EQ(ArgumentType::OBJECT, type_);
222 auto result = base::MakeUnique<base::DictionaryValue>(); 235 std::unique_ptr<base::DictionaryValue> result;
236 // Only construct the result if we have an |out_value| to populate.
237 if (out_value)
238 result = base::MakeUnique<base::DictionaryValue>();
223 gin::Dictionary dictionary(context->GetIsolate(), object); 239 gin::Dictionary dictionary(context->GetIsolate(), object);
224 for (const auto& kv : properties_) { 240 for (const auto& kv : properties_) {
225 v8::Local<v8::Value> subvalue; 241 v8::Local<v8::Value> subvalue;
226 // See comment in ConvertArgumentToArray() about passing in custom crazy 242 // See comment in ParseArgumentToArray() about passing in custom crazy
227 // values here. 243 // values here.
228 // TODO(devlin): gin::Dictionary::Get() uses Isolate::GetCurrentContext() - 244 // TODO(devlin): gin::Dictionary::Get() uses Isolate::GetCurrentContext() -
229 // is that always right here, or should we use the v8::Object APIs and 245 // is that always right here, or should we use the v8::Object APIs and
230 // pass in |context|? 246 // pass in |context|?
231 // TODO(devlin): Hyper-optimization - Dictionary::Get() also creates a new 247 // TODO(devlin): Hyper-optimization - Dictionary::Get() also creates a new
232 // v8::String for each call. Hypothetically, we could cache these, or at 248 // v8::String for each call. Hypothetically, we could cache these, or at
233 // least use an internalized string. 249 // least use an internalized string.
234 if (!dictionary.Get(kv.first, &subvalue)) 250 if (!dictionary.Get(kv.first, &subvalue))
235 return nullptr; 251 return false;
236 252
237 if (subvalue.IsEmpty() || subvalue->IsNull() || subvalue->IsUndefined()) { 253 if (subvalue.IsEmpty() || subvalue->IsNull() || subvalue->IsUndefined()) {
238 if (!kv.second->optional_) { 254 if (!kv.second->optional_) {
239 *error = "Missing key: " + kv.first; 255 *error = "Missing key: " + kv.first;
240 return nullptr; 256 return false;
241 } 257 }
242 continue; 258 continue;
243 } 259 }
244 std::unique_ptr<base::Value> property = 260 std::unique_ptr<base::Value> property;
245 kv.second->ConvertArgument(context, subvalue, refs, error); 261 if (!kv.second->ParseArgument(context, subvalue, refs,
246 if (!property) 262 out_value ? &property : nullptr, error)) {
247 return nullptr; 263 return false;
248 result->Set(kv.first, std::move(property)); 264 }
265 if (result)
266 result->Set(kv.first, std::move(property));
249 } 267 }
250 return std::move(result); 268 if (out_value)
269 *out_value = std::move(result);
270 return true;
251 } 271 }
252 272
253 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToArray( 273 bool ArgumentSpec::ParseArgumentToArray(v8::Local<v8::Context> context,
254 v8::Local<v8::Context> context, 274 v8::Local<v8::Array> value,
255 v8::Local<v8::Array> value, 275 const RefMap& refs,
256 const RefMap& refs, 276 std::unique_ptr<base::Value>* out_value,
257 std::string* error) const { 277 std::string* error) const {
258 DCHECK_EQ(ArgumentType::LIST, type_); 278 DCHECK_EQ(ArgumentType::LIST, type_);
259 auto result = base::MakeUnique<base::ListValue>(); 279 std::unique_ptr<base::ListValue> result;
280 // Only construct the result if we have an |out_value| to populate.
281 if (out_value)
282 result = base::MakeUnique<base::ListValue>();
260 uint32_t length = value->Length(); 283 uint32_t length = value->Length();
261 for (uint32_t i = 0; i < length; ++i) { 284 for (uint32_t i = 0; i < length; ++i) {
262 v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i); 285 v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i);
263 v8::Local<v8::Value> subvalue; 286 v8::Local<v8::Value> subvalue;
264 // Note: This can fail in the case of a developer passing in the following: 287 // Note: This can fail in the case of a developer passing in the following:
265 // var a = []; 288 // var a = [];
266 // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } }); 289 // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } });
267 // Currently, this will cause the developer-specified error ('foo') to be 290 // Currently, this will cause the developer-specified error ('foo') to be
268 // thrown. 291 // thrown.
269 // TODO(devlin): This is probably fine, but it's worth contemplating 292 // TODO(devlin): This is probably fine, but it's worth contemplating
270 // catching the error and throwing our own. 293 // catching the error and throwing our own.
271 if (!maybe_subvalue.ToLocal(&subvalue)) 294 if (!maybe_subvalue.ToLocal(&subvalue))
272 return nullptr; 295 return false;
273 std::unique_ptr<base::Value> item = 296 std::unique_ptr<base::Value> item;
274 list_element_type_->ConvertArgument(context, subvalue, refs, error); 297 if (!list_element_type_->ParseArgument(context, subvalue, refs,
275 if (!item) 298 result ? &item : nullptr, error)) {
276 return nullptr; 299 return false;
277 result->Append(std::move(item)); 300 }
301 if (result)
302 result->Append(std::move(item));
278 } 303 }
279 return std::move(result); 304 if (out_value)
305 *out_value = std::move(result);
306 return true;
280 } 307 }
281 308
282 std::unique_ptr<base::Value> ArgumentSpec::ConvertArgumentToAny( 309 bool ArgumentSpec::ParseArgumentToAny(v8::Local<v8::Context> context,
283 v8::Local<v8::Context> context, 310 v8::Local<v8::Value> value,
284 v8::Local<v8::Value> value, 311 std::unique_ptr<base::Value>* out_value,
285 std::string* error) const { 312 std::string* error) const {
286 DCHECK_EQ(ArgumentType::ANY, type_); 313 DCHECK_EQ(ArgumentType::ANY, type_);
287 std::unique_ptr<content::V8ValueConverter> converter( 314 if (out_value) {
288 content::V8ValueConverter::create()); 315 std::unique_ptr<content::V8ValueConverter> converter(
289 std::unique_ptr<base::Value> converted( 316 content::V8ValueConverter::create());
290 converter->FromV8Value(value, context)); 317 std::unique_ptr<base::Value> converted(
291 if (!converted) 318 converter->FromV8Value(value, context));
292 *error = "Could not convert to 'any'."; 319 if (!converted) {
293 return converted; 320 *error = "Could not convert to 'any'.";
321 return false;
322 }
323 *out_value = std::move(converted);
324 }
325 return true;
294 } 326 }
295 327
296 } // namespace extensions 328 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698