// 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_; // The callback which is passed to |handler|. void HandlerCallback(v8::Isolate* isolate, v8::Local external, v8::Local state, mate::Arguments* args) { // Check if the callback has already been called. v8::Local called_symbol = mate::StringToSymbol(isolate, "called"); if (state->Has(called_symbol)) return; // no nothing else state->Set(called_symbol, v8::Boolean::New(isolate, true)); // If there is no argument passed then we failed. scoped_ptr holder( static_cast(external->Value())); CHECK(holder); v8::Local value; if (!args->GetNext(&value)) { holder->callback.Run(false, nullptr); return; } // Pass whatever user passed to the actaul request job. V8ValueConverter converter; v8::Local context = args->isolate()->GetCurrentContext(); scoped_ptr options(converter.FromV8Value(value, context)); holder->callback.Run(true, options.Pass()); } // func.bind(func, ...). 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), v8::Object::New(isolate)); } } // 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