// Copyright (c) 2015 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #include "atom/browser/net/js_asker.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/v8_value_converter.h" #include "native_mate/function_template.h" namespace atom { namespace internal { namespace { struct CallbackHolder { ResponseCallback callback; }; // Cached JavaScript version of |HandlerCallback|. v8::Persistent g_handler_callback_; // Cached C++ version of |Function.prototype.bind|. base::Callback( v8::Local, v8::Local, v8::Local)> g_bind; // The callback which is passed to |handler|. void HandlerCallback(v8::Local external, mate::Arguments* args) { scoped_ptr holder( static_cast(external->Value())); CHECK(holder); v8::Local value; if (!args->GetNext(&value)) { holder->callback.Run(false, nullptr); return; } V8ValueConverter converter; v8::Local context = args->isolate()->GetCurrentContext(); scoped_ptr options(converter.FromV8Value(value, context)); holder->callback.Run(true, options.Pass()); } // func.bind(...). template v8::Local BindFunctionWith(v8::Isolate* isolate, v8::Local context, v8::Local func, ArgTypes... args) { v8::MaybeLocal bind = func->Get(mate::StringToV8(isolate, "bind")); CHECK(!bind.IsEmpty()); v8::Local bind_func = v8::Local::Cast(bind.ToLocalChecked()); std::vector> converted = { func, mate::ConvertToV8(isolate, args)..., }; return bind_func->Call( context, func, converted.size(), &converted.front()).ToLocalChecked(); } // Generate the callback that will be passed to |handler|. v8::MaybeLocal GenerateCallback(v8::Isolate* isolate, v8::Local context, const ResponseCallback& callback) { // The FunctionTemplate is cached. if (g_handler_callback_.IsEmpty()) g_handler_callback_.Reset( isolate, mate::CreateFunctionTemplate(isolate, base::Bind(&HandlerCallback))); v8::Local handler_callback = v8::Local::New(isolate, g_handler_callback_); CallbackHolder* holder = new CallbackHolder; holder->callback = callback; return BindFunctionWith(isolate, context, handler_callback->GetFunction(), v8::External::New(isolate, holder)); } } // namespace void AskForOptions(v8::Isolate* isolate, const JavaScriptHandler& handler, net::URLRequest* request, const ResponseCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); v8::Locker locker(isolate); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); v8::Context::Scope context_scope(context); // We don't convert the callback to C++ directly because creating // FunctionTemplate will cause memory leak since V8 never releases it. So we // have to create the function object in JavaScript to work around it. v8::MaybeLocal wrapped_callback = GenerateCallback( isolate, context, callback); if (wrapped_callback.IsEmpty()) { callback.Run(false, nullptr); return; } handler.Run(request, wrapped_callback.ToLocalChecked()); } } // namespace internal } // namespace atom