Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/api_signature.h" | 5 #include "extensions/renderer/api_signature.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 "gin/arguments.h" | 9 #include "gin/arguments.h" |
| 10 | 10 |
| 11 namespace extensions { | 11 namespace extensions { |
| 12 | 12 |
| 13 APISignature::APISignature(const base::ListValue& specification) { | 13 namespace { |
| 14 signature_.reserve(specification.GetSize()); | 14 |
| 15 for (const auto& value : specification) { | 15 // A class to help with argument parsing. Note that this uses v8::Locals and |
| 16 const base::DictionaryValue* param = nullptr; | 16 // const&s because it's an implementation detail of the APISignature; this |
| 17 CHECK(value->GetAsDictionary(¶m)); | 17 // should *only* be used directly on the stack! |
| 18 signature_.push_back(base::MakeUnique<ArgumentSpec>(*param)); | 18 class ArgumentParser { |
| 19 public: | |
| 20 ArgumentParser(const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | |
| 21 gin::Arguments* arguments, | |
| 22 const ArgumentSpec::RefMap& type_refs, | |
| 23 std::string* error) | |
| 24 : context_(arguments->isolate()->GetCurrentContext()), | |
| 25 signature_(signature), | |
| 26 arguments_(arguments), | |
| 27 type_refs_(type_refs), | |
| 28 error_(error) {} | |
| 29 | |
| 30 // Tries to parse the arguments against the expected signature, populating a | |
| 31 // resulting list as it goes. | |
|
lazyboy
2016/12/27 23:48:44
nittynit: I find "as it goes" a bit confusing, hin
Devlin
2016/12/28 00:56:18
Sounds good to me; done.
| |
| 32 bool ParseArguments(); | |
| 33 | |
| 34 protected: | |
| 35 v8::Isolate* GetIsolate() { return context_->GetIsolate(); } | |
| 36 | |
| 37 private: | |
| 38 // Attempts to match the next argument to the given |spec|. | |
| 39 // If the next argument does not match and |spec| is optional, uses a null | |
| 40 // value. | |
| 41 // Returns true on success. | |
| 42 bool ParseArgument(const ArgumentSpec& spec); | |
| 43 | |
| 44 // Attempts to parse the callback from the given |spec|. Returns true on | |
| 45 // success. | |
| 46 bool ParseCallback(const ArgumentSpec& spec); | |
| 47 | |
| 48 // Adds a null value to the parsed arguments. | |
| 49 virtual void AddNull() = 0; | |
| 50 // Returns a base::Value to be populated during argument matching. | |
| 51 virtual std::unique_ptr<base::Value>* GetBuffer() = 0; | |
| 52 // Adds a new parsed argument. | |
| 53 virtual void AddParsedArgument(v8::Local<v8::Value> value) = 0; | |
| 54 // Adds the parsed callback. | |
| 55 virtual void SetCallback(v8::Local<v8::Function> callback) = 0; | |
| 56 | |
| 57 v8::Local<v8::Context> context_; | |
| 58 const std::vector<std::unique_ptr<ArgumentSpec>>& signature_; | |
| 59 gin::Arguments* arguments_; | |
| 60 const ArgumentSpec::RefMap& type_refs_; | |
| 61 std::string* error_; | |
| 62 | |
| 63 DISALLOW_COPY_AND_ASSIGN(ArgumentParser); | |
| 64 }; | |
| 65 | |
| 66 class V8ArgumentParser : public ArgumentParser { | |
| 67 public: | |
| 68 V8ArgumentParser(const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | |
| 69 gin::Arguments* arguments, | |
| 70 const ArgumentSpec::RefMap& type_refs, | |
| 71 std::string* error, | |
| 72 std::vector<v8::Local<v8::Value>>* values) | |
| 73 : ArgumentParser(signature, arguments, type_refs, error), | |
| 74 values_(values) {} | |
| 75 | |
| 76 private: | |
| 77 void AddNull() override { values_->push_back(v8::Null(GetIsolate())); } | |
| 78 std::unique_ptr<base::Value>* GetBuffer() override { return nullptr; } | |
| 79 void AddParsedArgument(v8::Local<v8::Value> value) override { | |
| 80 values_->push_back(value); | |
| 19 } | 81 } |
| 20 } | 82 void SetCallback(v8::Local<v8::Function> callback) override { |
| 83 values_->push_back(callback); | |
| 84 } | |
| 21 | 85 |
| 22 APISignature::~APISignature() {} | 86 std::vector<v8::Local<v8::Value>>* values_; |
| 23 | 87 |
| 24 std::unique_ptr<base::ListValue> APISignature::ParseArguments( | 88 DISALLOW_COPY_AND_ASSIGN(V8ArgumentParser); |
| 25 gin::Arguments* arguments, | 89 }; |
| 26 const ArgumentSpec::RefMap& type_refs, | |
| 27 v8::Local<v8::Function>* callback_out, | |
| 28 std::string* error) const { | |
| 29 auto results = base::MakeUnique<base::ListValue>(); | |
| 30 | 90 |
| 91 class BaseValueArgumentParser : public ArgumentParser { | |
| 92 public: | |
| 93 BaseValueArgumentParser( | |
| 94 const std::vector<std::unique_ptr<ArgumentSpec>>& signature, | |
| 95 gin::Arguments* arguments, | |
| 96 const ArgumentSpec::RefMap& type_refs, | |
| 97 std::string* error, | |
| 98 base::ListValue* list_value) | |
| 99 : ArgumentParser(signature, arguments, type_refs, error), | |
| 100 list_value_(list_value) {} | |
| 101 | |
| 102 v8::Local<v8::Function> callback() { return callback_; } | |
| 103 | |
| 104 private: | |
| 105 void AddNull() override { | |
| 106 list_value_->Append(base::Value::CreateNullValue()); | |
| 107 } | |
| 108 std::unique_ptr<base::Value>* GetBuffer() override { return &last_arg_; } | |
| 109 void AddParsedArgument(v8::Local<v8::Value> value) override { | |
| 110 // The corresponding base::Value is expected to have been stored in | |
| 111 // |last_arg_| already. | |
| 112 DCHECK(last_arg_); | |
| 113 list_value_->Append(std::move(last_arg_)); | |
| 114 last_arg_.reset(); | |
| 115 } | |
| 116 void SetCallback(v8::Local<v8::Function> callback) override { | |
| 117 callback_ = callback; | |
| 118 } | |
| 119 | |
| 120 base::ListValue* list_value_; | |
| 121 std::unique_ptr<base::Value> last_arg_; | |
| 122 v8::Local<v8::Function> callback_; | |
| 123 | |
| 124 DISALLOW_COPY_AND_ASSIGN(BaseValueArgumentParser); | |
| 125 }; | |
| 126 | |
| 127 bool ArgumentParser::ParseArguments() { | |
| 31 // TODO(devlin): This is how extension APIs have always determined if a | 128 // TODO(devlin): This is how extension APIs have always determined if a |
| 32 // function has a callback, but it seems a little silly. In the long run (once | 129 // function has a callback, but it seems a little silly. In the long run (once |
| 33 // signatures are generated), it probably makes sense to indicate this | 130 // signatures are generated), it probably makes sense to indicate this |
| 34 // differently. | 131 // differently. |
| 35 bool signature_has_callback = | 132 bool signature_has_callback = |
| 36 !signature_.empty() && | 133 !signature_.empty() && |
| 37 signature_.back()->type() == ArgumentType::FUNCTION; | 134 signature_.back()->type() == ArgumentType::FUNCTION; |
| 38 | 135 |
| 39 v8::Local<v8::Context> context = arguments->isolate()->GetCurrentContext(); | |
| 40 | |
| 41 size_t end_size = | 136 size_t end_size = |
| 42 signature_has_callback ? signature_.size() - 1 : signature_.size(); | 137 signature_has_callback ? signature_.size() - 1 : signature_.size(); |
| 43 for (size_t i = 0; i < end_size; ++i) { | 138 for (size_t i = 0; i < end_size; ++i) { |
| 44 std::unique_ptr<base::Value> parsed = | 139 if (!ParseArgument(*signature_[i])) |
| 45 ParseArgument(*signature_[i], context, arguments, type_refs, error); | 140 return false; |
| 46 if (!parsed) | |
| 47 return nullptr; | |
| 48 results->Append(std::move(parsed)); | |
| 49 } | 141 } |
| 50 | 142 |
| 51 v8::Local<v8::Function> callback_value; | 143 if (signature_has_callback && !ParseCallback(*signature_.back())) |
| 52 if (signature_has_callback && | 144 return false; |
| 53 !ParseCallback(arguments, *signature_.back(), error, &callback_value)) { | |
| 54 return nullptr; | |
| 55 } | |
| 56 | 145 |
| 57 if (!arguments->PeekNext().IsEmpty()) | 146 if (!arguments_->PeekNext().IsEmpty()) |
| 58 return nullptr; // Extra arguments aren't allowed. | 147 return false; // Extra arguments aren't allowed. |
| 59 | 148 |
| 60 *callback_out = callback_value; | 149 return true; |
| 61 return results; | |
| 62 } | 150 } |
| 63 | 151 |
| 64 std::unique_ptr<base::Value> APISignature::ParseArgument( | 152 bool ArgumentParser::ParseArgument(const ArgumentSpec& spec) { |
| 65 const ArgumentSpec& spec, | 153 v8::Local<v8::Value> value = arguments_->PeekNext(); |
| 66 v8::Local<v8::Context> context, | |
| 67 gin::Arguments* arguments, | |
| 68 const ArgumentSpec::RefMap& type_refs, | |
| 69 std::string* error) const { | |
| 70 v8::Local<v8::Value> value = arguments->PeekNext(); | |
| 71 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { | 154 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { |
| 72 if (!spec.optional()) { | 155 if (!spec.optional()) { |
| 73 *error = "Missing required argument: " + spec.name(); | 156 *error_ = "Missing required argument: " + spec.name(); |
| 74 return nullptr; | 157 return false; |
| 75 } | 158 } |
| 76 // This is safe to call even if |arguments| is at the end (which can happen | 159 // This is safe to call even if |arguments| is at the end (which can happen |
| 77 // if n optional arguments are omitted at the end of the signature). | 160 // if n optional arguments are omitted at the end of the signature). |
| 78 arguments->Skip(); | 161 arguments_->Skip(); |
| 79 return base::Value::CreateNullValue(); | 162 |
| 163 AddNull(); | |
| 164 return true; | |
| 80 } | 165 } |
| 81 | 166 |
| 82 std::unique_ptr<base::Value> result = | 167 if (!spec.ParseArgument(context_, value, type_refs_, GetBuffer(), error_)) { |
| 83 spec.ConvertArgument(context, value, type_refs, error); | |
| 84 if (!result) { | |
| 85 if (!spec.optional()) { | 168 if (!spec.optional()) { |
| 86 *error = "Missing required argument: " + spec.name(); | 169 *error_ = "Missing required argument: " + spec.name(); |
| 87 return nullptr; | 170 return false; |
| 88 } | 171 } |
| 89 return base::Value::CreateNullValue(); | 172 |
| 173 AddNull(); | |
| 174 return true; | |
| 90 } | 175 } |
| 91 | 176 |
| 92 arguments->Skip(); | 177 arguments_->Skip(); |
| 93 return result; | 178 AddParsedArgument(value); |
| 179 return true; | |
| 94 } | 180 } |
| 95 | 181 |
| 96 bool APISignature::ParseCallback(gin::Arguments* arguments, | 182 bool ArgumentParser::ParseCallback(const ArgumentSpec& spec) { |
| 97 const ArgumentSpec& callback_spec, | 183 v8::Local<v8::Value> value = arguments_->PeekNext(); |
| 98 std::string* error, | |
| 99 v8::Local<v8::Function>* callback_out) const { | |
| 100 v8::Local<v8::Value> value = arguments->PeekNext(); | |
| 101 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { | 184 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) { |
| 102 if (!callback_spec.optional()) { | 185 if (!spec.optional()) { |
| 103 *error = "Missing required argument: " + callback_spec.name(); | 186 *error_ = "Missing required argument: " + spec.name(); |
| 104 return false; | 187 return false; |
| 105 } | 188 } |
| 106 arguments->Skip(); | 189 arguments_->Skip(); |
| 107 return true; | 190 return true; |
| 108 } | 191 } |
| 109 | 192 |
| 110 if (!value->IsFunction()) { | 193 if (!value->IsFunction()) { |
| 111 *error = "Argument is wrong type: " + callback_spec.name(); | 194 *error_ = "Argument is wrong type: " + spec.name(); |
| 112 return false; | 195 return false; |
| 113 } | 196 } |
| 114 | 197 |
| 115 *callback_out = value.As<v8::Function>(); | 198 arguments_->Skip(); |
| 116 arguments->Skip(); | 199 SetCallback(value.As<v8::Function>()); |
| 117 return true; | 200 return true; |
| 118 } | 201 } |
| 119 | 202 |
| 203 } // namespace | |
| 204 | |
| 205 APISignature::APISignature(const base::ListValue& specification) { | |
| 206 signature_.reserve(specification.GetSize()); | |
| 207 for (const auto& value : specification) { | |
| 208 const base::DictionaryValue* param = nullptr; | |
| 209 CHECK(value->GetAsDictionary(¶m)); | |
| 210 signature_.push_back(base::MakeUnique<ArgumentSpec>(*param)); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 APISignature::~APISignature() {} | |
| 215 | |
| 216 bool APISignature::ParseArgumentsToV8(gin::Arguments* arguments, | |
| 217 const ArgumentSpec::RefMap& type_refs, | |
| 218 std::vector<v8::Local<v8::Value>>* v8_out, | |
| 219 std::string* error) const { | |
| 220 DCHECK(v8_out); | |
| 221 std::vector<v8::Local<v8::Value>> v8_values; | |
| 222 V8ArgumentParser parser(signature_, arguments, type_refs, error, &v8_values); | |
| 223 if (!parser.ParseArguments()) | |
| 224 return false; | |
| 225 *v8_out = std::move(v8_values); | |
| 226 return true; | |
| 227 } | |
| 228 | |
| 229 bool APISignature::ParseArgumentsToJSON( | |
| 230 gin::Arguments* arguments, | |
| 231 const ArgumentSpec::RefMap& type_refs, | |
| 232 std::unique_ptr<base::ListValue>* json_out, | |
| 233 v8::Local<v8::Function>* callback_out, | |
| 234 std::string* error) const { | |
| 235 DCHECK(json_out); | |
| 236 DCHECK(callback_out); | |
| 237 std::unique_ptr<base::ListValue> json = base::MakeUnique<base::ListValue>(); | |
| 238 BaseValueArgumentParser parser( | |
| 239 signature_, arguments, type_refs, error, json.get()); | |
| 240 if (!parser.ParseArguments()) | |
| 241 return false; | |
| 242 *json_out = std::move(json); | |
| 243 *callback_out = parser.callback(); | |
| 244 return true; | |
| 245 } | |
| 246 | |
| 120 } // namespace extensions | 247 } // namespace extensions |
| OLD | NEW |