123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // 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/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 {
- // A list of API's which have a trailing function parameter that is not actually
- // meant to be treated like a traditional API success callback. For more details
- // see the comment where this is used in APISignature::CreateFromValues.
- constexpr const char* const kNonCallbackTrailingFunctionAPINames[] = {
- "test.listenForever",
- "test.listenOnce",
- "test.callbackPass",
- "test.callbackFail",
- "automation.addTreeChangeObserver",
- "automation.removeTreeChangeObserver",
- };
- std::vector<std::unique_ptr<ArgumentSpec>> ValueListToArgumentSpecs(
- const base::Value& specification_list,
- bool uses_returns_async) {
- std::vector<std::unique_ptr<ArgumentSpec>> signature;
- auto size = specification_list.GetList().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++;
- signature.reserve(size);
- for (const auto& value : specification_list.GetList()) {
- CHECK(value.is_dict());
- signature.push_back(std::make_unique<ArgumentSpec>(value));
- }
- return signature;
- }
- std::unique_ptr<APISignature::ReturnsAsync> BuildReturnsAsyncFromValues(
- const base::Value& returns_async_spec,
- bool api_supports_promises) {
- auto returns_async = std::make_unique<APISignature::ReturnsAsync>();
- if (api_supports_promises)
- returns_async->promise_support = binding::APIPromiseSupport::kSupported;
- const base::Value* callback_optional =
- returns_async_spec.FindKeyOfType("optional", base::Value::Type::BOOLEAN);
- returns_async->optional = callback_optional && callback_optional->GetBool();
- // 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* callback_params =
- returns_async_spec.FindKeyOfType("parameters", base::Value::Type::LIST);
- 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 std::vector<v8::Local<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 context_->GetIsolate(); }
- // 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,
- std::vector<v8::Local<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 std::vector<std::unique_ptr<ArgumentSpec>>& signature_;
- const std::vector<v8::Local<v8::Value>>& provided_arguments_;
- 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 std::vector<v8::Local<v8::Value>>& arguments,
- const APITypeReferenceMap& type_refs,
- PromisesAllowed promises_allowed)
- : ArgumentParser(context,
- signature,
- arguments,
- type_refs,
- promises_allowed) {}
- 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_;
- std::vector<v8::Local<v8::Value>> values_;
- };
- class BaseValueArgumentParser : public ArgumentParser {
- public:
- BaseValueArgumentParser(
- v8::Local<v8::Context> context,
- const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
- const std::vector<v8::Local<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;
- std::vector<v8::Local<v8::Value>> resolved_arguments(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,
- std::vector<v8::Local<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;
- DCHECK(!expected.empty());
- // 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::make_unique<base::Value>(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 std::vector<v8::Local<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() {}
- // static
- std::unique_ptr<APISignature> APISignature::CreateFromValues(
- const base::Value& spec_list,
- const base::Value* returns_async,
- BindingAccessChecker* access_checker,
- const std::string& api_name,
- bool is_event_signature) {
- bool uses_returns_async = returns_async != nullptr;
- auto argument_specs = ValueListToArgumentSpecs(spec_list, uses_returns_async);
- // Asynchronous returns for an API are either defined in the returns_async
- // part of the specification or as a trailing function argument.
- // TODO(crbug.com/1288583): There are a handful of APIs which have a trailing
- // function argument that is not a traditional callback. Once we have moved
- // all the asynchronous API schemas to use the returns_async format it will be
- // clear when this is the case, but for now we keep a list of the names of
- // these APIs to ensure we are handling them correctly.
- // This is irrelevant for event signatures.
- const base::Value* returns_async_spec = returns_async;
- if (!is_event_signature && !argument_specs.empty() &&
- argument_specs.back()->type() == ArgumentType::FUNCTION &&
- !base::Contains(kNonCallbackTrailingFunctionAPINames, api_name)) {
- DCHECK(!returns_async_spec);
- returns_async_spec = &spec_list.GetList().back();
- argument_specs.pop_back();
- }
- std::unique_ptr<APISignature::ReturnsAsync> returns_async_struct;
- if (returns_async_spec) {
- bool api_supports_promises = returns_async != nullptr;
- returns_async_struct =
- BuildReturnsAsyncFromValues(*returns_async_spec, api_supports_promises);
- }
- 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 std::vector<v8::Local<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 std::vector<v8::Local<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 std::vector<v8::Local<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::make_unique<base::Value>(std::move(json));
- return result;
- }
- bool APISignature::ValidateResponse(
- v8::Local<v8::Context> context,
- const std::vector<v8::Local<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 std::vector<v8::Local<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
|