| 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_binding_hooks.h" | 5 #include "extensions/renderer/api_binding_hooks.h" |
| 6 | 6 |
| 7 #include "base/memory/ptr_util.h" | 7 #include "base/memory/ptr_util.h" |
| 8 #include "base/strings/stringprintf.h" | 8 #include "base/strings/stringprintf.h" |
| 9 #include "base/supports_user_data.h" | 9 #include "base/supports_user_data.h" |
| 10 #include "extensions/renderer/api_signature.h" | 10 #include "extensions/renderer/api_signature.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 : api_name_(api_name) {} | 26 : api_name_(api_name) {} |
| 27 | 27 |
| 28 static gin::WrapperInfo kWrapperInfo; | 28 static gin::WrapperInfo kWrapperInfo; |
| 29 | 29 |
| 30 // gin::Wrappable: | 30 // gin::Wrappable: |
| 31 gin::ObjectTemplateBuilder GetObjectTemplateBuilder( | 31 gin::ObjectTemplateBuilder GetObjectTemplateBuilder( |
| 32 v8::Isolate* isolate) override { | 32 v8::Isolate* isolate) override { |
| 33 return Wrappable<JSHookInterface>::GetObjectTemplateBuilder(isolate) | 33 return Wrappable<JSHookInterface>::GetObjectTemplateBuilder(isolate) |
| 34 .SetMethod("setHandleRequest", &JSHookInterface::SetHandleRequest) | 34 .SetMethod("setHandleRequest", &JSHookInterface::SetHandleRequest) |
| 35 .SetMethod("setUpdateArgumentsPreValidate", | 35 .SetMethod("setUpdateArgumentsPreValidate", |
| 36 &JSHookInterface::SetUpdateArgumentsPreValidate); | 36 &JSHookInterface::SetUpdateArgumentsPreValidate) |
| 37 .SetMethod("setUpdateArgumentsPostValidate", |
| 38 &JSHookInterface::SetUpdateArgumentsPostValidate); |
| 37 } | 39 } |
| 38 | 40 |
| 39 void ClearHooks() { | 41 void ClearHooks() { |
| 40 handle_request_hooks_.clear(); | 42 handle_request_hooks_.clear(); |
| 41 pre_validation_hooks_.clear(); | 43 pre_validation_hooks_.clear(); |
| 44 post_validation_hooks_.clear(); |
| 42 } | 45 } |
| 43 | 46 |
| 44 v8::Local<v8::Function> GetHandleRequestHook(const std::string& method_name, | 47 v8::Local<v8::Function> GetHandleRequestHook(const std::string& method_name, |
| 45 v8::Isolate* isolate) const { | 48 v8::Isolate* isolate) const { |
| 46 return GetHookFromMap(handle_request_hooks_, method_name, isolate); | 49 return GetHookFromMap(handle_request_hooks_, method_name, isolate); |
| 47 } | 50 } |
| 48 | 51 |
| 49 v8::Local<v8::Function> GetPreValidationHook(const std::string& method_name, | 52 v8::Local<v8::Function> GetPreValidationHook(const std::string& method_name, |
| 50 v8::Isolate* isolate) const { | 53 v8::Isolate* isolate) const { |
| 51 return GetHookFromMap(pre_validation_hooks_, method_name, isolate); | 54 return GetHookFromMap(pre_validation_hooks_, method_name, isolate); |
| 52 } | 55 } |
| 53 | 56 |
| 57 v8::Local<v8::Function> GetPostValidationHook(const std::string& method_name, |
| 58 v8::Isolate* isolate) const { |
| 59 return GetHookFromMap(post_validation_hooks_, method_name, isolate); |
| 60 } |
| 61 |
| 54 private: | 62 private: |
| 55 using JSHooks = std::map<std::string, v8::Global<v8::Function>>; | 63 using JSHooks = std::map<std::string, v8::Global<v8::Function>>; |
| 56 | 64 |
| 57 v8::Local<v8::Function> GetHookFromMap(const JSHooks& map, | 65 v8::Local<v8::Function> GetHookFromMap(const JSHooks& map, |
| 58 const std::string& method_name, | 66 const std::string& method_name, |
| 59 v8::Isolate* isolate) const { | 67 v8::Isolate* isolate) const { |
| 60 auto iter = map.find(method_name); | 68 auto iter = map.find(method_name); |
| 61 if (iter == map.end()) | 69 if (iter == map.end()) |
| 62 return v8::Local<v8::Function>(); | 70 return v8::Local<v8::Function>(); |
| 63 return iter->second.Get(isolate); | 71 return iter->second.Get(isolate); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 85 } | 93 } |
| 86 | 94 |
| 87 // Adds a hook to update the arguments passed to the API method before we do | 95 // Adds a hook to update the arguments passed to the API method before we do |
| 88 // any kind of validation. | 96 // any kind of validation. |
| 89 void SetUpdateArgumentsPreValidate(v8::Isolate* isolate, | 97 void SetUpdateArgumentsPreValidate(v8::Isolate* isolate, |
| 90 const std::string& method_name, | 98 const std::string& method_name, |
| 91 v8::Local<v8::Function> hook) { | 99 v8::Local<v8::Function> hook) { |
| 92 AddHookToMap(&pre_validation_hooks_, isolate, method_name, hook); | 100 AddHookToMap(&pre_validation_hooks_, isolate, method_name, hook); |
| 93 } | 101 } |
| 94 | 102 |
| 103 void SetUpdateArgumentsPostValidate(v8::Isolate* isolate, |
| 104 const std::string& method_name, |
| 105 v8::Local<v8::Function> hook) { |
| 106 AddHookToMap(&post_validation_hooks_, isolate, method_name, hook); |
| 107 } |
| 108 |
| 95 std::string api_name_; | 109 std::string api_name_; |
| 96 | 110 |
| 97 JSHooks handle_request_hooks_; | 111 JSHooks handle_request_hooks_; |
| 98 JSHooks pre_validation_hooks_; | 112 JSHooks pre_validation_hooks_; |
| 113 JSHooks post_validation_hooks_; |
| 99 | 114 |
| 100 DISALLOW_COPY_AND_ASSIGN(JSHookInterface); | 115 DISALLOW_COPY_AND_ASSIGN(JSHookInterface); |
| 101 }; | 116 }; |
| 102 | 117 |
| 103 const char kExtensionAPIHooksPerContextKey[] = "extension_api_hooks"; | 118 const char kExtensionAPIHooksPerContextKey[] = "extension_api_hooks"; |
| 104 | 119 |
| 105 struct APIHooksPerContextData : public base::SupportsUserData::Data { | 120 struct APIHooksPerContextData : public base::SupportsUserData::Data { |
| 106 APIHooksPerContextData(v8::Isolate* isolate) : isolate(isolate) {} | 121 APIHooksPerContextData(v8::Isolate* isolate) : isolate(isolate) {} |
| 107 ~APIHooksPerContextData() override { | 122 ~APIHooksPerContextData() override { |
| 108 v8::HandleScope scope(isolate); | 123 v8::HandleScope scope(isolate); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 DCHECK(!hooks_used_) << "Hooks must be registered before the first use!"; | 193 DCHECK(!hooks_used_) << "Hooks must be registered before the first use!"; |
| 179 request_hooks_[method_name] = hook; | 194 request_hooks_[method_name] = hook; |
| 180 } | 195 } |
| 181 | 196 |
| 182 void APIBindingHooks::RegisterJsSource(v8::Global<v8::String> source, | 197 void APIBindingHooks::RegisterJsSource(v8::Global<v8::String> source, |
| 183 v8::Global<v8::String> resource_name) { | 198 v8::Global<v8::String> resource_name) { |
| 184 js_hooks_source_ = std::move(source); | 199 js_hooks_source_ = std::move(source); |
| 185 js_resource_name_ = std::move(resource_name); | 200 js_resource_name_ = std::move(resource_name); |
| 186 } | 201 } |
| 187 | 202 |
| 188 APIBindingHooks::RequestResult APIBindingHooks::HandleRequest( | 203 APIBindingHooks::RequestResult APIBindingHooks::RunHooks( |
| 189 const std::string& api_name, | 204 const std::string& api_name, |
| 190 const std::string& method_name, | 205 const std::string& method_name, |
| 191 v8::Local<v8::Context> context, | 206 v8::Local<v8::Context> context, |
| 192 const APISignature* signature, | 207 const APISignature* signature, |
| 193 std::vector<v8::Local<v8::Value>>* arguments, | 208 std::vector<v8::Local<v8::Value>>* arguments, |
| 194 const ArgumentSpec::RefMap& type_refs) { | 209 const ArgumentSpec::RefMap& type_refs) { |
| 195 // Easy case: a native custom hook. | 210 // Easy case: a native custom hook. |
| 196 auto request_hooks_iter = request_hooks_.find(method_name); | 211 auto request_hooks_iter = request_hooks_.find(method_name); |
| 197 if (request_hooks_iter != request_hooks_.end()) { | 212 if (request_hooks_iter != request_hooks_.end()) { |
| 198 RequestResult result = | 213 RequestResult result = |
| (...skipping 15 matching lines...) Expand all Loading... |
| 214 v8::Isolate* isolate = context->GetIsolate(); | 229 v8::Isolate* isolate = context->GetIsolate(); |
| 215 | 230 |
| 216 JSHookInterface* hook_interface = nullptr; | 231 JSHookInterface* hook_interface = nullptr; |
| 217 gin::Converter<JSHookInterface*>::FromV8( | 232 gin::Converter<JSHookInterface*>::FromV8( |
| 218 isolate, | 233 isolate, |
| 219 hook_interface_object, &hook_interface); | 234 hook_interface_object, &hook_interface); |
| 220 CHECK(hook_interface); | 235 CHECK(hook_interface); |
| 221 | 236 |
| 222 v8::Local<v8::Function> pre_validate_hook = | 237 v8::Local<v8::Function> pre_validate_hook = |
| 223 hook_interface->GetPreValidationHook(method_name, isolate); | 238 hook_interface->GetPreValidationHook(method_name, isolate); |
| 239 v8::TryCatch try_catch(isolate); |
| 224 if (!pre_validate_hook.IsEmpty()) { | 240 if (!pre_validate_hook.IsEmpty()) { |
| 225 v8::TryCatch try_catch(isolate); | |
| 226 // TODO(devlin): What to do with the result of this function call? Can it | 241 // TODO(devlin): What to do with the result of this function call? Can it |
| 227 // only fail in the case we've already thrown? | 242 // only fail in the case we've already thrown? |
| 228 UpdateArguments(pre_validate_hook, context, arguments); | 243 UpdateArguments(pre_validate_hook, context, arguments); |
| 229 if (try_catch.HasCaught()) { | 244 if (try_catch.HasCaught()) { |
| 230 try_catch.ReThrow(); | 245 try_catch.ReThrow(); |
| 231 return RequestResult(RequestResult::THROWN); | 246 return RequestResult(RequestResult::THROWN); |
| 232 } | 247 } |
| 233 } | 248 } |
| 234 | 249 |
| 250 v8::Local<v8::Function> post_validate_hook = |
| 251 hook_interface->GetPostValidationHook(method_name, isolate); |
| 235 v8::Local<v8::Function> handle_request = | 252 v8::Local<v8::Function> handle_request = |
| 236 hook_interface->GetHandleRequestHook(method_name, isolate); | 253 hook_interface->GetHandleRequestHook(method_name, isolate); |
| 237 if (!handle_request.IsEmpty()) { | 254 // If both the post validation hook and the handle request hook are empty, |
| 238 v8::TryCatch try_catch(isolate); | 255 // we're done... |
| 256 if (post_validate_hook.IsEmpty() && handle_request.IsEmpty()) |
| 257 return RequestResult(RequestResult::NOT_HANDLED); |
| 258 |
| 259 { |
| 260 // ... otherwise, we have to validate the arguments. |
| 239 std::vector<v8::Local<v8::Value>> parsed_v8_args; | 261 std::vector<v8::Local<v8::Value>> parsed_v8_args; |
| 240 std::string error; | 262 std::string error; |
| 241 bool success = signature->ParseArgumentsToV8(context, *arguments, type_refs, | 263 bool success = signature->ParseArgumentsToV8(context, *arguments, type_refs, |
| 242 &parsed_v8_args, &error); | 264 &parsed_v8_args, &error); |
| 243 if (try_catch.HasCaught()) { | 265 if (try_catch.HasCaught()) { |
| 244 try_catch.ReThrow(); | 266 try_catch.ReThrow(); |
| 245 return RequestResult(RequestResult::THROWN); | 267 return RequestResult(RequestResult::THROWN); |
| 246 } | 268 } |
| 247 if (!success) | 269 if (!success) |
| 248 return RequestResult(RequestResult::INVALID_INVOCATION); | 270 return RequestResult(RequestResult::INVALID_INVOCATION); |
| 271 arguments->swap(parsed_v8_args); |
| 272 } |
| 249 | 273 |
| 250 v8::Global<v8::Value> global_result = | 274 if (!post_validate_hook.IsEmpty()) { |
| 251 run_js_.Run(handle_request, context, parsed_v8_args.size(), | 275 UpdateArguments(post_validate_hook, context, arguments); |
| 252 parsed_v8_args.data()); | |
| 253 if (try_catch.HasCaught()) { | 276 if (try_catch.HasCaught()) { |
| 254 try_catch.ReThrow(); | 277 try_catch.ReThrow(); |
| 255 return RequestResult(RequestResult::THROWN); | 278 return RequestResult(RequestResult::THROWN); |
| 256 } | 279 } |
| 257 RequestResult result(RequestResult::HANDLED); | |
| 258 if (!global_result.IsEmpty()) | |
| 259 result.return_value = global_result.Get(isolate); | |
| 260 return result; | |
| 261 } | 280 } |
| 262 | 281 |
| 263 return RequestResult(RequestResult::NOT_HANDLED); | 282 if (handle_request.IsEmpty()) |
| 283 return RequestResult(RequestResult::NOT_HANDLED); |
| 284 |
| 285 v8::Global<v8::Value> global_result = |
| 286 run_js_.Run(handle_request, context, arguments->size(), |
| 287 arguments->data()); |
| 288 if (try_catch.HasCaught()) { |
| 289 try_catch.ReThrow(); |
| 290 return RequestResult(RequestResult::THROWN); |
| 291 } |
| 292 RequestResult result(RequestResult::HANDLED); |
| 293 if (!global_result.IsEmpty()) |
| 294 result.return_value = global_result.Get(isolate); |
| 295 return result; |
| 264 } | 296 } |
| 265 | 297 |
| 266 void APIBindingHooks::InitializeInContext( | 298 void APIBindingHooks::InitializeInContext( |
| 267 v8::Local<v8::Context> context, | 299 v8::Local<v8::Context> context, |
| 268 const std::string& api_name) { | 300 const std::string& api_name) { |
| 269 if (js_hooks_source_.IsEmpty()) | 301 if (js_hooks_source_.IsEmpty()) |
| 270 return; | 302 return; |
| 271 | 303 |
| 272 v8::Local<v8::String> source = js_hooks_source_.Get(context->GetIsolate()); | 304 v8::Local<v8::String> source = js_hooks_source_.Get(context->GetIsolate()); |
| 273 v8::Local<v8::String> resource_name = | 305 v8::Local<v8::String> resource_name = |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 if (result.IsEmpty() || | 343 if (result.IsEmpty() || |
| 312 !gin::Converter<std::vector<v8::Local<v8::Value>>>::FromV8( | 344 !gin::Converter<std::vector<v8::Local<v8::Value>>>::FromV8( |
| 313 context->GetIsolate(), result, &new_args)) { | 345 context->GetIsolate(), result, &new_args)) { |
| 314 return false; | 346 return false; |
| 315 } | 347 } |
| 316 arguments->swap(new_args); | 348 arguments->swap(new_args); |
| 317 return true; | 349 return true; |
| 318 } | 350 } |
| 319 | 351 |
| 320 } // namespace extensions | 352 } // namespace extensions |
| OLD | NEW |