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