Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/object_backed_native_handler.h" | 5 #include "extensions/renderer/object_backed_native_handler.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/linked_ptr.h" | 10 #include "base/memory/linked_ptr.h" |
| 11 #include "content/public/child/worker_thread.h" | 11 #include "content/public/child/worker_thread.h" |
| 12 #include "extensions/common/extension_api.h" | 12 #include "extensions/common/extension_api.h" |
| 13 #include "extensions/renderer/console.h" | 13 #include "extensions/renderer/console.h" |
| 14 #include "extensions/renderer/module_system.h" | 14 #include "extensions/renderer/module_system.h" |
| 15 #include "extensions/renderer/script_context.h" | 15 #include "extensions/renderer/script_context.h" |
| 16 #include "extensions/renderer/script_context_set.h" | 16 #include "extensions/renderer/script_context_set.h" |
| 17 #include "extensions/renderer/v8_helpers.h" | 17 #include "extensions/renderer/v8_helpers.h" |
| 18 #include "third_party/WebKit/public/web/WebLocalFrame.h" | 18 #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| 19 #include "v8/include/v8.h" | 19 #include "v8/include/v8.h" |
| 20 | 20 |
| 21 namespace extensions { | 21 namespace extensions { |
| 22 | 22 |
| 23 namespace { | 23 namespace { |
| 24 | |
| 25 using HandlerFunctionCheck = | |
|
asargent_no_longer_on_chrome
2016/05/06 17:09:04
nit: this could use a comment
Devlin
2016/05/06 22:13:47
Done.
| |
| 26 base::Callback<void(const v8::FunctionCallbackInfo<v8::Value>&, | |
| 27 const v8::Local<v8::Context>&)>; | |
| 28 | |
| 24 // Key for the base::Bound routed function. | 29 // Key for the base::Bound routed function. |
| 25 const char* kHandlerFunction = "handler_function"; | 30 const char* kHandlerFunction = "handler_function"; |
| 26 const char* kFeatureName = "feature_name"; | 31 |
| 32 // Checks whether a given |context| has any of the |allowed_features|. This | |
| 33 // treats an empty |allowed_features| as indicating that any context has access. | |
| 34 bool IsContextAllowed(const std::vector<std::string>& allowed_features, | |
| 35 const v8::Local<v8::Context>& context) { | |
| 36 // We can't access the ScriptContextSet on a worker thread. Luckily, we also | |
| 37 // don't inject many bindings into worker threads. | |
| 38 // TODO(devlin): Figure out a way around this. | |
| 39 if (content::WorkerThread::GetCurrentId() == 0) | |
| 40 return true; | |
| 41 if (allowed_features.empty()) | |
| 42 return true; | |
| 43 ScriptContext* script_context = | |
| 44 ScriptContextSet::GetContextByV8Context(context); | |
| 45 // TODO(devlin): Eventually, we should fail if script_context is null. | |
| 46 if (!script_context) | |
| 47 return true; | |
| 48 for (const std::string& feature : allowed_features) { | |
| 49 if (script_context->GetAvailability(feature).is_available()) | |
| 50 return true; | |
| 51 } | |
| 52 | |
| 53 return false; | |
| 54 } | |
| 55 | |
| 56 // Checks whether the given |context| has access to any of the necessary | |
| 57 // |allowed_features| and calls |function|. | |
| 58 void ValidateAndCall(const ObjectBackedNativeHandler::HandlerFunction& function, | |
| 59 const std::vector<std::string>& allowed_features, | |
| 60 const v8::FunctionCallbackInfo<v8::Value>& args, | |
| 61 const v8::Local<v8::Context>& context) { | |
| 62 if (!IsContextAllowed(allowed_features, context)) { | |
| 63 NOTREACHED(); | |
| 64 return; | |
| 65 } | |
| 66 function.Run(args); | |
| 67 } | |
| 68 | |
| 27 } // namespace | 69 } // namespace |
| 28 | 70 |
| 29 ObjectBackedNativeHandler::ObjectBackedNativeHandler(ScriptContext* context) | 71 ObjectBackedNativeHandler::ObjectBackedNativeHandler(ScriptContext* context) |
| 30 : router_data_(context->isolate()), | 72 : router_data_(context->isolate()), |
| 31 context_(context), | 73 context_(context), |
| 32 object_template_(context->isolate(), | 74 object_template_(context->isolate(), |
| 33 v8::ObjectTemplate::New(context->isolate())) { | 75 v8::ObjectTemplate::New(context->isolate())) { |
| 34 } | 76 } |
| 35 | 77 |
| 36 ObjectBackedNativeHandler::~ObjectBackedNativeHandler() { | 78 ObjectBackedNativeHandler::~ObjectBackedNativeHandler() { |
| 37 } | 79 } |
| 38 | 80 |
| 39 v8::Local<v8::Object> ObjectBackedNativeHandler::NewInstance() { | 81 v8::Local<v8::Object> ObjectBackedNativeHandler::NewInstance() { |
| 40 return v8::Local<v8::ObjectTemplate>::New(GetIsolate(), object_template_) | 82 return v8::Local<v8::ObjectTemplate>::New(GetIsolate(), object_template_) |
| 41 ->NewInstance(); | 83 ->NewInstance(); |
| 42 } | 84 } |
| 43 | 85 |
| 44 // static | 86 // static |
| 45 void ObjectBackedNativeHandler::Router( | 87 void ObjectBackedNativeHandler::Router( |
| 46 const v8::FunctionCallbackInfo<v8::Value>& args) { | 88 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 47 v8::Isolate* isolate = args.GetIsolate(); | 89 v8::Isolate* isolate = args.GetIsolate(); |
| 48 v8::HandleScope handle_scope(isolate); | 90 v8::HandleScope handle_scope(isolate); |
| 49 v8::Local<v8::Object> data = args.Data().As<v8::Object>(); | 91 v8::Local<v8::Object> data = args.Data().As<v8::Object>(); |
| 50 v8::Local<v8::Context> context = isolate->GetCurrentContext(); | 92 v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 51 | 93 |
| 52 v8::Local<v8::Value> handler_function_value; | 94 v8::Local<v8::Value> handler_function_value; |
| 53 v8::Local<v8::Value> feature_name_value; | |
| 54 // See comment in header file for why we do this. | 95 // See comment in header file for why we do this. |
| 55 if (!GetPrivate(context, data, kHandlerFunction, &handler_function_value) || | 96 if (!GetPrivate(context, data, kHandlerFunction, &handler_function_value) || |
| 56 handler_function_value->IsUndefined() || | 97 handler_function_value->IsUndefined()) { |
| 57 !GetPrivate(context, data, kFeatureName, &feature_name_value) || | |
| 58 !feature_name_value->IsString()) { | |
| 59 ScriptContext* script_context = | 98 ScriptContext* script_context = |
| 60 ScriptContextSet::GetContextByV8Context(context); | 99 ScriptContextSet::GetContextByV8Context(context); |
| 61 console::Error(script_context ? script_context->GetRenderFrame() : nullptr, | 100 console::Error(script_context ? script_context->GetRenderFrame() : nullptr, |
| 62 "Extension view no longer exists"); | 101 "Extension view no longer exists"); |
| 63 return; | 102 return; |
| 64 } | 103 } |
| 65 | 104 |
| 66 // We can't access the ScriptContextSet on a worker thread. Luckily, we also | |
| 67 // don't inject many bindings into worker threads. | |
| 68 // TODO(devlin): Figure out a way around this. | |
| 69 if (content::WorkerThread::GetCurrentId() == 0) { | |
| 70 ScriptContext* script_context = | |
| 71 ScriptContextSet::GetContextByV8Context(context); | |
| 72 v8::Local<v8::String> feature_name_string = | |
| 73 feature_name_value->ToString(context).ToLocalChecked(); | |
| 74 std::string feature_name = *v8::String::Utf8Value(feature_name_string); | |
| 75 // TODO(devlin): Eventually, we should fail if either script_context is null | |
| 76 // or feature_name is empty. | |
| 77 if (script_context && !feature_name.empty()) { | |
| 78 Feature::Availability availability = | |
| 79 script_context->GetAvailability(feature_name); | |
| 80 if (!availability.is_available()) { | |
| 81 DVLOG(1) << feature_name | |
| 82 << " is not available: " << availability.message(); | |
| 83 return; | |
| 84 } | |
| 85 } | |
| 86 } | |
| 87 // This CHECK is *important*. Otherwise, we'll go around happily executing | 105 // This CHECK is *important*. Otherwise, we'll go around happily executing |
| 88 // something random. See crbug.com/548273. | 106 // something random. See crbug.com/548273. |
| 89 CHECK(handler_function_value->IsExternal()); | 107 CHECK(handler_function_value->IsExternal()); |
| 90 static_cast<HandlerFunction*>( | 108 static_cast<HandlerFunctionCheck*>( |
| 91 handler_function_value.As<v8::External>()->Value())->Run(args); | 109 handler_function_value.As<v8::External>()->Value())->Run(args, context); |
| 92 | 110 |
| 93 // Verify that the return value, if any, is accessible by the context. | 111 // Verify that the return value, if any, is accessible by the context. |
| 94 v8::ReturnValue<v8::Value> ret = args.GetReturnValue(); | 112 v8::ReturnValue<v8::Value> ret = args.GetReturnValue(); |
| 95 v8::Local<v8::Value> ret_value = ret.Get(); | 113 v8::Local<v8::Value> ret_value = ret.Get(); |
| 96 if (ret_value->IsObject() && !ret_value->IsNull() && | 114 if (ret_value->IsObject() && !ret_value->IsNull() && |
| 97 !ContextCanAccessObject(context, v8::Local<v8::Object>::Cast(ret_value), | 115 !ContextCanAccessObject(context, v8::Local<v8::Object>::Cast(ret_value), |
| 98 true)) { | 116 true)) { |
| 99 NOTREACHED() << "Insecure return value"; | 117 NOTREACHED() << "Insecure return value"; |
| 100 ret.SetUndefined(); | 118 ret.SetUndefined(); |
| 101 } | 119 } |
| 102 } | 120 } |
| 103 | 121 |
| 104 void ObjectBackedNativeHandler::RouteFunction( | 122 void ObjectBackedNativeHandler::RouteFunction( |
| 105 const std::string& name, | 123 const std::string& name, |
| 106 const HandlerFunction& handler_function) { | 124 const HandlerFunction& handler_function) { |
| 107 RouteFunction(name, "", handler_function); | 125 RouteFunction(name, std::vector<std::string>(), handler_function); |
| 108 } | 126 } |
| 109 | 127 |
| 110 void ObjectBackedNativeHandler::RouteFunction( | 128 void ObjectBackedNativeHandler::RouteFunction( |
| 111 const std::string& name, | 129 const std::string& name, |
| 112 const std::string& feature_name, | 130 const std::string& feature_name, |
| 113 const HandlerFunction& handler_function) { | 131 const HandlerFunction& handler_function) { |
| 132 RouteFunction( | |
| 133 name, std::vector<std::string>(1, feature_name), handler_function); | |
| 134 } | |
| 135 | |
| 136 void ObjectBackedNativeHandler::RouteFunction( | |
| 137 const std::string& name, | |
| 138 const std::vector<std::string>& features, | |
| 139 const HandlerFunction& handler_function) { | |
| 114 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | 140 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| 115 v8::HandleScope handle_scope(isolate); | 141 v8::HandleScope handle_scope(isolate); |
| 116 v8::Context::Scope context_scope(context_->v8_context()); | 142 v8::Context::Scope context_scope(context_->v8_context()); |
| 117 | 143 |
| 118 v8::Local<v8::Object> data = v8::Object::New(isolate); | 144 v8::Local<v8::Object> data = v8::Object::New(isolate); |
| 145 HandlerFunctionCheck check = | |
| 146 base::Bind(&ValidateAndCall, handler_function, features); | |
|
Devlin
2016/05/05 22:20:22
This callback trickery struck me as easier than st
| |
| 119 SetPrivate(data, kHandlerFunction, | 147 SetPrivate(data, kHandlerFunction, |
| 120 v8::External::New(isolate, new HandlerFunction(handler_function))); | 148 v8::External::New(isolate, new HandlerFunctionCheck(check))); |
| 121 DCHECK(feature_name.empty() || | |
| 122 ExtensionAPI::GetSharedInstance()->GetFeatureDependency(feature_name)) | |
| 123 << feature_name; | |
| 124 SetPrivate(data, kFeatureName, | |
| 125 v8_helpers::ToV8StringUnsafe(isolate, feature_name)); | |
| 126 v8::Local<v8::FunctionTemplate> function_template = | 149 v8::Local<v8::FunctionTemplate> function_template = |
| 127 v8::FunctionTemplate::New(isolate, Router, data); | 150 v8::FunctionTemplate::New(isolate, Router, data); |
| 128 v8::Local<v8::ObjectTemplate>::New(isolate, object_template_) | 151 v8::Local<v8::ObjectTemplate>::New(isolate, object_template_) |
| 129 ->Set(isolate, name.c_str(), function_template); | 152 ->Set(isolate, name.c_str(), function_template); |
| 130 router_data_.Append(data); | 153 router_data_.Append(data); |
| 131 } | 154 } |
| 132 | 155 |
| 133 v8::Isolate* ObjectBackedNativeHandler::GetIsolate() const { | 156 v8::Isolate* ObjectBackedNativeHandler::GetIsolate() const { |
| 134 return context_->isolate(); | 157 return context_->isolate(); |
| 135 } | 158 } |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 v8::Local<v8::Object> obj, | 240 v8::Local<v8::Object> obj, |
| 218 const char* key) { | 241 const char* key) { |
| 219 obj->DeletePrivate(context, | 242 obj->DeletePrivate(context, |
| 220 v8::Private::ForApi( | 243 v8::Private::ForApi( |
| 221 context->GetIsolate(), | 244 context->GetIsolate(), |
| 222 v8::String::NewFromUtf8(context->GetIsolate(), key))) | 245 v8::String::NewFromUtf8(context->GetIsolate(), key))) |
| 223 .FromJust(); | 246 .FromJust(); |
| 224 } | 247 } |
| 225 | 248 |
| 226 } // namespace extensions | 249 } // namespace extensions |
| OLD | NEW |