| 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 // A wrapper around a handler function that will check if the context is allowed | |
| 26 // before passing the arguments to the handler function. | |
| 27 using HandlerFunctionCheck = | |
| 28 base::Callback<void(const v8::FunctionCallbackInfo<v8::Value>&, | |
| 29 const v8::Local<v8::Context>&)>; | |
| 30 | |
| 31 // Key for the base::Bound routed function. | 24 // Key for the base::Bound routed function. |
| 32 const char* kHandlerFunction = "handler_function"; | 25 const char* kHandlerFunction = "handler_function"; |
| 33 | 26 const char* kFeatureName = "feature_name"; |
| 34 // Checks whether a given |context| has any of the |allowed_features|. This | |
| 35 // treats an empty |allowed_features| as indicating that any context has access. | |
| 36 bool IsContextAllowed(const std::vector<std::string>& allowed_features, | |
| 37 const v8::Local<v8::Context>& context) { | |
| 38 // We can't access the ScriptContextSet on a worker thread. Luckily, we also | |
| 39 // don't inject many bindings into worker threads. | |
| 40 // TODO(devlin): Figure out a way around this. | |
| 41 if (content::WorkerThread::GetCurrentId() == 0) | |
| 42 return true; | |
| 43 if (allowed_features.empty()) | |
| 44 return true; | |
| 45 ScriptContext* script_context = | |
| 46 ScriptContextSet::GetContextByV8Context(context); | |
| 47 // TODO(devlin): Eventually, we should fail if script_context is null. | |
| 48 if (!script_context) | |
| 49 return true; | |
| 50 for (const std::string& feature : allowed_features) { | |
| 51 if (script_context->GetAvailability(feature).is_available()) | |
| 52 return true; | |
| 53 } | |
| 54 | |
| 55 return false; | |
| 56 } | |
| 57 | |
| 58 // Checks whether the given |context| has access to any of the necessary | |
| 59 // |allowed_features| and calls |function|. | |
| 60 void ValidateAndCall(const ObjectBackedNativeHandler::HandlerFunction& function, | |
| 61 const std::vector<std::string>& allowed_features, | |
| 62 const v8::FunctionCallbackInfo<v8::Value>& args, | |
| 63 const v8::Local<v8::Context>& context) { | |
| 64 if (!IsContextAllowed(allowed_features, context)) { | |
| 65 NOTREACHED(); | |
| 66 return; | |
| 67 } | |
| 68 function.Run(args); | |
| 69 } | |
| 70 | |
| 71 } // namespace | 27 } // namespace |
| 72 | 28 |
| 73 ObjectBackedNativeHandler::ObjectBackedNativeHandler(ScriptContext* context) | 29 ObjectBackedNativeHandler::ObjectBackedNativeHandler(ScriptContext* context) |
| 74 : router_data_(context->isolate()), | 30 : router_data_(context->isolate()), |
| 75 context_(context), | 31 context_(context), |
| 76 object_template_(context->isolate(), | 32 object_template_(context->isolate(), |
| 77 v8::ObjectTemplate::New(context->isolate())) { | 33 v8::ObjectTemplate::New(context->isolate())) { |
| 78 } | 34 } |
| 79 | 35 |
| 80 ObjectBackedNativeHandler::~ObjectBackedNativeHandler() { | 36 ObjectBackedNativeHandler::~ObjectBackedNativeHandler() { |
| 81 } | 37 } |
| 82 | 38 |
| 83 v8::Local<v8::Object> ObjectBackedNativeHandler::NewInstance() { | 39 v8::Local<v8::Object> ObjectBackedNativeHandler::NewInstance() { |
| 84 return v8::Local<v8::ObjectTemplate>::New(GetIsolate(), object_template_) | 40 return v8::Local<v8::ObjectTemplate>::New(GetIsolate(), object_template_) |
| 85 ->NewInstance(); | 41 ->NewInstance(); |
| 86 } | 42 } |
| 87 | 43 |
| 88 // static | 44 // static |
| 89 void ObjectBackedNativeHandler::Router( | 45 void ObjectBackedNativeHandler::Router( |
| 90 const v8::FunctionCallbackInfo<v8::Value>& args) { | 46 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 91 v8::Isolate* isolate = args.GetIsolate(); | 47 v8::Isolate* isolate = args.GetIsolate(); |
| 92 v8::HandleScope handle_scope(isolate); | 48 v8::HandleScope handle_scope(isolate); |
| 93 v8::Local<v8::Object> data = args.Data().As<v8::Object>(); | 49 v8::Local<v8::Object> data = args.Data().As<v8::Object>(); |
| 94 v8::Local<v8::Context> context = isolate->GetCurrentContext(); | 50 v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 95 | 51 |
| 96 v8::Local<v8::Value> handler_function_value; | 52 v8::Local<v8::Value> handler_function_value; |
| 53 v8::Local<v8::Value> feature_name_value; |
| 97 // See comment in header file for why we do this. | 54 // See comment in header file for why we do this. |
| 98 if (!GetPrivate(context, data, kHandlerFunction, &handler_function_value) || | 55 if (!GetPrivate(context, data, kHandlerFunction, &handler_function_value) || |
| 99 handler_function_value->IsUndefined()) { | 56 handler_function_value->IsUndefined() || |
| 57 !GetPrivate(context, data, kFeatureName, &feature_name_value) || |
| 58 !feature_name_value->IsString()) { |
| 100 ScriptContext* script_context = | 59 ScriptContext* script_context = |
| 101 ScriptContextSet::GetContextByV8Context(context); | 60 ScriptContextSet::GetContextByV8Context(context); |
| 102 console::Error(script_context ? script_context->GetRenderFrame() : nullptr, | 61 console::Error(script_context ? script_context->GetRenderFrame() : nullptr, |
| 103 "Extension view no longer exists"); | 62 "Extension view no longer exists"); |
| 104 return; | 63 return; |
| 105 } | 64 } |
| 106 | 65 |
| 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 } |
| 107 // This CHECK is *important*. Otherwise, we'll go around happily executing | 87 // This CHECK is *important*. Otherwise, we'll go around happily executing |
| 108 // something random. See crbug.com/548273. | 88 // something random. See crbug.com/548273. |
| 109 CHECK(handler_function_value->IsExternal()); | 89 CHECK(handler_function_value->IsExternal()); |
| 110 static_cast<HandlerFunctionCheck*>( | 90 static_cast<HandlerFunction*>( |
| 111 handler_function_value.As<v8::External>()->Value())->Run(args, context); | 91 handler_function_value.As<v8::External>()->Value())->Run(args); |
| 112 | 92 |
| 113 // Verify that the return value, if any, is accessible by the context. | 93 // Verify that the return value, if any, is accessible by the context. |
| 114 v8::ReturnValue<v8::Value> ret = args.GetReturnValue(); | 94 v8::ReturnValue<v8::Value> ret = args.GetReturnValue(); |
| 115 v8::Local<v8::Value> ret_value = ret.Get(); | 95 v8::Local<v8::Value> ret_value = ret.Get(); |
| 116 if (ret_value->IsObject() && !ret_value->IsNull() && | 96 if (ret_value->IsObject() && !ret_value->IsNull() && |
| 117 !ContextCanAccessObject(context, v8::Local<v8::Object>::Cast(ret_value), | 97 !ContextCanAccessObject(context, v8::Local<v8::Object>::Cast(ret_value), |
| 118 true)) { | 98 true)) { |
| 119 NOTREACHED() << "Insecure return value"; | 99 NOTREACHED() << "Insecure return value"; |
| 120 ret.SetUndefined(); | 100 ret.SetUndefined(); |
| 121 } | 101 } |
| 122 } | 102 } |
| 123 | 103 |
| 124 void ObjectBackedNativeHandler::RouteFunction( | 104 void ObjectBackedNativeHandler::RouteFunction( |
| 125 const std::string& name, | 105 const std::string& name, |
| 126 const HandlerFunction& handler_function) { | 106 const HandlerFunction& handler_function) { |
| 127 RouteFunction(name, std::vector<std::string>(), handler_function); | 107 RouteFunction(name, "", handler_function); |
| 128 } | 108 } |
| 129 | 109 |
| 130 void ObjectBackedNativeHandler::RouteFunction( | 110 void ObjectBackedNativeHandler::RouteFunction( |
| 131 const std::string& name, | 111 const std::string& name, |
| 132 const std::string& feature_name, | 112 const std::string& feature_name, |
| 133 const HandlerFunction& handler_function) { | 113 const HandlerFunction& handler_function) { |
| 134 RouteFunction( | |
| 135 name, std::vector<std::string>(1, feature_name), handler_function); | |
| 136 } | |
| 137 | |
| 138 void ObjectBackedNativeHandler::RouteFunction( | |
| 139 const std::string& name, | |
| 140 const std::vector<std::string>& features, | |
| 141 const HandlerFunction& handler_function) { | |
| 142 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | 114 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| 143 v8::HandleScope handle_scope(isolate); | 115 v8::HandleScope handle_scope(isolate); |
| 144 v8::Context::Scope context_scope(context_->v8_context()); | 116 v8::Context::Scope context_scope(context_->v8_context()); |
| 145 | 117 |
| 146 v8::Local<v8::Object> data = v8::Object::New(isolate); | 118 v8::Local<v8::Object> data = v8::Object::New(isolate); |
| 147 HandlerFunctionCheck check = | |
| 148 base::Bind(&ValidateAndCall, handler_function, features); | |
| 149 SetPrivate(data, kHandlerFunction, | 119 SetPrivate(data, kHandlerFunction, |
| 150 v8::External::New(isolate, new HandlerFunctionCheck(check))); | 120 v8::External::New(isolate, new HandlerFunction(handler_function))); |
| 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)); |
| 151 v8::Local<v8::FunctionTemplate> function_template = | 126 v8::Local<v8::FunctionTemplate> function_template = |
| 152 v8::FunctionTemplate::New(isolate, Router, data); | 127 v8::FunctionTemplate::New(isolate, Router, data); |
| 153 v8::Local<v8::ObjectTemplate>::New(isolate, object_template_) | 128 v8::Local<v8::ObjectTemplate>::New(isolate, object_template_) |
| 154 ->Set(isolate, name.c_str(), function_template); | 129 ->Set(isolate, name.c_str(), function_template); |
| 155 router_data_.Append(data); | 130 router_data_.Append(data); |
| 156 } | 131 } |
| 157 | 132 |
| 158 v8::Isolate* ObjectBackedNativeHandler::GetIsolate() const { | 133 v8::Isolate* ObjectBackedNativeHandler::GetIsolate() const { |
| 159 return context_->isolate(); | 134 return context_->isolate(); |
| 160 } | 135 } |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 v8::Local<v8::Object> obj, | 217 v8::Local<v8::Object> obj, |
| 243 const char* key) { | 218 const char* key) { |
| 244 obj->DeletePrivate(context, | 219 obj->DeletePrivate(context, |
| 245 v8::Private::ForApi( | 220 v8::Private::ForApi( |
| 246 context->GetIsolate(), | 221 context->GetIsolate(), |
| 247 v8::String::NewFromUtf8(context->GetIsolate(), key))) | 222 v8::String::NewFromUtf8(context->GetIsolate(), key))) |
| 248 .FromJust(); | 223 .FromJust(); |
| 249 } | 224 } |
| 250 | 225 |
| 251 } // namespace extensions | 226 } // namespace extensions |
| OLD | NEW |