| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "extensions/renderer/api_signature.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/memory/ptr_util.h" | |
| 10 #include "base/strings/string_util.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "base/values.h" | |
| 13 #include "content/public/child/v8_value_converter.h" | |
| 14 #include "extensions/renderer/api_invocation_errors.h" | |
| 15 #include "extensions/renderer/argument_spec.h" | |
| 16 #include "gin/arguments.h" | |
| 17 | |
| 18 namespace extensions { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 bool HasCallback(const std::vector<std::unique_ptr<ArgumentSpec>>& signature) { | |
| 23 // TODO(devlin): This is how extension APIs have always determined if a | |
| 24 // function has a callback, but it seems a little silly. In the long run (once | |
| 25 // signatures are generated), it probably makes sense to indicate this | |
| 26 // differently. | |
| 27 return !signature.empty() && | |
| 28 signature.back()->type() == ArgumentType::FUNCTION; | |
| 29 } | |
| 30 | |
| 31 // A class to help with argument parsing. Note that this uses v8::Locals and | |
| 32 // const&s because it's an implementation detail of the APISignature; this | |
| 33 // should *only* be used directly on the stack! | |
| 34 class ArgumentParser { | |
| 35 public: | |
| 36 ArgumentParser(v8::Local<v8::Context> context, | |
| 37 const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | |
| 38 const std::vector<v8::Local<v8::Value>>& arguments, | |
| 39 const APITypeReferenceMap& type_refs, | |
| 40 std::string* error) | |
| 41 : context_(context), | |
| 42 signature_(signature), | |
| 43 arguments_(arguments), | |
| 44 type_refs_(type_refs), | |
| 45 error_(error) {} | |
| 46 | |
| 47 // Tries to parse the arguments against the expected signature. | |
| 48 bool ParseArguments(); | |
| 49 | |
| 50 protected: | |
| 51 v8::Isolate* GetIsolate() { return context_->GetIsolate(); } | |
| 52 | |
| 53 private: | |
| 54 v8::Local<v8::Value> next_argument() { | |
| 55 return current_index_ < arguments_.size() ? | |
| 56 arguments_[current_index_] : v8::Local<v8::Value>(); | |
| 57 } | |
| 58 | |
| 59 void ConsumeArgument() { | |
| 60 current_index_ = std::min(arguments_.size(), current_index_ + 1); | |
| 61 } | |
| 62 | |
| 63 // Attempts to match the next argument to the given |spec|. | |
| 64 // If the next argument does not match and |spec| is optional, uses a null | |
| 65 // value. | |
| 66 // Returns true on success. | |
| 67 bool ParseArgument(const ArgumentSpec& spec); | |
| 68 | |
| 69 // Attempts to parse the callback from the given |spec|. Returns true on | |
| 70 // success. | |
| 71 bool ParseCallback(const ArgumentSpec& spec); | |
| 72 | |
| 73 // Adds a null value to the parsed arguments. | |
| 74 virtual void AddNull() = 0; | |
| 75 virtual void AddNullCallback() = 0; | |
| 76 // Returns a base::Value to be populated during argument matching. | |
| 77 virtual std::unique_ptr<base::Value>* GetBuffer() = 0; | |
| 78 // Adds a new parsed argument. | |
| 79 virtual void AddParsedArgument(v8::Local<v8::Value> value) = 0; | |
| 80 // Adds the parsed callback. | |
| 81 virtual void SetCallback(v8::Local<v8::Function> callback) = 0; | |
| 82 | |
| 83 v8::Local<v8::Context> context_; | |
| 84 const std::vector<std::unique_ptr<ArgumentSpec>>& signature_; | |
| 85 const std::vector<v8::Local<v8::Value>>& arguments_; | |
| 86 const APITypeReferenceMap& type_refs_; | |
| 87 std::string* error_; | |
| 88 size_t current_index_ = 0; | |
| 89 | |
| 90 // An error to pass while parsing arguments to avoid having to allocate a new | |
| 91 // std::string on the stack multiple times. | |
| 92 std::string parse_error_; | |
| 93 | |
| 94 DISALLOW_COPY_AND_ASSIGN(ArgumentParser); | |
| 95 }; | |
| 96 | |
| 97 class V8ArgumentParser : public ArgumentParser { | |
| 98 public: | |
| 99 V8ArgumentParser(v8::Local<v8::Context> context, | |
| 100 const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | |
| 101 const std::vector<v8::Local<v8::Value>>& arguments, | |
| 102 const APITypeReferenceMap& type_refs, | |
| 103 std::string* error, | |
| 104 std::vector<v8::Local<v8::Value>>* values) | |
| 105 : ArgumentParser(context, signature, arguments, type_refs, error), | |
| 106 values_(values) {} | |
| 107 | |
| 108 private: | |
| 109 void AddNull() override { values_->push_back(v8::Null(GetIsolate())); } | |
| 110 void AddNullCallback() override { | |
| 111 values_->push_back(v8::Null(GetIsolate())); | |
| 112 } | |
| 113 std::unique_ptr<base::Value>* GetBuffer() override { return nullptr; } | |
| 114 void AddParsedArgument(v8::Local<v8::Value> value) override { | |
| 115 values_->push_back(value); | |
| 116 } | |
| 117 void SetCallback(v8::Local<v8::Function> callback) override { | |
| 118 values_->push_back(callback); | |
| 119 } | |
| 120 | |
| 121 std::vector<v8::Local<v8::Value>>* values_; | |
| 122 | |
| 123 DISALLOW_COPY_AND_ASSIGN(V8ArgumentParser); | |
| 124 }; | |
| 125 | |
| 126 class BaseValueArgumentParser : public ArgumentParser { | |
| 127 public: | |
| 128 BaseValueArgumentParser( | |
| 129 v8::Local<v8::Context> context, | |
| 130 const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | |
| 131 const std::vector<v8::Local<v8::Value>>& arguments, | |
| 132 const APITypeReferenceMap& type_refs, | |
| 133 std::string* error, | |
| 134 base::ListValue* list_value) | |
| 135 : ArgumentParser(context, signature, arguments, type_refs, error), | |
| 136 list_value_(list_value) {} | |
| 137 | |
| 138 v8::Local<v8::Function> callback() { return callback_; } | |
| 139 | |
| 140 private: | |
| 141 void AddNull() override { | |
| 142 list_value_->Append(base::MakeUnique<base::Value>()); | |
| 143 } | |
| 144 void AddNullCallback() override { | |
| 145 // The base::Value conversion doesn't include the callback directly, so we | |
| 146 // don't add a null parameter here. | |
| 147 } | |
| 148 std::unique_ptr<base::Value>* GetBuffer() override { return &last_arg_; } | |
| 149 void AddParsedArgument(v8::Local<v8::Value> value) override { | |
| 150 // The corresponding base::Value is expected to have been stored in | |
| 151 // |last_arg_| already. | |
| 152 DCHECK(last_arg_); | |
| 153 list_value_->Append(std::move(last_arg_)); | |
| 154 last_arg_.reset(); | |
| 155 } | |
| 156 void SetCallback(v8::Local<v8::Function> callback) override { | |
| 157 callback_ = callback; | |
| 158 } | |
| 159 | |
| 160 base::ListValue* list_value_; | |
| 161 std::unique_ptr<base::Value> last_arg_; | |
| 162 v8::Local<v8::Function> callback_; | |
| 163 | |
| 164 DISALLOW_COPY_AND_ASSIGN(BaseValueArgumentParser); | |
| 165 }; | |
| 166 | |
| 167 bool ArgumentParser::ParseArguments() { | |
| 168 if (arguments_.size() > signature_.size()) { | |
| 169 *error_ = api_errors::TooManyArguments(); | |
| 170 return false; | |
| 171 } | |
| 172 | |
| 173 bool signature_has_callback = HasCallback(signature_); | |
| 174 | |
| 175 size_t end_size = | |
| 176 signature_has_callback ? signature_.size() - 1 : signature_.size(); | |
| 177 for (size_t i = 0; i < end_size; ++i) { | |
| 178 if (!ParseArgument(*signature_[i])) | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 if (signature_has_callback && !ParseCallback(*signature_.back())) | |
| 183 return false; | |
| 184 | |
| 185 if (current_index_ != arguments_.size()) { | |
| 186 // This can potentially happen even if the check above for too many | |
| 187 // arguments succeeds when optional parameters are omitted. For instance, | |
| 188 // if the signature expects (optional int, function callback) and the caller | |
| 189 // provides (function callback, object random), the first size check and | |
| 190 // callback spec would succeed, but we wouldn't consume all the arguments. | |
| 191 *error_ = api_errors::TooManyArguments(); | |
| 192 return false; // Extra arguments aren't allowed. | |
| 193 } | |
| 194 | |
| 195 return true; | |
| 196 } | |
| 197 | |
| 198 bool ArgumentParser::ParseArgument(const ArgumentSpec& spec) { | |
| 199 v8::Local<v8::Value> value = next_argument(); | |
| 200 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { | |
| 201 if (!spec.optional()) { | |
| 202 *error_ = api_errors::MissingRequiredArgument(spec.name().c_str()); | |
| 203 return false; | |
| 204 } | |
| 205 // This is safe to call even if |arguments| is at the end (which can happen | |
| 206 // if n optional arguments are omitted at the end of the signature). | |
| 207 ConsumeArgument(); | |
| 208 | |
| 209 AddNull(); | |
| 210 return true; | |
| 211 } | |
| 212 | |
| 213 if (!spec.ParseArgument(context_, value, type_refs_, GetBuffer(), | |
| 214 &parse_error_)) { | |
| 215 if (!spec.optional()) { | |
| 216 *error_ = api_errors::ArgumentError(spec.name(), parse_error_); | |
| 217 return false; | |
| 218 } | |
| 219 | |
| 220 AddNull(); | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 224 ConsumeArgument(); | |
| 225 AddParsedArgument(value); | |
| 226 return true; | |
| 227 } | |
| 228 | |
| 229 bool ArgumentParser::ParseCallback(const ArgumentSpec& spec) { | |
| 230 v8::Local<v8::Value> value = next_argument(); | |
| 231 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { | |
| 232 if (!spec.optional()) { | |
| 233 *error_ = api_errors::MissingRequiredArgument(spec.name().c_str()); | |
| 234 return false; | |
| 235 } | |
| 236 ConsumeArgument(); | |
| 237 AddNullCallback(); | |
| 238 return true; | |
| 239 } | |
| 240 | |
| 241 if (!spec.ParseArgument(context_, value, type_refs_, nullptr, | |
| 242 &parse_error_)) { | |
| 243 *error_ = api_errors::ArgumentError(spec.name(), parse_error_); | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 ConsumeArgument(); | |
| 248 SetCallback(value.As<v8::Function>()); | |
| 249 return true; | |
| 250 } | |
| 251 | |
| 252 } // namespace | |
| 253 | |
| 254 APISignature::APISignature(const base::ListValue& specification) { | |
| 255 signature_.reserve(specification.GetSize()); | |
| 256 for (const auto& value : specification) { | |
| 257 const base::DictionaryValue* param = nullptr; | |
| 258 CHECK(value.GetAsDictionary(¶m)); | |
| 259 signature_.push_back(base::MakeUnique<ArgumentSpec>(*param)); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 APISignature::APISignature(std::vector<std::unique_ptr<ArgumentSpec>> signature) | |
| 264 : signature_(std::move(signature)) {} | |
| 265 | |
| 266 APISignature::~APISignature() {} | |
| 267 | |
| 268 bool APISignature::ParseArgumentsToV8( | |
| 269 v8::Local<v8::Context> context, | |
| 270 const std::vector<v8::Local<v8::Value>>& arguments, | |
| 271 const APITypeReferenceMap& type_refs, | |
| 272 std::vector<v8::Local<v8::Value>>* v8_out, | |
| 273 std::string* error) const { | |
| 274 DCHECK(v8_out); | |
| 275 std::vector<v8::Local<v8::Value>> v8_values; | |
| 276 V8ArgumentParser parser( | |
| 277 context, signature_, arguments, type_refs, error, &v8_values); | |
| 278 if (!parser.ParseArguments()) | |
| 279 return false; | |
| 280 *v8_out = std::move(v8_values); | |
| 281 return true; | |
| 282 } | |
| 283 | |
| 284 bool APISignature::ParseArgumentsToJSON( | |
| 285 v8::Local<v8::Context> context, | |
| 286 const std::vector<v8::Local<v8::Value>>& arguments, | |
| 287 const APITypeReferenceMap& type_refs, | |
| 288 std::unique_ptr<base::ListValue>* json_out, | |
| 289 v8::Local<v8::Function>* callback_out, | |
| 290 std::string* error) const { | |
| 291 DCHECK(json_out); | |
| 292 DCHECK(callback_out); | |
| 293 std::unique_ptr<base::ListValue> json = base::MakeUnique<base::ListValue>(); | |
| 294 BaseValueArgumentParser parser( | |
| 295 context, signature_, arguments, type_refs, error, json.get()); | |
| 296 if (!parser.ParseArguments()) | |
| 297 return false; | |
| 298 *json_out = std::move(json); | |
| 299 *callback_out = parser.callback(); | |
| 300 return true; | |
| 301 } | |
| 302 | |
| 303 bool APISignature::ConvertArgumentsIgnoringSchema( | |
| 304 v8::Local<v8::Context> context, | |
| 305 const std::vector<v8::Local<v8::Value>>& arguments, | |
| 306 std::unique_ptr<base::ListValue>* json_out, | |
| 307 v8::Local<v8::Function>* callback_out) const { | |
| 308 size_t size = arguments.size(); | |
| 309 v8::Local<v8::Function> callback; | |
| 310 // TODO(devlin): This is what the current bindings do, but it's quite terribly | |
| 311 // incorrect. We only hit this flow when an API method has a hook to update | |
| 312 // the arguments post-validation, and in some cases, the arguments returned by | |
| 313 // that hook do *not* match the signature of the API method (e.g. | |
| 314 // fileSystem.getDisplayPath); see also note in api_bindings.cc for why this | |
| 315 // is bad. But then here, we *rely* on the signature to determine whether or | |
| 316 // not the last parameter is a callback, even though the hooks may not return | |
| 317 // the arguments in the signature. This is very broken. | |
| 318 if (HasCallback(signature_)) { | |
| 319 CHECK(!arguments.empty()); | |
| 320 v8::Local<v8::Value> value = arguments.back(); | |
| 321 --size; | |
| 322 // Bindings should ensure that the value here is appropriate, but see the | |
| 323 // comment above for limitations. | |
| 324 DCHECK(value->IsFunction() || value->IsUndefined() || value->IsNull()); | |
| 325 if (value->IsFunction()) | |
| 326 callback = value.As<v8::Function>(); | |
| 327 } | |
| 328 | |
| 329 auto json = base::MakeUnique<base::ListValue>(); | |
| 330 std::unique_ptr<content::V8ValueConverter> converter = | |
| 331 content::V8ValueConverter::Create(); | |
| 332 converter->SetFunctionAllowed(true); | |
| 333 for (size_t i = 0; i < size; ++i) { | |
| 334 std::unique_ptr<base::Value> converted = | |
| 335 converter->FromV8Value(arguments[i], context); | |
| 336 if (!converted) | |
| 337 return false; | |
| 338 json->Append(std::move(converted)); | |
| 339 } | |
| 340 | |
| 341 *json_out = std::move(json); | |
| 342 *callback_out = callback; | |
| 343 return true; | |
| 344 } | |
| 345 | |
| 346 std::string APISignature::GetExpectedSignature() const { | |
| 347 if (!expected_signature_.empty() || signature_.empty()) | |
| 348 return expected_signature_; | |
| 349 | |
| 350 std::vector<std::string> pieces; | |
| 351 pieces.reserve(signature_.size()); | |
| 352 const char* kOptionalPrefix = "optional "; | |
| 353 for (const auto& spec : signature_) { | |
| 354 pieces.push_back( | |
| 355 base::StringPrintf("%s%s %s", spec->optional() ? kOptionalPrefix : "", | |
| 356 spec->GetTypeName().c_str(), spec->name().c_str())); | |
| 357 } | |
| 358 expected_signature_ = base::JoinString(pieces, ", "); | |
| 359 | |
| 360 return expected_signature_; | |
| 361 } | |
| 362 | |
| 363 } // namespace extensions | |
| OLD | NEW |