diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index d7d308b306..18a3ccca57 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -7,15 +7,9 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" -#include "atom/browser/api/atom_api_session.h" -#include "atom/browser/net/adapter_request_job.h" -#include "atom/browser/net/atom_url_request_job_factory.h" +#include "atom/browser/net/url_request_string_job.h" #include "atom/common/native_mate_converters/callback.h" -#include "atom/common/native_mate_converters/file_path_converter.h" -#include "atom/common/native_mate_converters/gurl_converter.h" -#include "content/public/browser/browser_thread.h" #include "native_mate/dictionary.h" -#include "net/url_request/url_request_context.h" #include "atom/common/node_includes.h" @@ -41,230 +35,18 @@ namespace atom { namespace api { -namespace { - -typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler; - -scoped_refptr BufferToRefCountedBytes( - v8::Local buf) { - scoped_refptr data(new base::RefCountedBytes); - auto start = reinterpret_cast(node::Buffer::Data(buf)); - data->data().assign(start, start + node::Buffer::Length(buf)); - return data; -} - -class CustomProtocolRequestJob : public AdapterRequestJob { - public: - CustomProtocolRequestJob(Protocol* registry, - ProtocolHandler* protocol_handler, - net::URLRequest* request, - net::NetworkDelegate* network_delegate) - : AdapterRequestJob(protocol_handler, request, network_delegate), - registry_(registry) { - } - - void GetJobTypeInUI(const Protocol::JsProtocolHandler& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(registry_->isolate()); - v8::HandleScope handle_scope(registry_->isolate()); - - // Call the JS handler. - v8::Local result = callback.Run(request()); - - // Determine the type of the job we are going to create. - if (result->IsString()) { - std::string data = mate::V8ToString(result); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateStringJobAndStart, - GetWeakPtr(), "text/plain", "UTF-8", data)); - return; - } else if (result->IsObject()) { - v8::Local obj = result->ToObject(); - mate::Dictionary dict(registry_->isolate(), obj); - std::string name = mate::V8ToString(obj->GetConstructorName()); - if (name == "RequestStringJob") { - std::string mime_type, charset, data; - dict.Get("mimeType", &mime_type); - dict.Get("charset", &charset); - dict.Get("data", &data); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateStringJobAndStart, - GetWeakPtr(), mime_type, charset, data)); - return; - } else if (name == "RequestBufferJob") { - std::string mime_type, encoding; - v8::Local buffer; - dict.Get("mimeType", &mime_type); - dict.Get("encoding", &encoding); - dict.Get("data", &buffer); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateBufferJobAndStart, - GetWeakPtr(), mime_type, encoding, - BufferToRefCountedBytes(buffer))); - return; - } else if (name == "RequestFileJob") { - base::FilePath path; - dict.Get("path", &path); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateFileJobAndStart, - GetWeakPtr(), path)); - return; - } else if (name == "RequestErrorJob") { - int error = net::ERR_NOT_IMPLEMENTED; - dict.Get("error", &error); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateErrorJobAndStart, - GetWeakPtr(), error)); - return; - } else if (name == "RequestHttpJob") { - GURL url; - std::string method, referrer; - dict.Get("url", &url); - dict.Get("method", &method); - dict.Get("referrer", &referrer); - - v8::Local value; - mate::Handle session; - scoped_refptr request_context_getter; - // "session" null -> pass nullptr; - // "session" a Session object -> use passed session. - // "session" undefined -> use current session; - if (dict.Get("session", &session)) - request_context_getter = - session->browser_context()->GetRequestContext(); - else if (dict.Get("session", &value) && value->IsNull()) - request_context_getter = nullptr; - else - request_context_getter = registry_->request_context_getter(); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateHttpJobAndStart, GetWeakPtr(), - request_context_getter, url, method, referrer)); - return; - } - } - - // Try the default protocol handler if we have. - if (default_protocol_handler()) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart, - GetWeakPtr())); - return; - } - - // Fallback to the not implemented error. - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateErrorJobAndStart, - GetWeakPtr(), net::ERR_NOT_IMPLEMENTED)); - } - - // AdapterRequestJob: - void GetJobType() override { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&CustomProtocolRequestJob::GetJobTypeInUI, - base::Unretained(this), - registry_->GetProtocolHandler(request()->url().scheme()))); - } - - private: - Protocol* registry_; // Weak, the Protocol class is expected to live forever. -}; - -// Always return the same CustomProtocolRequestJob for all requests, because -// the content API needs the ProtocolHandler to return a job immediately, and -// getting the real job from the JS requires asynchronous calls, so we have -// to create an adapter job first. -// Users can also pass an extra ProtocolHandler as the fallback one when -// registered handler doesn't want to deal with the request. -class CustomProtocolHandler : public ProtocolHandler { - public: - CustomProtocolHandler(api::Protocol* registry, - ProtocolHandler* protocol_handler = NULL) - : registry_(registry), protocol_handler_(protocol_handler) { - } - - net::URLRequestJob* MaybeCreateJob( - net::URLRequest* request, - net::NetworkDelegate* network_delegate) const override { - return new CustomProtocolRequestJob(registry_, protocol_handler_.get(), - request, network_delegate); - } - - ProtocolHandler* ReleaseDefaultProtocolHandler() { - return protocol_handler_.release(); - } - - ProtocolHandler* original_handler() { return protocol_handler_.get(); } - - private: - Protocol* registry_; // Weak, the Protocol class is expected to live forever. - scoped_ptr protocol_handler_; - - DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); -}; - -std::string ConvertErrorCode(int error_code) { - switch (error_code) { - case Protocol::ERR_SCHEME_REGISTERED: - return "The Scheme is already registered"; - case Protocol::ERR_SCHEME_UNREGISTERED: - return "The Scheme has not been registered"; - case Protocol::ERR_SCHEME_INTERCEPTED: - return "There is no protocol handler to intercept"; - case Protocol::ERR_SCHEME_UNINTERCEPTED: - return "The protocol is not intercepted"; - case Protocol::ERR_NO_SCHEME: - return "The Scheme does not exist."; - case Protocol::ERR_SCHEME: - return "Cannot intercept custom protocols"; - default: - NOTREACHED(); - return std::string(); - } -} - -} // namespace - Protocol::Protocol(AtomBrowserContext* browser_context) : request_context_getter_(browser_context->GetRequestContext()), job_factory_(browser_context->job_factory()) { CHECK(job_factory_); } -Protocol::JsProtocolHandler Protocol::GetProtocolHandler( - const std::string& scheme) { - return protocol_handlers_[scheme]; -} - -void Protocol::OnIOActionCompleted(const JsCompletionCallback& callback, - int error) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - - if (error) { - callback.Run(v8::Exception::Error( - mate::StringToV8(isolate(), ConvertErrorCode(error)))); - return; - } - - callback.Run(v8::Null(isolate())); -} - mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder( v8::Isolate* isolate) { return mate::ObjectTemplateBuilder(isolate) .SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes) - .SetMethod("isHandledProtocol", &Protocol::IsHandledProtocol) - .SetMethod("_registerProtocol", &Protocol::RegisterProtocol) - .SetMethod("_unregisterProtocol", &Protocol::UnregisterProtocol) - .SetMethod("_interceptProtocol", &Protocol::InterceptProtocol) - .SetMethod("_uninterceptProtocol", &Protocol::UninterceptProtocol); + .SetMethod("registerStringProtocol", + &Protocol::RegisterProtocol); } void Protocol::RegisterStandardSchemes( @@ -272,131 +54,28 @@ void Protocol::RegisterStandardSchemes( atom::AtomBrowserClient::SetCustomSchemes(schemes); } -void Protocol::IsHandledProtocol(const std::string& scheme, - const net::CompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&AtomURLRequestJobFactory::IsHandledProtocol, - base::Unretained(job_factory_), scheme), - callback); -} +void Protocol::OnIOCompleted( + const CompletionCallback& callback, ProtocolError error) { + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); -void Protocol::RegisterProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::RegisterProtocolInIO, - base::Unretained(this), scheme, handler), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); -} - -void Protocol::UnregisterProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::UnregisterProtocolInIO, - base::Unretained(this), scheme), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); -} - -void Protocol::InterceptProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::InterceptProtocolInIO, - base::Unretained(this), scheme, handler), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); -} - -void Protocol::UninterceptProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::UninterceptProtocolInIO, - base::Unretained(this), scheme), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); -} - -int Protocol::RegisterProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (ContainsKey(protocol_handlers_, scheme) || - job_factory_->IsHandledProtocol(scheme)) { - return ERR_SCHEME_REGISTERED; + if (error == PROTOCOL_OK) { + callback.Run(v8::Null(isolate())); + } else { + std::string str = ErrorCodeToString(error); + callback.Run(v8::Exception::Error(mate::StringToV8(isolate(), str))); } - - protocol_handlers_[scheme] = handler; - job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this)); - - return OK; } -int Protocol::UnregisterProtocolInIO(const std::string& scheme) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme)); - if (it == protocol_handlers_.end()) { - return ERR_SCHEME_UNREGISTERED; +std::string Protocol::ErrorCodeToString(ProtocolError error) { + switch (error) { + case PROTOCOL_FAIL: return "Failed to manipulate protocol factory"; + case PROTOCOL_REGISTERED: return "The scheme has been registred"; + case PROTOCOL_NOT_REGISTERED: return "The scheme has not been registred"; + case PROTOCOL_INTERCEPTED: return "The scheme has been intercepted"; + case PROTOCOL_NOT_INTERCEPTED: return "The scheme has not been intercepted"; + default: return "Unexpected error"; } - - protocol_handlers_.erase(it); - job_factory_->SetProtocolHandler(scheme, NULL); - - return OK; -} - -int Protocol::InterceptProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Force the request context to initialize, otherwise we might have nothing - // to intercept. - request_context_getter_->GetURLRequestContext(); - - if (!job_factory_->HasProtocolHandler(scheme)) - return ERR_NO_SCHEME; - - if (ContainsKey(protocol_handlers_, scheme)) - return ERR_SCHEME; - - protocol_handlers_[scheme] = handler; - ProtocolHandler* original_handler = job_factory_->GetProtocolHandler(scheme); - if (original_handler == nullptr) { - return ERR_SCHEME_INTERCEPTED; - } - - job_factory_->ReplaceProtocol( - scheme, new CustomProtocolHandler(this, original_handler)); - - return OK; -} - -int Protocol::UninterceptProtocolInIO(const std::string& scheme) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme)); - if (it == protocol_handlers_.end()) - return ERR_SCHEME_UNREGISTERED; - - protocol_handlers_.erase(it); - CustomProtocolHandler* handler = static_cast( - job_factory_->GetProtocolHandler(scheme)); - if (handler->original_handler() == nullptr) { - return ERR_SCHEME_UNINTERCEPTED; - } - - // Reset the protocol handler to the orignal one and delete current protocol - // handler. - ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler(); - delete job_factory_->ReplaceProtocol(scheme, original_handler); - - return OK; } // static diff --git a/atom/browser/api/atom_api_protocol.h b/atom/browser/api/atom_api_protocol.h index 4dec17a743..54dac9b13b 100644 --- a/atom/browser/api/atom_api_protocol.h +++ b/atom/browser/api/atom_api_protocol.h @@ -9,10 +9,11 @@ #include #include -#include "atom/browser/api/event_emitter.h" +#include "atom/browser/net/atom_url_request_job_factory.h" #include "base/callback.h" +#include "content/public/browser/browser_thread.h" #include "native_mate/handle.h" -#include "net/base/completion_callback.h" +#include "native_mate/wrappable.h" namespace net { class URLRequest; @@ -26,31 +27,15 @@ class AtomURLRequestJobFactory; namespace api { -class Protocol : public mate::EventEmitter { +class Protocol : public mate::Wrappable { public: - using JsProtocolHandler = - base::Callback(const net::URLRequest*)>; - using JsCompletionCallback = base::Callback)>; - - enum { - OK = 0, - ERR_SCHEME_REGISTERED, - ERR_SCHEME_UNREGISTERED, - ERR_SCHEME_INTERCEPTED, - ERR_SCHEME_UNINTERCEPTED, - ERR_NO_SCHEME, - ERR_SCHEME - }; + using Handler = + base::Callback)>; + using CompletionCallback = base::Callback)>; static mate::Handle Create( v8::Isolate* isolate, AtomBrowserContext* browser_context); - JsProtocolHandler GetProtocolHandler(const std::string& scheme); - - net::URLRequestContextGetter* request_context_getter() { - return request_context_getter_.get(); - } - protected: explicit Protocol(AtomBrowserContext* browser_context); @@ -59,48 +44,77 @@ class Protocol : public mate::EventEmitter { v8::Isolate* isolate); private: - typedef std::map ProtocolHandlersMap; + // Possible errors. + enum ProtocolError { + PROTOCOL_OK, // no error + PROTOCOL_FAIL, // operation failed, should never occur + PROTOCOL_REGISTERED, + PROTOCOL_NOT_REGISTERED, + PROTOCOL_INTERCEPTED, + PROTOCOL_NOT_INTERCEPTED, + }; - // Callback called after performing action on IO thread. - void OnIOActionCompleted(const JsCompletionCallback& callback, - int error); + // The protocol handler that will create a protocol handler for certain + // request job. + template + class CustomProtocolHandler + : public net::URLRequestJobFactory::ProtocolHandler { + public: + CustomProtocolHandler(v8::Isolate* isolate, const Handler& handler) + : isolate_(isolate), handler_(handler) {} + ~CustomProtocolHandler() override {} + + net::URLRequestJob* MaybeCreateJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + return new RequestJob(request, network_delegate, isolate_, handler_); + } + + private: + v8::Isolate* isolate_; + Protocol::Handler handler_; + + DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); + }; // Register schemes to standard scheme list. void RegisterStandardSchemes(const std::vector& schemes); - // Returns whether a scheme has been registered. - void IsHandledProtocol(const std::string& scheme, - const net::CompletionCallback& callback); - - // Register/unregister an networking |scheme| which would be handled by - // |callback|. + // Register the protocol with certain request job. + template void RegisterProtocol(v8::Isolate* isolate, const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback); - void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme, - const JsCompletionCallback& callback); + const Handler& handler, + const CompletionCallback& callback) { + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&Protocol::RegisterProtocolInIO, + base::Unretained(this), scheme, handler), + base::Bind(&Protocol::OnIOCompleted, + base::Unretained(this), callback)); + } + template + ProtocolError RegisterProtocolInIO(const std::string& scheme, + const Handler& handler) { + if (job_factory_->IsHandledProtocol(scheme)) + return PROTOCOL_REGISTERED; + scoped_ptr> protocol_handler( + new CustomProtocolHandler(isolate(), handler)); + if (job_factory_->SetProtocolHandler(scheme, protocol_handler.Pass())) + return PROTOCOL_OK; + else + return PROTOCOL_FAIL; + } - // Intercept/unintercept an existing protocol handler. - void InterceptProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback); - void UninterceptProtocol(v8::Isolate* isolate, const std::string& scheme, - const JsCompletionCallback& callback); + // Convert error code to JS exception and call the callback. + void OnIOCompleted(const CompletionCallback& callback, ProtocolError error); - // The networking related operations have to be done in IO thread. - int RegisterProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler); - int UnregisterProtocolInIO(const std::string& scheme); - int InterceptProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler); - int UninterceptProtocolInIO(const std::string& scheme); + // Convert error code to string. + std::string ErrorCodeToString(ProtocolError error); scoped_refptr request_context_getter_; - AtomURLRequestJobFactory* job_factory_; - ProtocolHandlersMap protocol_handlers_; + AtomURLRequestJobFactory* job_factory_; // weak ref. DISALLOW_COPY_AND_ASSIGN(Protocol); }; diff --git a/atom/browser/api/lib/protocol.coffee b/atom/browser/api/lib/protocol.coffee index 4a66152350..4a45c70250 100644 --- a/atom/browser/api/lib/protocol.coffee +++ b/atom/browser/api/lib/protocol.coffee @@ -2,63 +2,5 @@ app = require 'app' throw new Error('Can not initialize protocol module before app is ready') unless app.isReady() protocol = process.atomBinding('protocol').protocol -EventEmitter = require('events').EventEmitter - -protocol.__proto__ = EventEmitter.prototype - -GetWrappedCallback = (scheme, callback, notification) -> - wrappedCallback = (error) -> - if not callback? - if error - throw error - else - protocol.emit notification, scheme - else - callback error, scheme - -# Compatibility with old api. -protocol.registerProtocol = (scheme, handler, callback) -> - protocol._registerProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'registered') - -protocol.unregisterProtocol = (scheme, callback) -> - protocol._unregisterProtocol scheme, GetWrappedCallback(scheme, callback, 'unregistered') - -protocol.interceptProtocol = (scheme, handler, callback) -> - protocol._interceptProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'intercepted') - -protocol.uninterceptProtocol = (scheme, callback) -> - protocol._uninterceptProtocol scheme, GetWrappedCallback(scheme, callback, 'unintercepted') - -protocol.RequestStringJob = -class RequestStringJob - constructor: ({mimeType, charset, data}) -> - if typeof data isnt 'string' and not data instanceof Buffer - throw new TypeError('Data should be string or Buffer') - - @mimeType = mimeType ? 'text/plain' - @charset = charset ? 'UTF-8' - @data = String data - -protocol.RequestBufferJob = -class RequestBufferJob - constructor: ({mimeType, encoding, data}) -> - if not data instanceof Buffer - throw new TypeError('Data should be Buffer') - - @mimeType = mimeType ? 'application/octet-stream' - @encoding = encoding ? 'utf8' - @data = new Buffer(data) - -protocol.RequestFileJob = -class RequestFileJob - constructor: (@path) -> - -protocol.RequestErrorJob = -class RequestErrorJob - constructor: (@error) -> - -protocol.RequestHttpJob = -class RequestHttpJob - constructor: ({@session, @url, @method, @referrer}) -> module.exports = protocol diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index f116af0a66..8db71fd303 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -70,7 +70,7 @@ ProcessOwner GetProcessOwner(int process_id, // First search for NativeWindow. *window = NativeWindow::FromWebContents(web_contents); - if (window) + if (*window) return OWNER_NATIVE_WINDOW; // Then search for guest WebContents. diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index f04fbca747..583a6324a8 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -85,32 +85,37 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory( content::URLRequestInterceptorScopedVector* interceptors) { scoped_ptr job_factory(job_factory_); - for (content::ProtocolHandlerMap::iterator it = handlers->begin(); - it != handlers->end(); ++it) - job_factory->SetProtocolHandler(it->first, it->second.release()); + for (auto& it : *handlers) { + job_factory->SetProtocolHandler(it.first, + make_scoped_ptr(it.second.release())); + } handlers->clear(); job_factory->SetProtocolHandler( - url::kDataScheme, new net::DataProtocolHandler); + url::kDataScheme, make_scoped_ptr(new net::DataProtocolHandler)); job_factory->SetProtocolHandler( - url::kFileScheme, new asar::AsarProtocolHandler( + url::kFileScheme, make_scoped_ptr(new asar::AsarProtocolHandler( BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))); + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)))); job_factory->SetProtocolHandler( - url::kHttpScheme, new HttpProtocolHandler(url::kHttpScheme)); + url::kHttpScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kHttpScheme))); job_factory->SetProtocolHandler( - url::kHttpsScheme, new HttpProtocolHandler(url::kHttpsScheme)); + url::kHttpsScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kHttpsScheme))); job_factory->SetProtocolHandler( - url::kWsScheme, new HttpProtocolHandler(url::kWsScheme)); + url::kWsScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kWsScheme))); job_factory->SetProtocolHandler( - url::kWssScheme, new HttpProtocolHandler(url::kWssScheme)); + url::kWssScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kWssScheme))); - auto host_resolver = url_request_context_getter() - ->GetURLRequestContext() - ->host_resolver(); + auto host_resolver = + url_request_context_getter()->GetURLRequestContext()->host_resolver(); job_factory->SetProtocolHandler( - url::kFtpScheme, new net::FtpProtocolHandler( - new net::FtpNetworkLayer(host_resolver))); + url::kFtpScheme, + make_scoped_ptr(new net::FtpProtocolHandler( + new net::FtpNetworkLayer(host_resolver)))); // Set up interceptors in the reverse order. scoped_ptr top_job_factory = job_factory.Pass(); diff --git a/atom/browser/lib/chrome-extension.coffee b/atom/browser/lib/chrome-extension.coffee index 884a9e25d6..c29df5d96f 100644 --- a/atom/browser/lib/chrome-extension.coffee +++ b/atom/browser/lib/chrome-extension.coffee @@ -64,14 +64,14 @@ app.once 'ready', -> catch e # The chrome-extension: can map a extension URL request to real file path. - protocol.registerProtocol 'chrome-extension', (request) -> - parsed = url.parse request.url - return unless parsed.hostname and parsed.path? - return unless /extension-\d+/.test parsed.hostname + # protocol.registerProtocol 'chrome-extension', (request) -> + # parsed = url.parse request.url + # return unless parsed.hostname and parsed.path? + # return unless /extension-\d+/.test parsed.hostname - directory = getPathForHost parsed.hostname - return unless directory? - return new protocol.RequestFileJob(path.join(directory, parsed.path)) + # directory = getPathForHost parsed.hostname + # return unless directory? + # return new protocol.RequestFileJob(path.join(directory, parsed.path)) BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) -> @devToolsWebContents?.executeJavaScript "DevToolsAPI.addExtensions(#{JSON.stringify(extensionInfoArray)});" diff --git a/atom/browser/net/adapter_request_job.cc b/atom/browser/net/adapter_request_job.cc deleted file mode 100644 index ca7dcf2e56..0000000000 --- a/atom/browser/net/adapter_request_job.cc +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2013 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/adapter_request_job.h" - -#include "base/threading/sequenced_worker_pool.h" -#include "atom/browser/net/url_request_buffer_job.h" -#include "atom/browser/net/url_request_fetch_job.h" -#include "atom/browser/net/url_request_string_job.h" -#include "atom/browser/net/asar/url_request_asar_job.h" -#include "atom/common/asar/asar_util.h" -#include "content/public/browser/browser_thread.h" -#include "net/base/net_errors.h" -#include "net/url_request/url_request_error_job.h" -#include "net/url_request/url_request_file_job.h" - -namespace atom { - -AdapterRequestJob::AdapterRequestJob(ProtocolHandler* protocol_handler, - net::URLRequest* request, - net::NetworkDelegate* network_delegate) - : URLRequestJob(request, network_delegate), - protocol_handler_(protocol_handler), - weak_factory_(this) { -} - -void AdapterRequestJob::Start() { - DCHECK(!real_job_.get()); - GetJobType(); -} - -void AdapterRequestJob::Kill() { - if (real_job_.get()) // Kill could happen when real_job_ is created. - real_job_->Kill(); -} - -bool AdapterRequestJob::ReadRawData(net::IOBuffer* buf, - int buf_size, - int *bytes_read) { - DCHECK(!real_job_.get()); - // Read post-filtered data if available. - if (real_job_->HasFilter()) - return real_job_->Read(buf, buf_size, bytes_read); - else - return real_job_->ReadRawData(buf, buf_size, bytes_read); -} - -bool AdapterRequestJob::IsRedirectResponse(GURL* location, - int* http_status_code) { - DCHECK(!real_job_.get()); - return real_job_->IsRedirectResponse(location, http_status_code); -} - -net::Filter* AdapterRequestJob::SetupFilter() const { - DCHECK(!real_job_.get()); - return real_job_->SetupFilter(); -} - -bool AdapterRequestJob::GetMimeType(std::string* mime_type) const { - DCHECK(!real_job_.get()); - return real_job_->GetMimeType(mime_type); -} - -bool AdapterRequestJob::GetCharset(std::string* charset) { - DCHECK(!real_job_.get()); - return real_job_->GetCharset(charset); -} - -void AdapterRequestJob::GetResponseInfo(net::HttpResponseInfo* info) { - real_job_->GetResponseInfo(info); -} - -int AdapterRequestJob::GetResponseCode() const { - return real_job_->GetResponseCode(); -} - -void AdapterRequestJob::GetLoadTimingInfo( - net::LoadTimingInfo* load_timing_info) const { - real_job_->GetLoadTimingInfo(load_timing_info); -} - -base::WeakPtr AdapterRequestJob::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - -void AdapterRequestJob::CreateErrorJobAndStart(int error_code) { - real_job_ = new net::URLRequestErrorJob( - request(), network_delegate(), error_code); - real_job_->Start(); -} - -void AdapterRequestJob::CreateStringJobAndStart(const std::string& mime_type, - const std::string& charset, - const std::string& data) { - real_job_ = new URLRequestStringJob( - request(), network_delegate(), mime_type, charset, data); - real_job_->Start(); -} - -void AdapterRequestJob::CreateBufferJobAndStart( - const std::string& mime_type, - const std::string& charset, - scoped_refptr data) { - real_job_ = new URLRequestBufferJob( - request(), network_delegate(), mime_type, charset, data); - real_job_->Start(); -} - -void AdapterRequestJob::CreateFileJobAndStart(const base::FilePath& path) { - real_job_ = asar::CreateJobFromPath( - path, - request(), - network_delegate(), - content::BrowserThread::GetBlockingPool()-> - GetTaskRunnerWithShutdownBehavior( - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); - real_job_->Start(); -} - -void AdapterRequestJob::CreateHttpJobAndStart( - scoped_refptr request_context_getter, - const GURL& url, - const std::string& method, - const std::string& referrer) { - if (!url.is_valid()) { - CreateErrorJobAndStart(net::ERR_INVALID_URL); - return; - } - - real_job_ = new URLRequestFetchJob(request_context_getter, request(), - network_delegate(), url, method, referrer); - real_job_->Start(); -} - -void AdapterRequestJob::CreateJobFromProtocolHandlerAndStart() { - real_job_ = protocol_handler_->MaybeCreateJob(request(), - network_delegate()); - if (!real_job_.get()) { - CreateErrorJobAndStart(net::ERR_NOT_IMPLEMENTED); - } else { - // Copy headers from original request. - real_job_->SetExtraRequestHeaders(request()->extra_request_headers()); - real_job_->Start(); - } -} - -} // namespace atom diff --git a/atom/browser/net/adapter_request_job.h b/atom/browser/net/adapter_request_job.h deleted file mode 100644 index afb9d5f55d..0000000000 --- a/atom/browser/net/adapter_request_job.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_ -#define ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_ - -#include - -#include "base/memory/ref_counted_memory.h" -#include "base/memory/weak_ptr.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" -#include "net/url_request/url_request_job.h" -#include "net/url_request/url_request_job_factory.h" -#include "v8/include/v8.h" - -namespace base { -class FilePath; -} - -namespace atom { - -class AtomBrowserContext; - -// Ask JS which type of job it wants, and then delegate corresponding methods. -class AdapterRequestJob : public net::URLRequestJob { - public: - typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler; - - AdapterRequestJob(ProtocolHandler* protocol_handler, - net::URLRequest* request, - net::NetworkDelegate* network_delegate); - - public: - // net::URLRequestJob: - void Start() override; - void Kill() override; - bool ReadRawData(net::IOBuffer* buf, - int buf_size, - int *bytes_read) override; - bool IsRedirectResponse(GURL* location, - int* http_status_code) override; - net::Filter* SetupFilter() const override; - bool GetMimeType(std::string* mime_type) const override; - bool GetCharset(std::string* charset) override; - void GetResponseInfo(net::HttpResponseInfo* info) override; - int GetResponseCode() const override; - void GetLoadTimingInfo( - net::LoadTimingInfo* load_timing_info) const override; - - base::WeakPtr GetWeakPtr(); - - ProtocolHandler* default_protocol_handler() { return protocol_handler_; } - - // Override this function to determine which job should be started. - virtual void GetJobType() = 0; - - void CreateErrorJobAndStart(int error_code); - void CreateStringJobAndStart(const std::string& mime_type, - const std::string& charset, - const std::string& data); - void CreateBufferJobAndStart(const std::string& mime_type, - const std::string& charset, - scoped_refptr data); - void CreateFileJobAndStart(const base::FilePath& path); - void CreateHttpJobAndStart( - scoped_refptr request_context_getter, - const GURL& url, - const std::string& method, - const std::string& referrer); - void CreateJobFromProtocolHandlerAndStart(); - - private: - // The delegated request job. - scoped_refptr real_job_; - - // Default protocol handler. - ProtocolHandler* protocol_handler_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(AdapterRequestJob); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_ diff --git a/atom/browser/net/atom_url_request_job_factory.cc b/atom/browser/net/atom_url_request_job_factory.cc index 00942b2ad0..dbd8b4160c 100644 --- a/atom/browser/net/atom_url_request_job_factory.cc +++ b/atom/browser/net/atom_url_request_job_factory.cc @@ -23,10 +23,7 @@ AtomURLRequestJobFactory::~AtomURLRequestJobFactory() { } bool AtomURLRequestJobFactory::SetProtocolHandler( - const std::string& scheme, - ProtocolHandler* protocol_handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - + const std::string& scheme, scoped_ptr protocol_handler) { if (!protocol_handler) { ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme); if (it == protocol_handler_map_.end()) @@ -39,21 +36,17 @@ bool AtomURLRequestJobFactory::SetProtocolHandler( if (ContainsKey(protocol_handler_map_, scheme)) return false; - protocol_handler_map_[scheme] = protocol_handler; + protocol_handler_map_[scheme] = protocol_handler.release(); return true; } -ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol( - const std::string& scheme, - ProtocolHandler* protocol_handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(protocol_handler); - +scoped_ptr AtomURLRequestJobFactory::ReplaceProtocol( + const std::string& scheme, scoped_ptr protocol_handler) { if (!ContainsKey(protocol_handler_map_, scheme)) return nullptr; ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme]; - protocol_handler_map_[scheme] = protocol_handler; - return original_protocol_handler; + protocol_handler_map_[scheme] = protocol_handler.release(); + return make_scoped_ptr(original_protocol_handler); } ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler( diff --git a/atom/browser/net/atom_url_request_job_factory.h b/atom/browser/net/atom_url_request_job_factory.h index ce2a18a85e..dde36225b7 100644 --- a/atom/browser/net/atom_url_request_job_factory.h +++ b/atom/browser/net/atom_url_request_job_factory.h @@ -10,8 +10,7 @@ #include #include -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "net/url_request/url_request_job_factory.h" @@ -25,13 +24,13 @@ class AtomURLRequestJobFactory : public net::URLRequestJobFactory { // Sets the ProtocolHandler for a scheme. Returns true on success, false on // failure (a ProtocolHandler already exists for |scheme|). On success, // URLRequestJobFactory takes ownership of |protocol_handler|. - bool SetProtocolHandler(const std::string& scheme, - ProtocolHandler* protocol_handler); + bool SetProtocolHandler( + const std::string& scheme, scoped_ptr protocol_handler); // Intercepts the ProtocolHandler for a scheme. Returns the original protocol // handler on success, otherwise returns NULL. - ProtocolHandler* ReplaceProtocol(const std::string& scheme, - ProtocolHandler* protocol_handler); + scoped_ptr ReplaceProtocol( + const std::string& scheme, scoped_ptr protocol_handler); // Returns the protocol handler registered with scheme. ProtocolHandler* GetProtocolHandler(const std::string& scheme) const; diff --git a/atom/browser/net/js_asker.cc b/atom/browser/net/js_asker.cc new file mode 100644 index 0000000000..a24e910217 --- /dev/null +++ b/atom/browser/net/js_asker.cc @@ -0,0 +1,106 @@ +// 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 diff --git a/atom/browser/net/js_asker.h b/atom/browser/net/js_asker.h new file mode 100644 index 0000000000..a1bf972f63 --- /dev/null +++ b/atom/browser/net/js_asker.h @@ -0,0 +1,85 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_NET_JS_ASKER_H_ +#define ATOM_BROWSER_NET_JS_ASKER_H_ + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "base/values.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request_job.h" +#include "v8/include/v8.h" + +namespace atom { + +using JavaScriptHandler = + base::Callback)>; + +namespace internal { + +using ResponseCallback = + base::Callback options)>; + +// Ask handler for options in UI thread. +void AskForOptions(v8::Isolate* isolate, + const JavaScriptHandler& handler, + net::URLRequest* request, + const ResponseCallback& callback); + +} // namespace internal + +template +class JsAsker : public RequestJob { + public: + JsAsker(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + v8::Isolate* isolate, + const JavaScriptHandler& handler) + : RequestJob(request, network_delegate), + isolate_(isolate), + handler_(handler), + weak_factory_(this) {} + + // Subclass should do initailze work here. + virtual void StartAsync(scoped_ptr options) { + RequestJob::Start(); + } + + private: + // RequestJob: + void Start() override { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&internal::AskForOptions, + isolate_, + handler_, + RequestJob::request(), + base::Bind(&JsAsker::OnResponse, + weak_factory_.GetWeakPtr()))); + } + + // Called when the JS handler has sent the response, we need to decide whether + // to start, or fail the job. + void OnResponse(bool success, scoped_ptr options) { + if (success) { + StartAsync(options.Pass()); + } else { + RequestJob::NotifyStartError( + net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_NOT_IMPLEMENTED)); + } + } + + v8::Isolate* isolate_; + JavaScriptHandler handler_; + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(JsAsker); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NET_JS_ASKER_H_ diff --git a/atom/browser/net/url_request_string_job.cc b/atom/browser/net/url_request_string_job.cc index d0f205789a..1afa95e512 100644 --- a/atom/browser/net/url_request_string_job.cc +++ b/atom/browser/net/url_request_string_job.cc @@ -12,13 +12,25 @@ namespace atom { URLRequestStringJob::URLRequestStringJob(net::URLRequest* request, net::NetworkDelegate* network_delegate, - const std::string& mime_type, - const std::string& charset, - const std::string& data) - : net::URLRequestSimpleJob(request, network_delegate), - mime_type_(mime_type), - charset_(charset), - data_(data) { + v8::Isolate* isolate, + const JavaScriptHandler& handler) + : JsAsker(request, network_delegate, isolate, + handler) { +} + +void URLRequestStringJob::StartAsync(scoped_ptr options) { + if (options) { + if (options->IsType(base::Value::TYPE_DICTIONARY)) { + base::DictionaryValue* dict = + static_cast(options.get()); + dict->GetString("mimeType", &mime_type_); + dict->GetString("charset", &charset_); + dict->GetString("data", &data_); + } else if (options->IsType(base::Value::TYPE_STRING)) { + options->GetAsString(&data_); + } + } + net::URLRequestSimpleJob::Start(); } int URLRequestStringJob::GetData( diff --git a/atom/browser/net/url_request_string_job.h b/atom/browser/net/url_request_string_job.h index 7ad250466a..7a2e23acab 100644 --- a/atom/browser/net/url_request_string_job.h +++ b/atom/browser/net/url_request_string_job.h @@ -5,19 +5,22 @@ #ifndef ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_ #define ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_ +#include "atom/browser/net/js_asker.h" #include "net/url_request/url_request_simple_job.h" #include namespace atom { -class URLRequestStringJob : public net::URLRequestSimpleJob { +class URLRequestStringJob : public JsAsker { public: URLRequestStringJob(net::URLRequest* request, net::NetworkDelegate* network_delegate, - const std::string& mime_type, - const std::string& charset, - const std::string& data); + v8::Isolate* isolate, + const JavaScriptHandler& handler); + + // JsAsker: + void StartAsync(scoped_ptr options) override; // URLRequestSimpleJob: int GetData(std::string* mime_type, diff --git a/filenames.gypi b/filenames.gypi index 40af1ebb1f..2dc110ef85 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -146,8 +146,6 @@ 'atom/browser/native_window_mac.h', 'atom/browser/native_window_mac.mm', 'atom/browser/native_window_observer.h', - 'atom/browser/net/adapter_request_job.cc', - 'atom/browser/net/adapter_request_job.h', 'atom/browser/net/asar/asar_protocol_handler.cc', 'atom/browser/net/asar/asar_protocol_handler.h', 'atom/browser/net/asar/url_request_asar_job.cc', @@ -156,6 +154,8 @@ 'atom/browser/net/atom_url_request_job_factory.h', 'atom/browser/net/http_protocol_handler.cc', 'atom/browser/net/http_protocol_handler.h', + 'atom/browser/net/js_asker.cc', + 'atom/browser/net/js_asker.h', 'atom/browser/net/url_request_string_job.cc', 'atom/browser/net/url_request_string_job.h', 'atom/browser/net/url_request_buffer_job.cc', diff --git a/vendor/brightray b/vendor/brightray index 46b093d514..cb8f80f473 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 46b093d514750aee66092ec64defe7bd8e1bc74d +Subproject commit cb8f80f473a54fe1de72d478fd7809cf0c01fdb2