| // Copyright 2016 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "extensions/renderer/bindings/api_signature.h" | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "base/containers/contains.h" | 
 | #include "base/memory/raw_ref.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/values.h" | 
 | #include "content/public/renderer/v8_value_converter.h" | 
 | #include "extensions/renderer/bindings/api_binding_util.h" | 
 | #include "extensions/renderer/bindings/api_invocation_errors.h" | 
 | #include "extensions/renderer/bindings/argument_spec.h" | 
 | #include "gin/arguments.h" | 
 |  | 
 | namespace extensions { | 
 |  | 
 | namespace { | 
 |  | 
 | std::vector<std::unique_ptr<ArgumentSpec>> ValueListToArgumentSpecs( | 
 |     const base::Value::List& specification_list, | 
 |     bool uses_returns_async) { | 
 |   std::vector<std::unique_ptr<ArgumentSpec>> signatures; | 
 |   auto size = specification_list.size(); | 
 |   // If the API specification uses the returns_async format we will be pushing a | 
 |   // callback onto the end of the argument spec list during the call to the ctor | 
 |   // later, so we make room for it now when we reserve the size. | 
 |   if (uses_returns_async) | 
 |     size++; | 
 |   signatures.reserve(size); | 
 |   for (const auto& signature : specification_list) { | 
 |     signatures.push_back(std::make_unique<ArgumentSpec>(signature.GetDict())); | 
 |   } | 
 |  | 
 |   return signatures; | 
 | } | 
 |  | 
 | std::unique_ptr<APISignature::ReturnsAsync> BuildReturnsAsyncFromValues( | 
 |     const base::Value::Dict& returns_async_spec) { | 
 |   auto returns_async = std::make_unique<APISignature::ReturnsAsync>(); | 
 |  | 
 |   returns_async->promise_support = | 
 |       returns_async_spec.Find("does_not_support_promises") | 
 |           ? binding::APIPromiseSupport::kUnsupported | 
 |           : binding::APIPromiseSupport::kSupported; | 
 |   std::optional<bool> callback_optional = | 
 |       returns_async_spec.FindBool("optional"); | 
 |   returns_async->optional = callback_optional.value_or(false); | 
 |  | 
 |   // If response validation is enabled, parse the callback signature. Otherwise, | 
 |   // there's no reason to, so don't bother. | 
 |   if (binding::IsResponseValidationEnabled()) { | 
 |     const base::Value::List* callback_params = | 
 |         returns_async_spec.FindList("parameters"); | 
 |     if (callback_params) { | 
 |       returns_async->signature = | 
 |           ValueListToArgumentSpecs(*callback_params, false); | 
 |     } | 
 |   } | 
 |   return returns_async; | 
 | } | 
 |  | 
 | std::string ArgumentSpecsToString( | 
 |     const std::vector<std::unique_ptr<ArgumentSpec>>& argument_specs) { | 
 |   std::vector<std::string> pieces; | 
 |   pieces.reserve(argument_specs.size()); | 
 |   const char* kOptionalPrefix = "optional "; | 
 |   for (const auto& spec : argument_specs) { | 
 |     pieces.push_back( | 
 |         base::StringPrintf("%s%s %s", spec->optional() ? kOptionalPrefix : "", | 
 |                            spec->GetTypeName().c_str(), spec->name().c_str())); | 
 |   } | 
 |   return base::JoinString(pieces, ", "); | 
 | } | 
 |  | 
 | // A class to help with argument parsing. Note that this uses v8::Locals and | 
 | // const&s because it's an implementation detail of the APISignature; this | 
 | // should *only* be used directly on the stack! | 
 | class ArgumentParser { | 
 |  public: | 
 |   ArgumentParser(v8::Local<v8::Context> context, | 
 |                  const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | 
 |                  const v8::LocalVector<v8::Value>& arguments, | 
 |                  const APITypeReferenceMap& type_refs, | 
 |                  PromisesAllowed promises_allowed) | 
 |       : context_(context), | 
 |         signature_(signature), | 
 |         provided_arguments_(arguments), | 
 |         type_refs_(type_refs), | 
 |         promises_allowed_(promises_allowed) {} | 
 |  | 
 |   ArgumentParser(const ArgumentParser&) = delete; | 
 |   ArgumentParser& operator=(const ArgumentParser&) = delete; | 
 |  | 
 |  protected: | 
 |   v8::Isolate* GetIsolate() { return v8::Isolate::GetCurrent(); } | 
 |  | 
 |   // Common implementation for parsing arguments to either V8 values or | 
 |   // base::Values. | 
 |   bool ParseArgumentsImpl(bool signature_has_callback); | 
 |  | 
 |   std::string TakeError() { return std::move(error_); } | 
 |   binding::AsyncResponseType async_type() const { return async_type_; } | 
 |  | 
 |  private: | 
 |   // API methods can have multiple possible signatures. For instance, an API | 
 |   // method that takes (optional int, string) could be invoked with either | 
 |   // an int and string, or just a string. ResolveArguments() takes the | 
 |   // |provided| arguments and the |expected| signature, and populates |result| | 
 |   // with a normalized array of values such that each entry in |result| is | 
 |   // positionally correct with the signature. Omitted arguments will be | 
 |   // empty v8::Local<v8::Value> handles in the array. | 
 |   // |allow_omitted_final_argument| indicates that the final argument is allowed | 
 |   // to be omitted, even if it is not flagged as optional. This is used to allow | 
 |   // callers to omit the final "callback" argument if promises can be used | 
 |   // instead. | 
 |   // Returns true if the arguments were successfully resolved. | 
 |   // Note: This only checks arguments against their basic types, not other | 
 |   // values (like specific required properties or values). | 
 |   bool ResolveArguments( | 
 |       base::span<const v8::Local<v8::Value>> provided, | 
 |       base::span<const std::unique_ptr<ArgumentSpec>> expected, | 
 |       v8::LocalVector<v8::Value>* result, | 
 |       size_t index, | 
 |       bool allow_omitted_final_argument); | 
 |  | 
 |   // Attempts to match the next argument to the given |spec|. | 
 |   // If the next argument does not match and |spec| is optional, uses a null | 
 |   // value. | 
 |   // Returns true on success. | 
 |   bool ParseArgument(const ArgumentSpec& spec, v8::Local<v8::Value> value); | 
 |  | 
 |   // Attempts to parse the callback from the given |spec|. Returns true on | 
 |   // success. | 
 |   bool ParseCallback(const ArgumentSpec& spec, v8::Local<v8::Value> value); | 
 |  | 
 |   // Adds a null value to the parsed arguments. | 
 |   virtual void AddNull() = 0; | 
 |   virtual void AddNullCallback() = 0; | 
 |   // Returns a base::Value to be populated during argument matching. | 
 |   virtual std::unique_ptr<base::Value>* GetBaseBuffer() = 0; | 
 |   // Returns a v8::Value to be populated during argument matching. | 
 |   virtual v8::Local<v8::Value>* GetV8Buffer() = 0; | 
 |   // Adds the argument parsed into the appropriate buffer. | 
 |   virtual void AddParsedArgument() = 0; | 
 |   // Adds the parsed callback. | 
 |   virtual void SetCallback(v8::Local<v8::Function> callback) = 0; | 
 |  | 
 |   v8::Local<v8::Context> context_; | 
 |   const raw_ref<const std::vector<std::unique_ptr<ArgumentSpec>>> signature_; | 
 |   const raw_ref<const v8::LocalVector<v8::Value>> provided_arguments_; | 
 |   const raw_ref<const APITypeReferenceMap> type_refs_; | 
 |   PromisesAllowed promises_allowed_; | 
 |   binding::AsyncResponseType async_type_ = binding::AsyncResponseType::kNone; | 
 |   std::string error_; | 
 |  | 
 |   // An error to pass while parsing arguments to avoid having to allocate a new | 
 |   // std::string on the stack multiple times. | 
 |   std::string parse_error_; | 
 | }; | 
 |  | 
 | class V8ArgumentParser : public ArgumentParser { | 
 |  public: | 
 |   V8ArgumentParser(v8::Local<v8::Context> context, | 
 |                    const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | 
 |                    const v8::LocalVector<v8::Value>& arguments, | 
 |                    const APITypeReferenceMap& type_refs, | 
 |                    PromisesAllowed promises_allowed) | 
 |       : ArgumentParser(context, | 
 |                        signature, | 
 |                        arguments, | 
 |                        type_refs, | 
 |                        promises_allowed), | 
 |         values_(v8::Isolate::GetCurrent()) {} | 
 |  | 
 |   V8ArgumentParser(const V8ArgumentParser&) = delete; | 
 |   V8ArgumentParser& operator=(const V8ArgumentParser&) = delete; | 
 |  | 
 |   APISignature::V8ParseResult ParseArguments(bool signature_has_callback); | 
 |  | 
 |  private: | 
 |   void AddNull() override { values_.push_back(v8::Null(GetIsolate())); } | 
 |   void AddNullCallback() override { values_.push_back(v8::Null(GetIsolate())); } | 
 |   std::unique_ptr<base::Value>* GetBaseBuffer() override { return nullptr; } | 
 |   v8::Local<v8::Value>* GetV8Buffer() override { return &last_arg_; } | 
 |   void AddParsedArgument() override { | 
 |     DCHECK(!last_arg_.IsEmpty()); | 
 |     values_.push_back(last_arg_); | 
 |     last_arg_.Clear(); | 
 |   } | 
 |   void SetCallback(v8::Local<v8::Function> callback) override { | 
 |     values_.push_back(callback); | 
 |   } | 
 |  | 
 |   v8::Local<v8::Value> last_arg_; | 
 |   v8::LocalVector<v8::Value> values_; | 
 | }; | 
 |  | 
 | class BaseValueArgumentParser : public ArgumentParser { | 
 |  public: | 
 |   BaseValueArgumentParser( | 
 |       v8::Local<v8::Context> context, | 
 |       const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | 
 |       const v8::LocalVector<v8::Value>& arguments, | 
 |       const APITypeReferenceMap& type_refs, | 
 |       PromisesAllowed promises_allowed) | 
 |       : ArgumentParser(context, | 
 |                        signature, | 
 |                        arguments, | 
 |                        type_refs, | 
 |                        promises_allowed) {} | 
 |  | 
 |   BaseValueArgumentParser(const BaseValueArgumentParser&) = delete; | 
 |   BaseValueArgumentParser& operator=(const BaseValueArgumentParser&) = delete; | 
 |  | 
 |   APISignature::JSONParseResult ParseArguments(bool signature_has_callback); | 
 |  | 
 |  private: | 
 |   void AddNull() override { list_value_.Append(base::Value()); } | 
 |   void AddNullCallback() override { | 
 |     // The base::Value conversion doesn't include the callback directly, so we | 
 |     // don't add a null parameter here. | 
 |   } | 
 |   std::unique_ptr<base::Value>* GetBaseBuffer() override { return &last_arg_; } | 
 |   v8::Local<v8::Value>* GetV8Buffer() override { return nullptr; } | 
 |   void AddParsedArgument() override { | 
 |     // The corresponding base::Value is expected to have been stored in | 
 |     // |last_arg_| already. | 
 |     DCHECK(last_arg_); | 
 |     list_value_.Append(base::Value::FromUniquePtrValue(std::move(last_arg_))); | 
 |   } | 
 |   void SetCallback(v8::Local<v8::Function> callback) override { | 
 |     callback_ = callback; | 
 |   } | 
 |  | 
 |   base::Value::List list_value_; | 
 |   std::unique_ptr<base::Value> last_arg_; | 
 |   v8::Local<v8::Function> callback_; | 
 | }; | 
 |  | 
 | bool ArgumentParser::ParseArgumentsImpl(bool signature_has_callback) { | 
 |   if (provided_arguments_->size() > signature_->size()) { | 
 |     error_ = api_errors::NoMatchingSignature(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // We allow the final argument to be omitted if the signature expects a | 
 |   // callback and promise-based APIs are supported. If the caller omits this | 
 |   // callback, the invocation is assumed to expect to a promise. | 
 |   bool allow_omitted_final_argument = | 
 |       signature_has_callback && promises_allowed_ == PromisesAllowed::kAllowed; | 
 |  | 
 |   v8::LocalVector<v8::Value> resolved_arguments(v8::Isolate::GetCurrent(), | 
 |                                                 signature_->size()); | 
 |   if (!ResolveArguments(*provided_arguments_, *signature_, &resolved_arguments, | 
 |                         0u, allow_omitted_final_argument)) { | 
 |     error_ = api_errors::NoMatchingSignature(); | 
 |     return false; | 
 |   } | 
 |   DCHECK_EQ(resolved_arguments.size(), signature_->size()); | 
 |  | 
 |   size_t end_size = | 
 |       signature_has_callback ? signature_->size() - 1 : signature_->size(); | 
 |   for (size_t i = 0; i < end_size; ++i) { | 
 |     if (!ParseArgument(*(*signature_)[i], resolved_arguments[i])) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   if (signature_has_callback && | 
 |       !ParseCallback(*signature_->back(), resolved_arguments.back())) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool ArgumentParser::ResolveArguments( | 
 |     base::span<const v8::Local<v8::Value>> provided, | 
 |     base::span<const std::unique_ptr<ArgumentSpec>> expected, | 
 |     v8::LocalVector<v8::Value>* result, | 
 |     size_t index, | 
 |     bool allow_omitted_final_argument) { | 
 |   // If the provided arguments and expected arguments are both empty, it means | 
 |   // we've successfully matched all provided arguments to the expected | 
 |   // signature. | 
 |   if (provided.empty() && expected.empty()) | 
 |     return true; | 
 |  | 
 |   // If there are more provided arguments than expected arguments, there's no | 
 |   // possible signature that could match. | 
 |   if (provided.size() > expected.size()) | 
 |     return false; | 
 |  | 
 |   // If there are more provided arguments (and more expected arguments, as | 
 |   // guaranteed above), check if the next argument could match the next expected | 
 |   // argument. | 
 |   if (!provided.empty()) { | 
 |     // The argument could potentially match if it is either null or undefined | 
 |     // and an optional argument, or if it's the correct expected type. | 
 |     bool can_match = false; | 
 |     if (expected[0]->optional() && provided[0]->IsNullOrUndefined()) { | 
 |       can_match = true; | 
 |       // For null/undefined, just use an empty handle. It'll be normalized to | 
 |       // null in ParseArgument(). | 
 |       (*result)[index] = v8::Local<v8::Value>(); | 
 |     } else if (expected[0]->IsCorrectType(provided[0], *type_refs_, &error_)) { | 
 |       can_match = true; | 
 |       (*result)[index] = provided[0]; | 
 |     } | 
 |  | 
 |     // If the provided argument could potentially match the next expected | 
 |     // argument, assume it does, and try to match the remaining arguments. | 
 |     // This recursion is safe because it's bounded by the number of arguments | 
 |     // present in the signature. Additionally, though this is 2^n complexity, | 
 |     // <n> is bounded by the number of expected arguments, which is almost | 
 |     // always small. Further, it is only when parameters are optional, which is | 
 |     // also not the default. | 
 |     if (can_match && | 
 |         ResolveArguments(provided.subspan<1>(), expected.subspan<1>(), result, | 
 |                          index + 1, allow_omitted_final_argument)) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   // One of three cases happened: | 
 |   // - There are no more provided arguments. | 
 |   // - The next provided argument could not match the expected argument. | 
 |   // - The next provided argument could match the expected argument, but | 
 |   //   subsequent arguments did not. | 
 |   // In all of these cases, if the expected argument was optional, assume it | 
 |   // was omitted, and try matching subsequent arguments. | 
 |   if (expected[0]->optional()) { | 
 |     // Assume the expected argument was omitted. | 
 |     (*result)[index] = v8::Local<v8::Value>(); | 
 |     // See comments above for recursion notes. | 
 |     if (ResolveArguments(provided, expected.subspan<1>(), result, index + 1, | 
 |                          allow_omitted_final_argument)) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   // A required argument was not matched. There is only one case in which this | 
 |   // is allowed: a required callback has been left off of the provided arguments | 
 |   // when Promises are supported; if this is the case, | 
 |   // |allow_omitted_final_argument| is true and there should be no provided | 
 |   // arguments left. | 
 |   if (allow_omitted_final_argument && provided.size() == 0 && | 
 |       expected.size() == 1) { | 
 |     (*result)[index] = v8::Local<v8::Value>(); | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | bool ArgumentParser::ParseArgument(const ArgumentSpec& spec, | 
 |                                    v8::Local<v8::Value> value) { | 
 |   if (value.IsEmpty()) { | 
 |     // ResolveArguments() should only allow empty values for optional arguments. | 
 |     DCHECK(spec.optional()); | 
 |     AddNull(); | 
 |     return true; | 
 |   } | 
 |  | 
 |   // ResolveArguments() should verify that all arguments are at least the | 
 |   // correct type. | 
 |   DCHECK(spec.IsCorrectType(value, *type_refs_, &error_)); | 
 |   if (!spec.ParseArgument(context_, value, *type_refs_, GetBaseBuffer(), | 
 |                           GetV8Buffer(), &parse_error_)) { | 
 |     error_ = api_errors::ArgumentError(spec.name(), parse_error_); | 
 |     return false; | 
 |   } | 
 |  | 
 |   AddParsedArgument(); | 
 |   return true; | 
 | } | 
 |  | 
 | bool ArgumentParser::ParseCallback(const ArgumentSpec& spec, | 
 |                                    v8::Local<v8::Value> value) { | 
 |   if (value.IsEmpty()) { | 
 |     // Note: The null callback isn't exactly correct. See | 
 |     // https://crbug.com/1220910 for details. | 
 |     AddNullCallback(); | 
 |  | 
 |     if (promises_allowed_ == PromisesAllowed::kAllowed) { | 
 |       // If the callback is omitted and promises are supported, assume the | 
 |       // async response type is a promise. | 
 |       async_type_ = binding::AsyncResponseType::kPromise; | 
 |     } else { | 
 |       // Otherwise, we should only get to this point if the callback argument is | 
 |       // optional. | 
 |       DCHECK(spec.optional()); | 
 |       async_type_ = binding::AsyncResponseType::kNone; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   // Note: callbacks are set through SetCallback() rather than through the | 
 |   // buffered argument. | 
 |   if (!spec.ParseArgument(context_, value, *type_refs_, nullptr, nullptr, | 
 |                           &parse_error_)) { | 
 |     error_ = api_errors::ArgumentError(spec.name(), parse_error_); | 
 |     return false; | 
 |   } | 
 |  | 
 |   SetCallback(value.As<v8::Function>()); | 
 |   async_type_ = binding::AsyncResponseType::kCallback; | 
 |   return true; | 
 | } | 
 |  | 
 | APISignature::V8ParseResult V8ArgumentParser::ParseArguments( | 
 |     bool signature_has_callback) { | 
 |   APISignature::V8ParseResult result; | 
 |   if (!ParseArgumentsImpl(signature_has_callback)) { | 
 |     result.error = TakeError(); | 
 |   } else { | 
 |     result.arguments = std::move(values_); | 
 |     result.async_type = async_type(); | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | APISignature::JSONParseResult BaseValueArgumentParser::ParseArguments( | 
 |     bool signature_has_callback) { | 
 |   APISignature::JSONParseResult result; | 
 |   if (!ParseArgumentsImpl(signature_has_callback)) { | 
 |     result.error = TakeError(); | 
 |   } else { | 
 |     result.arguments_list = std::move(list_value_); | 
 |     result.callback = callback_; | 
 |     result.async_type = async_type(); | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | // A helper method used to validate a signature for an internal caller (such as | 
 | // a response to an API method or event arguments) to ensure it matches the | 
 | // expected schema. | 
 | bool ValidateSignatureForInternalCaller( | 
 |     v8::Local<v8::Context> context, | 
 |     const v8::LocalVector<v8::Value>& arguments, | 
 |     const std::vector<std::unique_ptr<ArgumentSpec>>& expected, | 
 |     const APITypeReferenceMap& type_refs, | 
 |     std::string* error) { | 
 |   size_t expected_size = expected.size(); | 
 |   size_t actual_size = arguments.size(); | 
 |   if (actual_size > expected_size) { | 
 |     *error = api_errors::TooManyArguments(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Easy validation: arguments go in order, and must match the expected schema. | 
 |   // Anything less is failure. | 
 |   std::string parse_error; | 
 |   for (size_t i = 0; i < actual_size; ++i) { | 
 |     DCHECK(!arguments[i].IsEmpty()); | 
 |     const ArgumentSpec& spec = *expected[i]; | 
 |     if (arguments[i]->IsNullOrUndefined()) { | 
 |       if (!spec.optional()) { | 
 |         *error = api_errors::MissingRequiredArgument(spec.name().c_str()); | 
 |         return false; | 
 |       } | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (!spec.ParseArgument(context, arguments[i], type_refs, nullptr, nullptr, | 
 |                             &parse_error)) { | 
 |       *error = api_errors::ArgumentError(spec.name(), parse_error); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   // Responses may omit trailing optional parameters (which would then be | 
 |   // undefined for the caller). | 
 |   // NOTE(devlin): It might be nice to see if we could require all arguments to | 
 |   // be present, no matter what. For one, it avoids this loop, and it would also | 
 |   // unify what a "not found" value was (some APIs use undefined, some use | 
 |   // null). | 
 |   for (size_t i = actual_size; i < expected_size; ++i) { | 
 |     if (!expected[i]->optional()) { | 
 |       *error = api_errors::MissingRequiredArgument(expected[i]->name().c_str()); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | APISignature::ReturnsAsync::ReturnsAsync() = default; | 
 | APISignature::ReturnsAsync::~ReturnsAsync() = default; | 
 |  | 
 | APISignature::V8ParseResult::V8ParseResult() = default; | 
 | APISignature::V8ParseResult::~V8ParseResult() = default; | 
 | APISignature::V8ParseResult::V8ParseResult(V8ParseResult&& other) = default; | 
 | APISignature::V8ParseResult& APISignature::V8ParseResult::operator=( | 
 |     V8ParseResult&& other) = default; | 
 |  | 
 | APISignature::JSONParseResult::JSONParseResult() = default; | 
 | APISignature::JSONParseResult::~JSONParseResult() = default; | 
 | APISignature::JSONParseResult::JSONParseResult(JSONParseResult&& other) = | 
 |     default; | 
 | APISignature::JSONParseResult& APISignature::JSONParseResult::operator=( | 
 |     JSONParseResult&& other) = default; | 
 |  | 
 | APISignature::APISignature( | 
 |     std::vector<std::unique_ptr<ArgumentSpec>> signature, | 
 |     std::unique_ptr<APISignature::ReturnsAsync> returns_async, | 
 |     BindingAccessChecker* access_checker) | 
 |     : signature_(std::move(signature)), | 
 |       returns_async_(std::move(returns_async)), | 
 |       access_checker_(access_checker) { | 
 |   if (returns_async_) { | 
 |     // TODO(tjudkins): Argument parsing during an API call currently expects any | 
 |     // potential callback to be part of the list of expected arguments, rather | 
 |     // than represented separately in the ReturnsAsync struct. It would be nice | 
 |     // to update that code to know about the ReturnsAsync struct instead. | 
 |     // That would also avoid the slightly-inefficient "dance" we have for APIs | 
 |     // that don't specify returns_async today, since we currently pop the | 
 |     // callback in CreateFromValues() and then re-add it here. | 
 |     auto callback = std::make_unique<ArgumentSpec>(ArgumentType::FUNCTION); | 
 |     callback->set_optional(returns_async_->optional); | 
 |     callback->set_name("callback"); | 
 |     signature_.push_back(std::move(callback)); | 
 |  | 
 |     if (returns_async_->promise_support == | 
 |         binding::APIPromiseSupport::kSupported) { | 
 |       DCHECK(access_checker_) | 
 |           << "If an API supports promises, it needs to supply a " | 
 |              "BindingAccessChecker to be able to check if calling contexts are " | 
 |              "allowed to use promises"; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | APISignature::~APISignature() = default; | 
 |  | 
 | // static | 
 | std::unique_ptr<APISignature> APISignature::CreateFromValues( | 
 |     const base::Value& spec_list, | 
 |     const base::Value* returns_async, | 
 |     BindingAccessChecker* access_checker) { | 
 |   bool uses_returns_async = returns_async != nullptr; | 
 |   auto argument_specs = | 
 |       ValueListToArgumentSpecs(spec_list.GetList(), uses_returns_async); | 
 |  | 
 |   // Asynchronous returns for an API are defined in the returns_async part of | 
 |   // the specification. | 
 |   std::unique_ptr<APISignature::ReturnsAsync> returns_async_struct; | 
 |   if (returns_async) { | 
 |     returns_async_struct = | 
 |         BuildReturnsAsyncFromValues(returns_async->GetDict()); | 
 |   } | 
 |  | 
 |   return std::make_unique<APISignature>(std::move(argument_specs), | 
 |                                         std::move(returns_async_struct), | 
 |                                         access_checker); | 
 | } | 
 |  | 
 | APISignature::V8ParseResult APISignature::ParseArgumentsToV8( | 
 |     v8::Local<v8::Context> context, | 
 |     const v8::LocalVector<v8::Value>& arguments, | 
 |     const APITypeReferenceMap& type_refs) const { | 
 |   PromisesAllowed promises_allowed = CheckPromisesAllowed(context); | 
 |   return V8ArgumentParser(context, signature_, arguments, type_refs, | 
 |                           promises_allowed) | 
 |       .ParseArguments(has_async_return()); | 
 | } | 
 |  | 
 | APISignature::JSONParseResult APISignature::ParseArgumentsToJSON( | 
 |     v8::Local<v8::Context> context, | 
 |     const v8::LocalVector<v8::Value>& arguments, | 
 |     const APITypeReferenceMap& type_refs) const { | 
 |   PromisesAllowed promises_allowed = CheckPromisesAllowed(context); | 
 |   return BaseValueArgumentParser(context, signature_, arguments, type_refs, | 
 |                                  promises_allowed) | 
 |       .ParseArguments(has_async_return()); | 
 | } | 
 |  | 
 | APISignature::JSONParseResult APISignature::ConvertArgumentsIgnoringSchema( | 
 |     v8::Local<v8::Context> context, | 
 |     const v8::LocalVector<v8::Value>& arguments) const { | 
 |   JSONParseResult result; | 
 |   size_t size = arguments.size(); | 
 |   // TODO(devlin): This is what the current bindings do, but it's quite terribly | 
 |   // incorrect. We only hit this flow when an API method has a hook to update | 
 |   // the arguments post-validation, and in some cases, the arguments returned by | 
 |   // that hook do *not* match the signature of the API method (e.g. | 
 |   // fileSystem.getDisplayPath); see also note in api_bindings.cc for why this | 
 |   // is bad. But then here, we *rely* on the signature to determine whether or | 
 |   // not the last parameter is a callback, even though the hooks may not return | 
 |   // the arguments in the signature. This is very broken. | 
 |   if (has_async_return()) { | 
 |     CHECK(!arguments.empty()); | 
 |     v8::Local<v8::Value> value = arguments.back(); | 
 |     --size; | 
 |     // Bindings should ensure that the value here is appropriate, but see the | 
 |     // comment above for limitations. | 
 |     DCHECK(value->IsFunction() || value->IsUndefined() || value->IsNull()); | 
 |     if (value->IsFunction()) { | 
 |       result.callback = value.As<v8::Function>(); | 
 |       result.async_type = binding::AsyncResponseType::kCallback; | 
 |     } else if ((value->IsNull() || value->IsUndefined()) && | 
 |                CheckPromisesAllowed(context) == PromisesAllowed::kAllowed) { | 
 |       result.async_type = binding::AsyncResponseType::kPromise; | 
 |     } | 
 |   } | 
 |  | 
 |   base::Value::List json; | 
 |   json.reserve(size); | 
 |  | 
 |   std::unique_ptr<content::V8ValueConverter> converter = | 
 |       content::V8ValueConverter::Create(); | 
 |   converter->SetFunctionAllowed(true); | 
 |   converter->SetConvertNegativeZeroToInt(true); | 
 |   converter->SetStripNullFromObjects(true); | 
 |  | 
 |   for (size_t i = 0; i < size; ++i) { | 
 |     std::unique_ptr<base::Value> converted = | 
 |         converter->FromV8Value(arguments[i], context); | 
 |     if (!converted) { | 
 |       // JSON.stringify inserts non-serializable values as "null" when | 
 |       // handling arrays, and this behavior is emulated in V8ValueConverter for | 
 |       // array values. Since JS bindings parsed arguments from a single array, | 
 |       // it accepted unserializable argument entries (which were converted to | 
 |       // null). Duplicate that behavior here. | 
 |       converted = std::make_unique<base::Value>(); | 
 |     } | 
 |     json.Append(base::Value::FromUniquePtrValue(std::move(converted))); | 
 |   } | 
 |  | 
 |   result.arguments_list = std::move(json); | 
 |   return result; | 
 | } | 
 |  | 
 | bool APISignature::ValidateResponse(v8::Local<v8::Context> context, | 
 |                                     const v8::LocalVector<v8::Value>& arguments, | 
 |                                     const APITypeReferenceMap& type_refs, | 
 |                                     std::string* error) const { | 
 |   DCHECK(returns_async_); | 
 |   DCHECK(returns_async_->signature); | 
 |   return ValidateSignatureForInternalCaller( | 
 |       context, arguments, *returns_async_->signature, type_refs, error); | 
 | } | 
 |  | 
 | bool APISignature::ValidateCall(v8::Local<v8::Context> context, | 
 |                                 const v8::LocalVector<v8::Value>& arguments, | 
 |                                 const APITypeReferenceMap& type_refs, | 
 |                                 std::string* error) const { | 
 |   return ValidateSignatureForInternalCaller(context, arguments, signature_, | 
 |                                             type_refs, error); | 
 | } | 
 |  | 
 | std::string APISignature::GetExpectedSignature() const { | 
 |   if (!expected_signature_.empty() || signature_.empty()) | 
 |     return expected_signature_; | 
 |  | 
 |   expected_signature_ = ArgumentSpecsToString(signature_); | 
 |  | 
 |   return expected_signature_; | 
 | } | 
 |  | 
 | PromisesAllowed APISignature::CheckPromisesAllowed( | 
 |     v8::Local<v8::Context> context) const { | 
 |   // Promises are only allowed if both the API supports promises and the context | 
 |   // is allowed to use promises. | 
 |   if (returns_async_ && returns_async_->promise_support == | 
 |                             binding::APIPromiseSupport::kSupported) { | 
 |     DCHECK(access_checker_); | 
 |     if (access_checker_->HasPromiseAccess(context)) | 
 |       return PromisesAllowed::kAllowed; | 
 |   } | 
 |   return PromisesAllowed::kDisallowed; | 
 | } | 
 |  | 
 | }  // namespace extensions |