From 6f83977f47f084120e5488a0650af6aeafc0b543 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 29 Apr 2019 11:37:45 +0900 Subject: [PATCH] feat: migrate protocol module to NetworkService (Part 2) (#17965) * Pass protocol type and handler to factory * Add converter for network::ResourceRequest * Implement Buffer and String protocol handler * Implement file protocol --- atom/browser/api/atom_api_protocol_ns.cc | 35 ++-- atom/browser/api/atom_api_protocol_ns.h | 22 ++- atom/browser/net/atom_url_loader_factory.cc | 175 ++++++++++++++++-- atom/browser/net/atom_url_loader_factory.h | 46 ++++- .../native_mate_converters/net_converter.cc | 17 ++ .../native_mate_converters/net_converter.h | 10 + 6 files changed, 266 insertions(+), 39 deletions(-) diff --git a/atom/browser/api/atom_api_protocol_ns.cc b/atom/browser/api/atom_api_protocol_ns.cc index b6382eb5e9e8..03227afcd806 100644 --- a/atom/browser/api/atom_api_protocol_ns.cc +++ b/atom/browser/api/atom_api_protocol_ns.cc @@ -7,9 +7,8 @@ #include #include "atom/browser/atom_browser_context.h" -#include "atom/browser/net/atom_url_loader_factory.h" -#include "atom/common/native_mate_converters/callback.h" -#include "atom/common/native_mate_converters/value_converter.h" +#include "atom/common/native_mate_converters/net_converter.h" +#include "atom/common/native_mate_converters/once_callback.h" #include "atom/common/promise_util.h" #include "base/stl_util.h" @@ -48,19 +47,20 @@ ProtocolNS::~ProtocolNS() = default; void ProtocolNS::RegisterURLLoaderFactories( content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) { - for (const auto& it : handlers_) - factories->emplace(it.first, std::make_unique()); + for (const auto& it : handlers_) { + factories->emplace(it.first, std::make_unique( + it.second.first, it.second.second)); + } } -int ProtocolNS::RegisterProtocol(const std::string& scheme, - const Handler& handler, - mate::Arguments* args) { +ProtocolError ProtocolNS::RegisterProtocol(ProtocolType type, + const std::string& scheme, + const ProtocolHandler& handler) { ProtocolError error = PROTOCOL_OK; if (!base::ContainsKey(handlers_, scheme)) - handlers_[scheme] = handler; + handlers_[scheme] = std::make_pair(type, handler); else error = PROTOCOL_REGISTERED; - HandleOptionalCallback(args, error); return error; } @@ -109,11 +109,16 @@ void ProtocolNS::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "Protocol")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) - .SetMethod("registerStringProtocol", &ProtocolNS::RegisterProtocol) - .SetMethod("registerBufferProtocol", &ProtocolNS::RegisterProtocol) - .SetMethod("registerFileProtocol", &ProtocolNS::RegisterProtocol) - .SetMethod("registerHttpProtocol", &ProtocolNS::RegisterProtocol) - .SetMethod("registerStreamProtocol", &ProtocolNS::RegisterProtocol) + .SetMethod("registerStringProtocol", + &ProtocolNS::RegisterProtocolFor) + .SetMethod("registerBufferProtocol", + &ProtocolNS::RegisterProtocolFor) + .SetMethod("registerFileProtocol", + &ProtocolNS::RegisterProtocolFor) + .SetMethod("registerHttpProtocol", + &ProtocolNS::RegisterProtocolFor) + .SetMethod("registerStreamProtocol", + &ProtocolNS::RegisterProtocolFor) .SetMethod("unregisterProtocol", &ProtocolNS::UnregisterProtocol) .SetMethod("isProtocolRegistered", &ProtocolNS::IsProtocolRegistered) .SetMethod("isProtocolHandled", &ProtocolNS::IsProtocolHandled) diff --git a/atom/browser/api/atom_api_protocol_ns.h b/atom/browser/api/atom_api_protocol_ns.h index 97f4f67144b1..2917213cca49 100644 --- a/atom/browser/api/atom_api_protocol_ns.h +++ b/atom/browser/api/atom_api_protocol_ns.h @@ -7,8 +7,10 @@ #include #include +#include #include "atom/browser/api/trackable_object.h" +#include "atom/browser/net/atom_url_loader_factory.h" #include "content/public/browser/content_browser_client.h" #include "native_mate/dictionary.h" #include "native_mate/handle.h" @@ -46,25 +48,31 @@ class ProtocolNS : public mate::TrackableObject { ~ProtocolNS() override; // Callback types. - using Handler = - base::Callback)>; using CompletionCallback = base::Callback)>; // JS APIs. - int RegisterProtocol(const std::string& scheme, - const Handler& handler, - mate::Arguments* args); + ProtocolError RegisterProtocol(ProtocolType type, + const std::string& scheme, + const ProtocolHandler& handler); void UnregisterProtocol(const std::string& scheme, mate::Arguments* args); bool IsProtocolRegistered(const std::string& scheme); // Old async version of IsProtocolRegistered. v8::Local IsProtocolHandled(const std::string& scheme); + // Helper for converting old registration APIs to new RegisterProtocol API. + template + void RegisterProtocolFor(const std::string& scheme, + const ProtocolHandler& handler, + mate::Arguments* args) { + HandleOptionalCallback(args, RegisterProtocol(type, scheme, handler)); + } + // Be compatible with old interface, which accepts optional callback. void HandleOptionalCallback(mate::Arguments* args, ProtocolError error); - // scheme => handler. - std::map handlers_; + // scheme => (type, handler). + std::map> handlers_; }; } // namespace api diff --git a/atom/browser/net/atom_url_loader_factory.cc b/atom/browser/net/atom_url_loader_factory.cc index a9b57faa8c2f..e7d51ee07b1d 100644 --- a/atom/browser/net/atom_url_loader_factory.cc +++ b/atom/browser/net/atom_url_loader_factory.cc @@ -7,15 +7,24 @@ #include #include +#include "atom/common/native_mate_converters/file_path_converter.h" +#include "atom/common/native_mate_converters/net_converter.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/file_url_loader.h" +#include "gin/dictionary.h" +#include "net/base/filename_util.h" #include "services/network/public/cpp/url_loader_completion_status.h" #include "services/network/public/mojom/url_loader.mojom.h" +#include "atom/common/node_includes.h" + using content::BrowserThread; namespace atom { -AtomURLLoaderFactory::AtomURLLoaderFactory() {} +AtomURLLoaderFactory::AtomURLLoaderFactory(ProtocolType type, + const ProtocolHandler& handler) + : type_(type), handler_(handler), weak_factory_(this) {} AtomURLLoaderFactory::~AtomURLLoaderFactory() = default; @@ -27,23 +36,39 @@ void AtomURLLoaderFactory::CreateLoaderAndStart( const network::ResourceRequest& request, network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { - std::string contents = "Not Implemented"; + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - uint32_t size = base::saturated_cast(contents.size()); - mojo::DataPipe pipe(size); - MojoResult result = pipe.producer_handle->WriteData( - contents.data(), &size, MOJO_WRITE_DATA_FLAG_NONE); - if (result != MOJO_RESULT_OK || size < contents.size()) { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); - return; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Locker locker(isolate); + v8::HandleScope handle_scope(isolate); + v8::Local context = isolate->GetCurrentContext(); + v8::Context::Scope context_scope(context); + + switch (type_) { + case ProtocolType::kBuffer: + handler_.Run(request, + base::BindOnce(&AtomURLLoaderFactory::SendResponseBuffer, + weak_factory_.GetWeakPtr(), std::move(client), + isolate)); + break; + case ProtocolType::kString: + handler_.Run(request, + base::BindOnce(&AtomURLLoaderFactory::SendResponseString, + weak_factory_.GetWeakPtr(), std::move(client), + isolate)); + break; + case ProtocolType::kFile: + handler_.Run(request, + base::BindOnce(&AtomURLLoaderFactory::SendResponseFile, + weak_factory_.GetWeakPtr(), std::move(loader), + request, std::move(client), isolate)); + break; + default: { + std::string contents = "Not Implemented"; + SendContents(std::move(client), "text/html", "utf-8", contents.data(), + contents.size()); + } } - - network::ResourceResponseHead head; - head.mime_type = "text/html"; - head.charset = "utf-8"; - client->OnReceiveResponse(head); - client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle)); - client->OnComplete(network::URLLoaderCompletionStatus(net::OK)); } void AtomURLLoaderFactory::Clone( @@ -51,4 +76,122 @@ void AtomURLLoaderFactory::Clone( bindings_.AddBinding(this, std::move(request)); } +void AtomURLLoaderFactory::SendResponseBuffer( + network::mojom::URLLoaderClientPtr client, + v8::Isolate* isolate, + v8::Local response) { + if (HandleError(&client, isolate, response)) + return; + + std::string mime_type = "text/html"; + std::string charset = "utf-8"; + v8::Local buffer; + if (node::Buffer::HasInstance(response)) { + buffer = response; + } else if (response->IsObject()) { + gin::Dictionary dict( + isolate, + response->ToObject(isolate->GetCurrentContext()).ToLocalChecked()); + dict.Get("mimeType", &mime_type); + dict.Get("charset", &charset); + dict.Get("data", &buffer); + if (!node::Buffer::HasInstance(response)) + buffer = v8::Local(); + } + + if (buffer.IsEmpty()) { + network::URLLoaderCompletionStatus status; + status.error_code = net::ERR_NOT_IMPLEMENTED; + client->OnComplete(status); + return; + } + + SendContents(std::move(client), std::move(mime_type), std::move(charset), + node::Buffer::Data(buffer), node::Buffer::Length(buffer)); +} + +void AtomURLLoaderFactory::SendResponseString( + network::mojom::URLLoaderClientPtr client, + v8::Isolate* isolate, + v8::Local response) { + if (HandleError(&client, isolate, response)) + return; + + std::string mime_type = "text/html"; + std::string charset = "utf-8"; + std::string contents; + if (response->IsString()) { + contents = gin::V8ToString(isolate, response); + } else if (response->IsObject()) { + gin::Dictionary dict( + isolate, + response->ToObject(isolate->GetCurrentContext()).ToLocalChecked()); + dict.Get("mimeType", &mime_type); + dict.Get("charset", &charset); + dict.Get("data", &contents); + } + SendContents(std::move(client), std::move(mime_type), std::move(charset), + contents.data(), contents.size()); +} + +void AtomURLLoaderFactory::SendResponseFile( + network::mojom::URLLoaderRequest loader, + network::ResourceRequest request, + network::mojom::URLLoaderClientPtr client, + v8::Isolate* isolate, + v8::Local response) { + if (HandleError(&client, isolate, response)) + return; + + base::FilePath path; + if (!mate::ConvertFromV8(isolate, response, &path)) { + network::URLLoaderCompletionStatus status; + status.error_code = net::ERR_NOT_IMPLEMENTED; + client->OnComplete(status); + return; + } + + request.url = net::FilePathToFileURL(path); + content::CreateFileURLLoader(request, std::move(loader), std::move(client), + nullptr, false); +} + +bool AtomURLLoaderFactory::HandleError( + network::mojom::URLLoaderClientPtr* client, + v8::Isolate* isolate, + v8::Local response) { + if (!response->IsObject()) + return false; + v8::Local obj = + response->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); + network::URLLoaderCompletionStatus status; + if (!gin::Dictionary(isolate, obj).Get("error", &status.error_code)) + return false; + std::move(*client)->OnComplete(status); + return true; +} + +void AtomURLLoaderFactory::SendContents( + network::mojom::URLLoaderClientPtr client, + std::string mime_type, + std::string charset, + const char* data, + size_t ssize) { + uint32_t size = base::saturated_cast(ssize); + mojo::DataPipe pipe(size); + MojoResult result = + pipe.producer_handle->WriteData(data, &size, MOJO_WRITE_DATA_FLAG_NONE); + if (result != MOJO_RESULT_OK || size < ssize) { + client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + return; + } + + network::ResourceResponseHead head; + head.mime_type = std::move(mime_type); + head.charset = std::move(charset); + client->OnReceiveResponse(head); + client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle)); + client->OnComplete(network::URLLoaderCompletionStatus(net::OK)); +} + } // namespace atom diff --git a/atom/browser/net/atom_url_loader_factory.h b/atom/browser/net/atom_url_loader_factory.h index 21f49c7b5d17..367e418320e0 100644 --- a/atom/browser/net/atom_url_loader_factory.h +++ b/atom/browser/net/atom_url_loader_factory.h @@ -5,16 +5,34 @@ #ifndef ATOM_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_ #define ATOM_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_ +#include + +#include "base/memory/weak_ptr.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "net/url_request/url_request_job_factory.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "v8/include/v8.h" namespace atom { +// Old Protocol API can only serve one type of response for one scheme. +enum class ProtocolType { + kBuffer, + kString, + kFile, + kHttp, + kStream, + kFree, // special type for returning arbitrary type of response. +}; + +using SendResponseCallback = base::OnceCallback)>; +using ProtocolHandler = + base::Callback; + // Implementation of URLLoaderFactory. class AtomURLLoaderFactory : public network::mojom::URLLoaderFactory { public: - AtomURLLoaderFactory(); + AtomURLLoaderFactory(ProtocolType type, const ProtocolHandler& handler); ~AtomURLLoaderFactory() override; // network::mojom::URLLoaderFactory: @@ -29,11 +47,37 @@ class AtomURLLoaderFactory : public network::mojom::URLLoaderFactory { void Clone(network::mojom::URLLoaderFactoryRequest request) override; private: + void SendResponseBuffer(network::mojom::URLLoaderClientPtr client, + v8::Isolate* isolate, + v8::Local response); + void SendResponseString(network::mojom::URLLoaderClientPtr client, + v8::Isolate* isolate, + v8::Local response); + void SendResponseFile(network::mojom::URLLoaderRequest loader, + network::ResourceRequest request, + network::mojom::URLLoaderClientPtr client, + v8::Isolate* isolate, + v8::Local response); + + bool HandleError(network::mojom::URLLoaderClientPtr* client, + v8::Isolate* isolate, + v8::Local response); + void SendContents(network::mojom::URLLoaderClientPtr client, + std::string mime_type, + std::string charset, + const char* data, + size_t size); + // TODO(zcbenz): This comes from extensions/browser/extension_protocols.cc // but I don't know what it actually does, find out the meanings of |Clone| // and |bindings_| and add comments for them. mojo::BindingSet bindings_; + ProtocolType type_; + ProtocolHandler handler_; + + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(AtomURLLoaderFactory); }; diff --git a/atom/common/native_mate_converters/net_converter.cc b/atom/common/native_mate_converters/net_converter.cc index 91548e1406db..ff18d303790a 100644 --- a/atom/common/native_mate_converters/net_converter.cc +++ b/atom/common/native_mate_converters/net_converter.cc @@ -24,6 +24,7 @@ #include "net/cert/x509_util.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" +#include "services/network/public/cpp/resource_request.h" #include "storage/browser/blob/upload_blob_element_reader.h" namespace mate { @@ -223,6 +224,22 @@ bool Converter::FromV8( return true; } +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const network::ResourceRequest& val) { + mate::Dictionary dict(isolate, v8::Object::New(isolate)); + dict.Set("method", val.method); + dict.Set("url", val.url.spec()); + dict.Set("referrer", val.referrer.spec()); + mate::Dictionary headers(isolate, v8::Object::New(isolate)); + for (net::HttpRequestHeaders::Iterator it(val.headers); it.GetNext();) + headers.Set(it.name(), it.value()); + dict.Set("headers", headers); + // FIXME(zcbenz): Figure out how to support uploadData. + return dict.GetHandle(); +} + } // namespace mate namespace atom { diff --git a/atom/common/native_mate_converters/net_converter.h b/atom/common/native_mate_converters/net_converter.h index dea11386a7f0..b86c89bee0ed 100644 --- a/atom/common/native_mate_converters/net_converter.h +++ b/atom/common/native_mate_converters/net_converter.h @@ -21,6 +21,10 @@ class HttpResponseHeaders; struct CertPrincipal; } // namespace net +namespace network { +struct ResourceRequest; +} + namespace mate { template <> @@ -55,6 +59,12 @@ struct Converter { net::HttpResponseHeaders* out); }; +template <> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const network::ResourceRequest& val); +}; + } // namespace mate namespace atom {