From 2b18cd33844a9a2967054ac54df23617ea04f6fd Mon Sep 17 00:00:00 2001 From: "ayush.k123" Date: Tue, 10 Jan 2023 09:21:29 +0530 Subject: [PATCH] [M108 Migration][API] Implement Intercept Request EWK APIs This patch implements Intercept Request EWK APIs. Reference: 1. https://review.tizen.org/gerrit/273043/ 2. https://review.tizen.org/gerrit/273467/ 3. https://review.tizen.org/gerrit/279298/ Change-Id: I9bee738d055289644ec3f78c1fc633bca7ed500f Signed-off-by: Ayush Kumar --- net/base/chunked_upload_data_stream.cc | 22 ++ net/base/chunked_upload_data_stream.h | 5 + net/base/elements_upload_data_stream.cc | 38 ++++ net/base/elements_upload_data_stream.h | 5 + net/base/upload_bytes_element_reader.cc | 16 ++ net/base/upload_bytes_element_reader.h | 5 + net/base/upload_data_stream.cc | 12 ++ net/base/upload_data_stream.h | 5 + net/base/upload_element_reader.cc | 16 ++ net/base/upload_element_reader.h | 5 + net/base/upload_file_element_reader.cc | 13 ++ net/base/upload_file_element_reader.h | 5 + tizen_src/ewk/efl_integration/BUILD.gn | 11 + .../browser/intercept_request_params.h | 25 +++ .../network_service/proxying_url_loader_efl.cc | 173 ++++++++++++++++ .../network_service/proxying_url_loader_efl.h | 80 ++++++++ .../proxying_url_loader_factory_efl.cc | 175 ++++++++++++++++ .../proxying_url_loader_factory_efl.h | 66 ++++++ .../ewk/efl_integration/browser_context_efl.cc | 26 ++- .../ewk/efl_integration/browser_context_efl.h | 21 ++ .../efl_integration/content_browser_client_efl.cc | 32 +++ .../efl_integration/content_browser_client_efl.h | 15 ++ tizen_src/ewk/efl_integration/eweb_context.cc | 14 ++ tizen_src/ewk/efl_integration/eweb_context.h | 5 + .../ewk/efl_integration/network_delegate_efl.cc | 19 +- .../ewk/efl_integration/network_delegate_efl.h | 26 ++- .../efl_integration/private/ewk_context_private.cc | 6 + .../efl_integration/private/ewk_context_private.h | 4 + .../private/ewk_intercept_request_private.cc | 222 +++++++++++++++++++++ .../private/ewk_intercept_request_private.h | 116 +++++++++++ .../ewk/efl_integration/public/ewk_context.cc | 8 + .../public/ewk_intercept_request.cc | 120 ++++++++--- .../url_request_context_getter_efl.cc | 26 ++- .../url_request_context_getter_efl.h | 9 + .../efl_integration/url_request_interceptor_efl.cc | 83 ++++++++ .../efl_integration/url_request_interceptor_efl.h | 36 ++++ .../ewk/efl_integration/url_request_job_efl.cc | 207 +++++++++++++++++++ .../ewk/efl_integration/url_request_job_efl.h | 94 +++++++++ .../efl_integration/web_contents_delegate_efl.cc | 7 + 39 files changed, 1730 insertions(+), 43 deletions(-) create mode 100644 tizen_src/ewk/efl_integration/browser/intercept_request_params.h create mode 100644 tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.cc create mode 100644 tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.h create mode 100644 tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.cc create mode 100644 tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.h create mode 100644 tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.cc create mode 100644 tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.h create mode 100644 tizen_src/ewk/efl_integration/url_request_interceptor_efl.cc create mode 100644 tizen_src/ewk/efl_integration/url_request_interceptor_efl.h create mode 100644 tizen_src/ewk/efl_integration/url_request_job_efl.cc create mode 100644 tizen_src/ewk/efl_integration/url_request_job_efl.h diff --git a/net/base/chunked_upload_data_stream.cc b/net/base/chunked_upload_data_stream.cc index 3c6521a..3b5aa8e 100644 --- a/net/base/chunked_upload_data_stream.cc +++ b/net/base/chunked_upload_data_stream.cc @@ -115,4 +115,26 @@ int ChunkedUploadDataStream::ReadChunk(IOBuffer* buf, int buf_len) { return bytes_read; } +#if BUILDFLAG(IS_EFL) +int64_t ChunkedUploadDataStream::GetSizeSync() const { + // See comment in ::DumpUploadData + LOG(ERROR) << "Trying to get size of unsupported ChunkedUploadDataStream"; + return -1; +} + +bool ChunkedUploadDataStream::DumpUploadData(std::string& data) const { + // DumpUploadData was added to support ewk_intercept_request_body_get API, + // which can be used to extract body of intercepted request. Chunked upload + // data stream has specific usage outside of chromium EFL port - see + // SetChunkedUpload in google_one_shot_remote_engine.cc and + // google_streaming_remote_engine.cc. + // We couldn't support it in a meaningful way, unless all_data_appended_ is + // true when we reach ewk intercept request callback in + // URLRequestInterceptorEFL::MaybeInterceptRequest. Otherwise we would get + // partial data, or no data at all. + LOG(ERROR) << "Trying to dump data from unsupported ChunkedUploadDataStream"; + return false; +} +#endif + } // namespace net diff --git a/net/base/chunked_upload_data_stream.h b/net/base/chunked_upload_data_stream.h index 36f87c4..ea0d5e9 100644 --- a/net/base/chunked_upload_data_stream.h +++ b/net/base/chunked_upload_data_stream.h @@ -78,6 +78,11 @@ class NET_EXPORT ChunkedUploadDataStream : public UploadDataStream { // Writers. void AppendData(const char* data, int data_len, bool is_done); +#if BUILDFLAG(IS_EFL) + int64_t GetSizeSync() const override; + bool DumpUploadData(std::string& data) const override; +#endif + private: // UploadDataStream implementation. int InitInternal(const NetLogWithSource& net_log) override; diff --git a/net/base/elements_upload_data_stream.cc b/net/base/elements_upload_data_stream.cc index f034db8..c2d3e51 100644 --- a/net/base/elements_upload_data_stream.cc +++ b/net/base/elements_upload_data_stream.cc @@ -148,4 +148,42 @@ void ElementsUploadDataStream::ProcessReadResult( } } +#if BUILDFLAG(IS_EFL) +int64_t ElementsUploadDataStream::GetSizeSync() const { + int64_t total_size = 0; + for (size_t i = 0; i < element_readers_.size(); ++i) { + int64_t element_size = element_readers_[i]->GetSizeSync(); + if (element_size < 0) + return -1; + total_size += element_size; + } + return total_size; +} + +bool ElementsUploadDataStream::DumpUploadData(std::string& data) const { + int64_t total_size = GetSizeSync(); + if (total_size < 0) + return false; + + data.clear(); + data.reserve(total_size); + for (size_t i = 0; i < element_readers_.size(); ++i) { + std::string reader_data; + if (element_readers_[i]->DumpReaderData(reader_data)) { + data += reader_data; + } else { + // If read fails, we pad result with null bytes to match behavior of + // ::ReadElements + // TODO(g.ludwikowsk): should we also do this if read doesn't fail, but + // we get too little data? Also we already return false if GetSizeSync + // fails. + LOG(ERROR) << "Read failed, padding result with zero"; + data += std::string(total_size - data.size(), '\0'); + break; + } + } + return true; +} +#endif + } // namespace net diff --git a/net/base/elements_upload_data_stream.h b/net/base/elements_upload_data_stream.h index 3b13c25..ef296a1 100644 --- a/net/base/elements_upload_data_stream.h +++ b/net/base/elements_upload_data_stream.h @@ -41,6 +41,11 @@ class NET_EXPORT ElementsUploadDataStream : public UploadDataStream { std::unique_ptr reader, int64_t identifier); +#if BUILDFLAG(IS_EFL) + int64_t GetSizeSync() const override; + bool DumpUploadData(std::string& data) const override; +#endif + private: // UploadDataStream implementation. bool IsInMemory() const override; diff --git a/net/base/upload_bytes_element_reader.cc b/net/base/upload_bytes_element_reader.cc index 19b195e..887c664 100644 --- a/net/base/upload_bytes_element_reader.cc +++ b/net/base/upload_bytes_element_reader.cc @@ -70,4 +70,20 @@ UploadOwnedBytesElementReader::CreateWithString(const std::string& string) { return std::make_unique(&data); } +#if BUILDFLAG(IS_EFL) +int64_t UploadBytesElementReader::GetSizeSync() const { + if (!bytes()) + return -1; + return length(); +} + +bool UploadBytesElementReader::DumpReaderData(std::string& data) const { + if (!bytes()) + return false; + + data = std::string(bytes(), bytes() + length()); + return true; +} +#endif + } // namespace net diff --git a/net/base/upload_bytes_element_reader.h b/net/base/upload_bytes_element_reader.h index bb8459b..54beba2 100644 --- a/net/base/upload_bytes_element_reader.h +++ b/net/base/upload_bytes_element_reader.h @@ -38,6 +38,11 @@ class NET_EXPORT UploadBytesElementReader : public UploadElementReader { int buf_length, CompletionOnceCallback callback) override; +#if BUILDFLAG(IS_EFL) + int64_t GetSizeSync() const override; + bool DumpReaderData(std::string& data) const override; +#endif + private: const char* const bytes_; const uint64_t length_; diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index 2038f39..44e620f 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -194,4 +194,16 @@ bool UploadDataStream::AllowHTTP1() const { return true; } +#if BUILDFLAG(IS_EFL) +int64_t UploadDataStream::GetSizeSync() const { + LOG(ERROR) << "Trying to get size of unsupported UploadDataStream"; + return -1; +} + +bool UploadDataStream::DumpUploadData(std::string& data) const { + LOG(ERROR) << "Trying to dump data from unsupported UploadDataStream"; + return false; +} +#endif + } // namespace net diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index ae538f8..65b88d4 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -106,6 +106,11 @@ class NET_EXPORT UploadDataStream { // Even if this is false but there is a QUIC/H2 stream, the upload is allowed. virtual bool AllowHTTP1() const; +#if BUILDFLAG(IS_EFL) + virtual int64_t GetSizeSync() const; + virtual bool DumpUploadData(std::string& data) const; +#endif + protected: // Must be called by subclasses when InitInternal and ReadInternal complete // asynchronously. diff --git a/net/base/upload_element_reader.cc b/net/base/upload_element_reader.cc index bc45006..b291dc5 100644 --- a/net/base/upload_element_reader.cc +++ b/net/base/upload_element_reader.cc @@ -4,6 +4,10 @@ #include "net/base/upload_element_reader.h" +#if BUILDFLAG(IS_EFL) +#include "base/logging.h" +#endif + namespace net { const UploadBytesElementReader* UploadElementReader::AsBytesReader() const { @@ -18,4 +22,16 @@ bool UploadElementReader::IsInMemory() const { return false; } +#if BUILDFLAG(IS_EFL) +int64_t UploadElementReader::GetSizeSync() const { + LOG(ERROR) << "Trying to get size of unsupported UploadElementReader"; + return -1; +} + +bool UploadElementReader::DumpReaderData(std::string& data) const { + LOG(ERROR) << "Trying to dump data from unsupported UploadElementReader"; + return false; +} +#endif + } // namespace net diff --git a/net/base/upload_element_reader.h b/net/base/upload_element_reader.h index 8877532..0770445 100644 --- a/net/base/upload_element_reader.h +++ b/net/base/upload_element_reader.h @@ -61,6 +61,11 @@ class NET_EXPORT UploadElementReader { virtual int Read(IOBuffer* buf, int buf_length, CompletionOnceCallback callback) = 0; + +#if BUILDFLAG(IS_EFL) + virtual int64_t GetSizeSync() const; + virtual bool DumpReaderData(std::string& data) const; +#endif }; } // namespace net diff --git a/net/base/upload_file_element_reader.cc b/net/base/upload_file_element_reader.cc index 8aabd90..decaa97 100644 --- a/net/base/upload_file_element_reader.cc +++ b/net/base/upload_file_element_reader.cc @@ -316,4 +316,17 @@ UploadFileElementReader::ScopedOverridingContentLengthForTests:: overriding_content_length = 0; } +#if BUILDFLAG(IS_EFL) +int64_t UploadFileElementReader::GetSizeSync() const { + int64_t file_size; + if (GetFileSize(path_, &file_size)) + return file_size; + return -1; +} + +bool UploadFileElementReader::DumpReaderData(std::string& data) const { + return base::ReadFileToString(path_, &data); +} +#endif + } // namespace net diff --git a/net/base/upload_file_element_reader.h b/net/base/upload_file_element_reader.h index 197ef31..65b9b51 100644 --- a/net/base/upload_file_element_reader.h +++ b/net/base/upload_file_element_reader.h @@ -74,6 +74,11 @@ class NET_EXPORT UploadFileElementReader : public UploadElementReader { int buf_length, CompletionOnceCallback callback) override; +#if BUILDFLAG(IS_EFL) + int64_t GetSizeSync() const override; + bool DumpReaderData(std::string& data) const override; +#endif + private: enum class State { // No async operation is pending. diff --git a/tizen_src/ewk/efl_integration/BUILD.gn b/tizen_src/ewk/efl_integration/BUILD.gn index 1a195b6..eb52644 100644 --- a/tizen_src/ewk/efl_integration/BUILD.gn +++ b/tizen_src/ewk/efl_integration/BUILD.gn @@ -192,6 +192,7 @@ shared_library("chromium-ewk") { "browser/browsing_data_remover_efl.h", "browser/download_manager_delegate_efl.cc", "browser/download_manager_delegate_efl.h", + "browser/intercept_request_params.h", "browser/javascript_dialog_manager_efl.cc", "browser/javascript_dialog_manager_efl.h", "browser/javascript_modal_dialog_efl.cc", @@ -202,6 +203,10 @@ shared_library("chromium-ewk") { "browser/mime_override_manager_efl.h", "browser/navigation_policy_handler_efl.cc", "browser/navigation_policy_handler_efl.h", + "browser/network_service/proxying_url_loader_efl.cc", + "browser/network_service/proxying_url_loader_efl.h", + "browser/network_service/proxying_url_loader_factory_efl.cc", + "browser/network_service/proxying_url_loader_factory_efl.h", "browser/policy_response_delegate_efl.cc", "browser/policy_response_delegate_efl.h", "browser/quota_permission_context_efl.cc", @@ -273,6 +278,10 @@ shared_library("chromium-ewk") { "text_encoding_map_efl.h", "url_request_context_getter_efl.cc", "url_request_context_getter_efl.h", + "url_request_interceptor_efl.cc", + "url_request_interceptor_efl.h", + "url_request_job_efl.cc", + "url_request_job_efl.h", "usermedia_permission_popup.cc", "usermedia_permission_popup.h", "web_contents_delegate_efl.cc", @@ -372,6 +381,8 @@ shared_library("chromium-ewk") { "private/ewk_history_private.h", "private/ewk_hit_test_private.cc", "private/ewk_hit_test_private.h", + "private/ewk_intercept_request_private.cc", + "private/ewk_intercept_request_private.h", "private/ewk_main_private.cc", "private/ewk_main_private.h", "private/ewk_manifest_private.cc", diff --git a/tizen_src/ewk/efl_integration/browser/intercept_request_params.h b/tizen_src/ewk/efl_integration/browser/intercept_request_params.h new file mode 100644 index 0000000..0f0b753 --- /dev/null +++ b/tizen_src/ewk/efl_integration/browser/intercept_request_params.h @@ -0,0 +1,25 @@ +// Copyright 2022 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INTERCEPT_REQUEST_PARAMS_H_ +#define INTERCEPT_REQUEST_PARAMS_H_ + +#include + +#include "net/base/upload_data_stream.h" +#include "net/http/http_request_headers.h" +#include "url/gurl.h" + +struct InterceptRequestParams { + InterceptRequestParams() = default; + InterceptRequestParams(const InterceptRequestParams& params) = default; + ~InterceptRequestParams() = default; + + GURL url; + std::string method = "GET"; + net::HttpRequestHeaders headers; + net::UploadDataStream* upload = nullptr; +}; + +#endif // INTERCEPT_REQUEST_PARAMS_H_ diff --git a/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.cc b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.cc new file mode 100644 index 0000000..a7c85f9 --- /dev/null +++ b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.cc @@ -0,0 +1,173 @@ +// Copyright 2022 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/network_service/proxying_url_loader_efl.h" + +#include "base/threading/sequenced_task_runner_handle.h" +#include "content/public/browser/browser_thread.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "net/base/io_buffer.h" +#include "net/http/http_util.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/mojom/url_response_head.mojom.h" + +// static +void ProxyingURLLoaderEfl::Create( + std::unique_ptr<_Ewk_Intercept_Request> intercept_request, + mojo::PendingReceiver<::network::mojom::URLLoader> loader, + mojo::PendingRemote client) { + auto* proxying_url_loader_efl = new ProxyingURLLoaderEfl( + std::move(intercept_request), std::move(loader), std::move(client)); +} + +ProxyingURLLoaderEfl::ProxyingURLLoaderEfl( + std::unique_ptr<_Ewk_Intercept_Request> intercept_request, + mojo::PendingReceiver<::network::mojom::URLLoader> loader, + mojo::PendingRemote client) + : intercept_request_(std::move(intercept_request)), + binding_(this), + weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + intercept_request_->set_delegate(this); + + binding_.Bind(std::move(loader)); + binding_.set_disconnect_handler( + base::BindOnce(&ProxyingURLLoaderEfl::OnConnectionError, + weak_ptr_factory_.GetWeakPtr())); + client_.Bind(std::move(client)); +} + +void ProxyingURLLoaderEfl::ResponseDecided() { + mojo::ScopedDataPipeProducerHandle pipe_producer_handle; + mojo::ScopedDataPipeConsumerHandle pipe_consumer_handle; + MojoResult pipe_result = mojo::CreateDataPipe( + network::features::GetDataPipeDefaultAllocationSize(), + pipe_producer_handle, pipe_consumer_handle); + if (pipe_result != MOJO_RESULT_OK) { + Finish(net::ERR_FAILED); + return; + } + + network::mojom::URLResponseHeadPtr response_head = + network::mojom::URLResponseHead::New(); + + response_head->request_start = base::TimeTicks::Now(); + response_head->response_start = response_head->request_start; + + std::string headers = intercept_request_->response_headers_take(); + response_head->headers = base::MakeRefCounted( + net::HttpUtil::AssembleRawHeaders(headers)); + response_head->headers->GetMimeType(&response_head->mime_type); + + response_head->charset = "utf-8"; + + client_->OnReceiveResponse(std::move(response_head), + std::move(pipe_consumer_handle), absl::nullopt); + + producer_handle_ = std::move(pipe_producer_handle); + + producer_handle_watcher_ = std::make_unique( + FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, + base::SequencedTaskRunnerHandle::Get()); + producer_handle_watcher_->Watch( + producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_WATCH_CONDITION_SATISFIED, + base::BindRepeating(&ProxyingURLLoaderEfl::OnHandleReady, + weak_ptr_factory_.GetWeakPtr())); + + if (!intercept_request_->is_chunked_write()) + ReadRawData(); +} + +void ProxyingURLLoaderEfl::PutChunk(const char* data, size_t length) { + AddRawDataIntoBuffer(data, length); +} + +void ProxyingURLLoaderEfl::ChunkedReadDone() { + read_complete_ = true; + // All data has already been sent, so call Finish() here. + if (buffers_.empty()) + Finish(net::OK); +} + +void ProxyingURLLoaderEfl::ReadRawData() { + std::unique_ptr response_body = + intercept_request_->response_body_take(); + // All raw data has been read. + read_complete_ = true; + AddRawDataIntoBuffer(response_body.get(), + intercept_request_->response_body_length_get()); +} + +void ProxyingURLLoaderEfl::AddRawDataIntoBuffer(const char* data, + size_t length) { + scoped_refptr buffer = + base::MakeRefCounted(length); + memcpy(buffer->data(), data, length); + buffers_.push( + base::MakeRefCounted(std::move(buffer), length)); + + SendRawData(); +} + +void ProxyingURLLoaderEfl::SendRawData() { + while (true) { + if (buffers_.empty()) { + if (read_complete_) + Finish(net::OK); + return; + } + + net::DrainableIOBuffer* buffer = buffers_.front().get(); + auto write_size = static_cast(buffer->BytesRemaining()); + DCHECK_GT(write_size, 0); + + MojoResult result = producer_handle_->WriteData(buffer->data(), &write_size, + MOJO_WRITE_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + producer_handle_watcher_->ArmOrNotify(); + return; + } + + if (result != MOJO_RESULT_OK) { + Finish(net::ERR_FAILED); + return; + } + + buffer->DidConsume(write_size); + if (!buffer->BytesRemaining()) + buffers_.pop(); + } +} + +void ProxyingURLLoaderEfl::OnHandleReady( + MojoResult result, + const mojo::HandleSignalsState& state) { + if (result != MOJO_RESULT_OK) { + Finish(net::ERR_FAILED); + return; + } + + SendRawData(); +} + +void ProxyingURLLoaderEfl::Finish(int error) { + client_->OnComplete(network::URLLoaderCompletionStatus(error)); + producer_handle_watcher_.reset(); + producer_handle_.reset(); + client_.reset(); + weak_ptr_factory_.InvalidateWeakPtrs(); + MaybeDeleteSelf(); +} + +void ProxyingURLLoaderEfl::OnConnectionError() { + binding_.reset(); + client_.reset(); + MaybeDeleteSelf(); +} + +void ProxyingURLLoaderEfl::MaybeDeleteSelf() { + if (!binding_.is_bound() && !client_.is_bound()) + delete this; +} diff --git a/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.h b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.h new file mode 100644 index 0000000..50b79e9 --- /dev/null +++ b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.h @@ -0,0 +1,80 @@ +// Copyright 2022 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PROXYING_URL_LOADER_EFL_H_ +#define PROXYING_URL_LOADER_EFL_H_ + +#include "base/containers/queue.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "private/ewk_intercept_request_private.h" +#include "services/network/public/mojom/url_loader.mojom.h" + +namespace mojo { +class SimpleWatcher; +} + +namespace net { +class DrainableIOBuffer; +} + +class ProxyingURLLoaderEfl : public network::mojom::URLLoader, + public _Ewk_Intercept_Request::Delegate { + public: + static void Create( + std::unique_ptr<_Ewk_Intercept_Request> intercept_request, + mojo::PendingReceiver<::network::mojom::URLLoader> loader, + mojo::PendingRemote client); + + ~ProxyingURLLoaderEfl() override = default; + + private: + // network::mojom::URLLoader: + void FollowRedirect( + const std::vector& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + const absl::optional& new_url) override {} + void SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) override {} + void PauseReadingBodyFromNet() override {} + void ResumeReadingBodyFromNet() override {} + + // _Ewk_Intercept_Request::Delegate: + void ResponseDecided() override; + void PutChunk(const char* data, size_t length) override; + void ChunkedReadDone() override; + + ProxyingURLLoaderEfl( + std::unique_ptr<_Ewk_Intercept_Request> intercept_request, + mojo::PendingReceiver<::network::mojom::URLLoader> loader, + mojo::PendingRemote client); + + ProxyingURLLoaderEfl(const ProxyingURLLoaderEfl&) = delete; + ProxyingURLLoaderEfl& operator=(const ProxyingURLLoaderEfl&) = delete; + + void ReadRawData(); + void AddRawDataIntoBuffer(const char* data, size_t length); + void SendRawData(); + void OnHandleReady(MojoResult result, const mojo::HandleSignalsState& state); + void Finish(int error); + void OnConnectionError(); + void MaybeDeleteSelf(); + + std::unique_ptr<_Ewk_Intercept_Request> intercept_request_; + + mojo::Receiver binding_; + mojo::Remote client_; + mojo::ScopedDataPipeProducerHandle producer_handle_; + std::unique_ptr producer_handle_watcher_; + + base::queue> buffers_; + bool read_complete_ = false; + + base::WeakPtrFactory weak_ptr_factory_; +}; + +#endif // URL_LOADER_EFL_H_ diff --git a/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.cc b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.cc new file mode 100644 index 0000000..df5dea7 --- /dev/null +++ b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.cc @@ -0,0 +1,175 @@ +// Copyright 2022 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/network_service/proxying_url_loader_factory_efl.h" + +#include "base/task/thread_pool.h" +#include "browser/intercept_request_params.h" +#include "browser/network_service/proxying_url_loader_efl.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/elements_upload_data_stream.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/base/upload_file_element_reader.h" +#include "private/ewk_intercept_request_private.h" +#include "services/network/public/cpp/resource_request.h" + +using network::ResourceRequestBody; + +namespace { + +// A subclass of net::UploadBytesElementReader which owns +// ResourceRequestBody. +class BytesElementReader : public net::UploadBytesElementReader { + public: + BytesElementReader(ResourceRequestBody* resource_request_body, + const network::DataElementBytes& element) + : net::UploadBytesElementReader(element.AsStringPiece().data(), + element.AsStringPiece().size()), + resource_request_body_(resource_request_body) {} + + ~BytesElementReader() override {} + + BytesElementReader(const BytesElementReader&) = delete; + BytesElementReader& operator=(const BytesElementReader&) = delete; + + private: + scoped_refptr resource_request_body_; +}; + +// A subclass of net::UploadFileElementReader which owns +// ResourceRequestBody. +// This class is necessary to ensure the BlobData and any attached shareable +// files survive until upload completion. +class FileElementReader : public net::UploadFileElementReader { + public: + FileElementReader(ResourceRequestBody* resource_request_body, + base::TaskRunner* task_runner, + const network::DataElementFile& element) + : net::UploadFileElementReader(task_runner, + element.path(), + element.offset(), + element.length(), + element.expected_modification_time()), + resource_request_body_(resource_request_body) {} + + ~FileElementReader() override {} + + FileElementReader(const FileElementReader&) = delete; + FileElementReader& operator=(const FileElementReader&) = delete; + + private: + scoped_refptr resource_request_body_; +}; + +} // namespace + +// static +void ProxyingURLLoaderFactoryEfl::CreateProxy( + content::BrowserContextEfl::ResourceContextEfl* resource_context, + mojo::PendingReceiver loader_receiver, + mojo::PendingRemote + target_factory_remote) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + new ProxyingURLLoaderFactoryEfl(resource_context, std::move(loader_receiver), + std::move(target_factory_remote)); +} + +ProxyingURLLoaderFactoryEfl::ProxyingURLLoaderFactoryEfl( + content::BrowserContextEfl::ResourceContextEfl* resource_context, + mojo::PendingReceiver loader_receiver, + mojo::PendingRemote target_factory_remote) + : resource_context_(resource_context), weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (target_factory_remote) { + target_factory_.Bind(std::move(target_factory_remote)); + target_factory_.set_disconnect_handler( + base::BindOnce(&ProxyingURLLoaderFactoryEfl::OnTargetFactoryError, + base::Unretained(this))); + } + + proxy_receivers_.Add(this, std::move(loader_receiver)); + proxy_receivers_.set_disconnect_handler( + base::BindRepeating(&ProxyingURLLoaderFactoryEfl::OnProxyBindingError, + base::Unretained(this))); +} + +ProxyingURLLoaderFactoryEfl::~ProxyingURLLoaderFactoryEfl() {} + +std::unique_ptr +ProxyingURLLoaderFactoryEfl::BuildUploadDataStream( + network::ResourceRequestBody* body, + base::SingleThreadTaskRunner* file_task_runner) { + std::vector> element_readers; + for (const auto& element : *body->elements()) { + switch (element.type()) { + case network::mojom::DataElementDataView::Tag::kBytes: + element_readers.push_back(std::make_unique( + body, element.As())); + break; + case network::mojom::DataElementDataView::Tag::kFile: + element_readers.push_back(std::make_unique( + body, file_task_runner, element.As())); + break; + default: + NOTREACHED(); + break; + } + } + + return std::make_unique( + std::move(element_readers), body->identifier()); +} + +void ProxyingURLLoaderFactoryEfl::CreateLoaderAndStart( + mojo::PendingReceiver<::network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + InterceptRequestParams params; + params.url = request.url; + params.method = request.method; + params.headers = request.headers; + + std::unique_ptr upload_data_stream; + if (request.request_body.get()) { + upload_data_stream = BuildUploadDataStream( + request.request_body.get(), + base::ThreadPool::CreateSingleThreadTaskRunner( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE}) + .get()); + params.upload = upload_data_stream.get(); + } + + auto intercept_request = std::make_unique<_Ewk_Intercept_Request>(params); + resource_context_->RunInterceptRequestCallback(intercept_request.get()); + intercept_request->callback_ended(); + if (intercept_request->is_ignored()) { + target_factory_->CreateLoaderAndStart(std::move(loader), request_id, + options, request, std::move(client), + traffic_annotation); + return; + } + + ProxyingURLLoaderEfl::Create(std::move(intercept_request), std::move(loader), + std::move(client)); +} + +void ProxyingURLLoaderFactoryEfl::Clone( + mojo::PendingReceiver loader_receiver) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + proxy_receivers_.Add(this, std::move(loader_receiver)); +} + +void ProxyingURLLoaderFactoryEfl::OnTargetFactoryError() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + delete this; +} + +void ProxyingURLLoaderFactoryEfl::OnProxyBindingError() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (proxy_receivers_.empty()) + delete this; +} diff --git a/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.h b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.h new file mode 100644 index 0000000..2626a3d --- /dev/null +++ b/tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.h @@ -0,0 +1,66 @@ +// Copyright 2022 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PROXYING_URL_LOADER_FACTORY_EFL_H_ +#define PROXYING_URL_LOADER_FACTORY_EFL_H_ + +#include "base/memory/weak_ptr.h" +#include "browser_context_efl.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" + +namespace content { +class ResourceContext; +} + +class ProxyingURLLoaderFactoryEfl : public network::mojom::URLLoaderFactory { + public: + static void CreateProxy( + content::BrowserContextEfl::ResourceContextEfl* resource_context, + mojo::PendingReceiver loader_receiver, + mojo::PendingRemote + target_factory_remote); + + ~ProxyingURLLoaderFactoryEfl() override; + + private: + // network::mojom::URLLoaderFactory + void CreateLoaderAndStart( + mojo::PendingReceiver<::network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + void Clone(mojo::PendingReceiver + loader_receiver) override; + + ProxyingURLLoaderFactoryEfl( + content::BrowserContextEfl::ResourceContextEfl* resource_context, + mojo::PendingReceiver loader_receiver, + mojo::PendingRemote + target_factory_remote); + + ProxyingURLLoaderFactoryEfl(const ProxyingURLLoaderFactoryEfl&) = delete; + ProxyingURLLoaderFactoryEfl& operator=(const ProxyingURLLoaderFactoryEfl&) = + delete; + + void OnTargetFactoryError(); + void OnProxyBindingError(); + std::unique_ptr BuildUploadDataStream( + network::ResourceRequestBody* body, + base::SingleThreadTaskRunner* file_task_runner); + + content::BrowserContextEfl::ResourceContextEfl* resource_context_; + + mojo::ReceiverSet proxy_receivers_; + mojo::Remote target_factory_; + + base::WeakPtrFactory weak_ptr_factory_; +}; + +#endif // PROXYING_URL_LOADER_FACTORY_EFL_H_ diff --git a/tizen_src/ewk/efl_integration/browser_context_efl.cc b/tizen_src/ewk/efl_integration/browser_context_efl.cc index 8145294..ff0ea68 100644 --- a/tizen_src/ewk/efl_integration/browser_context_efl.cc +++ b/tizen_src/ewk/efl_integration/browser_context_efl.cc @@ -52,7 +52,8 @@ static void CreateNetworkDelegateOnIOThread(BrowserContextEfl* context, BrowserContextEfl::ResourceContextEfl::ResourceContextEfl( scoped_refptr cookie_manager) - : cookie_manager_(cookie_manager) {} + : cookie_manager_(cookie_manager), + intercept_request_callback_{nullptr, nullptr, nullptr} {} BrowserContextEfl::~BrowserContextEfl() { NotifyWillBeDestroyed(); @@ -96,6 +97,29 @@ BrowserContextEfl::ResourceContextEfl::GetHTTPCustomHeadersEflMap() const { return http_custom_headers_; } +void BrowserContextEfl::ResourceContextEfl::SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + base::AutoLock locker(intercept_request_callback_lock_); + intercept_request_callback_ = {ewk_context, callback, user_data}; +} + +bool BrowserContextEfl::ResourceContextEfl::HasInterceptRequestCallback() + const { + base::AutoLock locker(intercept_request_callback_lock_); + return !!intercept_request_callback_.callback; +} + +void BrowserContextEfl::ResourceContextEfl::RunInterceptRequestCallback( + _Ewk_Intercept_Request* intercept_request) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + base::AutoLock locker(intercept_request_callback_lock_); + if (intercept_request_callback_.callback) + intercept_request_callback_.Run(intercept_request); +} + scoped_refptr BrowserContextEfl::ResourceContextEfl::GetCookieManager() const { return cookie_manager_; diff --git a/tizen_src/ewk/efl_integration/browser_context_efl.h b/tizen_src/ewk/efl_integration/browser_context_efl.h index 90ac364..1f88ac0 100644 --- a/tizen_src/ewk/efl_integration/browser_context_efl.h +++ b/tizen_src/ewk/efl_integration/browser_context_efl.h @@ -50,10 +50,31 @@ class BrowserContextEfl : public BrowserContext, scoped_refptr GetCookieManager() const; + void SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data); + bool HasInterceptRequestCallback() const; + void RunInterceptRequestCallback( + _Ewk_Intercept_Request* intercept_request) const; + private: + struct InterceptRequestCallbackWithData { + Ewk_Context* context; + Ewk_Context_Intercept_Request_Callback callback; + void* user_data; + + void Run(_Ewk_Intercept_Request* intercept_request) const { + callback(context, intercept_request, user_data); + } + }; + scoped_refptr cookie_manager_; HTTPCustomHeadersEflMap http_custom_headers_; mutable base::Lock http_custom_headers_lock_; + + InterceptRequestCallbackWithData intercept_request_callback_; + mutable base::Lock intercept_request_callback_lock_; }; BrowserContextEfl(EWebContext*, bool incognito = false); diff --git a/tizen_src/ewk/efl_integration/content_browser_client_efl.cc b/tizen_src/ewk/efl_integration/content_browser_client_efl.cc index 05d477a..bbe1fa0 100644 --- a/tizen_src/ewk/efl_integration/content_browser_client_efl.cc +++ b/tizen_src/ewk/efl_integration/content_browser_client_efl.cc @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/strings/string_number_conversions.h" #include "browser/editor_client_observer.h" +#include "browser/network_service/proxying_url_loader_factory_efl.h" #include "browser/notification/notification_controller_efl.h" #include "browser/quota_permission_context_efl.h" #include "browser/render_message_filter_efl.h" @@ -494,4 +495,35 @@ std::unique_ptr ContentBrowserClientEfl::CreateLoginDelegate( return nullptr; } +bool ContentBrowserClientEfl::WillCreateURLLoaderFactory( + content::BrowserContext* browser_context, + content::RenderFrameHost* frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin& request_initiator, + absl::optional navigation_id, + ukm::SourceIdObj ukm_source_id, + mojo::PendingReceiver* factory_receiver, + mojo::PendingRemote* + header_client, + bool* bypass_redirect_checks, + bool* disable_secure_dns, + network::mojom::URLLoaderFactoryOverridePtr* factory_override) { + auto* resource_context_efl = + static_cast(browser_context) + ->GetResourceContextEfl(); + if (!resource_context_efl->HasInterceptRequestCallback()) + return false; + + auto proxied_receiver = std::move(*factory_receiver); + mojo::PendingRemote target_factory_remote; + *factory_receiver = target_factory_remote.InitWithNewPipeAndPassReceiver(); + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&ProxyingURLLoaderFactoryEfl::CreateProxy, + resource_context_efl, std::move(proxied_receiver), + std::move(target_factory_remote))); + return true; +} + } // namespace content diff --git a/tizen_src/ewk/efl_integration/content_browser_client_efl.h b/tizen_src/ewk/efl_integration/content_browser_client_efl.h index 9071a2d..b0615ec 100644 --- a/tizen_src/ewk/efl_integration/content_browser_client_efl.h +++ b/tizen_src/ewk/efl_integration/content_browser_client_efl.h @@ -128,6 +128,21 @@ class ContentBrowserClientEfl : public ContentBrowserClient { void RemoveAcceptLangsChangedCallback(AcceptLangsChangedCallback callback); private: + bool WillCreateURLLoaderFactory( + BrowserContext* browser_context, + RenderFrameHost* frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin& request_initiator, + absl::optional navigation_id, + ukm::SourceIdObj ukm_source_id, + mojo::PendingReceiver* factory_receiver, + mojo::PendingRemote* + header_client, + bool* bypass_redirect_checks, + bool* disable_secure_dns, + network::mojom::URLLoaderFactoryOverridePtr* factory_override) override; + scoped_refptr GetSystemSharedURLLoaderFactory() override; diff --git a/tizen_src/ewk/efl_integration/eweb_context.cc b/tizen_src/ewk/efl_integration/eweb_context.cc index 10786a8..8e9fbda 100644 --- a/tizen_src/ewk/efl_integration/eweb_context.cc +++ b/tizen_src/ewk/efl_integration/eweb_context.cc @@ -814,3 +814,17 @@ bool EWebContext::SetExtensibleAPI(const std::string& api_name, bool enable) { bool EWebContext::GetExtensibleAPI(const std::string& api_name) { return TizenExtensibleHost::GetInstance()->GetExtensibleAPI(api_name); } + +void EWebContext::SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data) { + BrowserContextEfl::ResourceContextEfl* resource_context_efl = + browser_context_->GetResourceContextEfl(); + if (!resource_context_efl) { + LOG(ERROR) << "Unable to get ResourceContextEfl."; + return; + } + resource_context_efl->SetInterceptRequestCallback(ewk_context, callback, + user_data); +} diff --git a/tizen_src/ewk/efl_integration/eweb_context.h b/tizen_src/ewk/efl_integration/eweb_context.h index f600b6b..65a4168 100644 --- a/tizen_src/ewk/efl_integration/eweb_context.h +++ b/tizen_src/ewk/efl_integration/eweb_context.h @@ -172,6 +172,11 @@ class EWebContext { bool SetExtensibleAPI(const std::string& api_name, bool enable); bool GetExtensibleAPI(const std::string& api_name); + void SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data); + private: EWebContext(bool incognito); EWebContext(const std::string& injectedBundlePath); diff --git a/tizen_src/ewk/efl_integration/network_delegate_efl.cc b/tizen_src/ewk/efl_integration/network_delegate_efl.cc index 3b94127..0db1df1 100644 --- a/tizen_src/ewk/efl_integration/network_delegate_efl.cc +++ b/tizen_src/ewk/efl_integration/network_delegate_efl.cc @@ -13,7 +13,8 @@ namespace net { NetworkDelegateEfl::NetworkDelegateEfl( base::WeakPtr cookie_manager) - : cookie_manager_(cookie_manager) {} + : intercept_request_callback_with_data_{nullptr, nullptr, nullptr}, + cookie_manager_(cookie_manager) {} #if !defined(EWK_BRINGUP) // FIXME: m85 bringup NetworkDelegate::AuthRequiredResponse NetworkDelegateEfl::OnAuthRequired( @@ -46,5 +47,21 @@ bool NetworkDelegateEfl::OnCanAccessFile( const base::FilePath& absolute_path) const { return true; } +void NetworkDelegateEfl::SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data) { + intercept_request_callback_with_data_ = {ewk_context, callback, user_data}; +} + +bool NetworkDelegateEfl::HasInterceptRequestCallback() const { + return !!intercept_request_callback_with_data_.callback; +} + +void NetworkDelegateEfl::RunInterceptRequestCallback( + _Ewk_Intercept_Request* intercept_request) const { + if (intercept_request && intercept_request_callback_with_data_.callback) + intercept_request_callback_with_data_.Run(intercept_request); +} }; // namespace net diff --git a/tizen_src/ewk/efl_integration/network_delegate_efl.h b/tizen_src/ewk/efl_integration/network_delegate_efl.h index 3048563..1281ed9 100644 --- a/tizen_src/ewk/efl_integration/network_delegate_efl.h +++ b/tizen_src/ewk/efl_integration/network_delegate_efl.h @@ -7,8 +7,9 @@ #define _NETWORK_DELEGATE_EFL_H_ #include "base/compiler_specific.h" -#include "net/base/network_delegate_impl.h" #include "cookie_manager.h" +#include "net/base/network_delegate_impl.h" +#include "public/ewk_context.h" namespace net { @@ -16,6 +17,17 @@ class NetworkDelegateEfl : public NetworkDelegateImpl { public: NetworkDelegateEfl(base::WeakPtr cookie_manager); + NetworkDelegateEfl(const NetworkDelegateEfl&) = delete; + NetworkDelegateEfl& operator=(const NetworkDelegateEfl&) = delete; + + void SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data); + bool HasInterceptRequestCallback() const; + void RunInterceptRequestCallback( + _Ewk_Intercept_Request* intercept_request) const; + private: // NetworkDelegate implementation. #if !defined(EWK_BRINGUP) // FIXME: m85 bringup @@ -35,6 +47,18 @@ class NetworkDelegateEfl : public NetworkDelegateImpl { const base::FilePath& original_path, const base::FilePath& absolute_path) const; + struct InterceptRequestCallbackWithData { + Ewk_Context* context; + Ewk_Context_Intercept_Request_Callback callback; + void* user_data; + + void Run(_Ewk_Intercept_Request* intercept_request) const { + callback(context, intercept_request, user_data); + } + }; + + InterceptRequestCallbackWithData intercept_request_callback_with_data_; + base::WeakPtr cookie_manager_; }; diff --git a/tizen_src/ewk/efl_integration/private/ewk_context_private.cc b/tizen_src/ewk/efl_integration/private/ewk_context_private.cc index 95ea267..dce115c 100644 --- a/tizen_src/ewk/efl_integration/private/ewk_context_private.cc +++ b/tizen_src/ewk/efl_integration/private/ewk_context_private.cc @@ -260,3 +260,9 @@ void Ewk_Context::SetNotificationCallbacks( impl->SetNotificationCallbacks( this, show_callback, cancel_callback, user_data); } + +void Ewk_Context::SetContextInterceptRequestCallback( + Ewk_Context_Intercept_Request_Callback callback, + void* user_data) { + impl->SetInterceptRequestCallback(this, callback, user_data); +} diff --git a/tizen_src/ewk/efl_integration/private/ewk_context_private.h b/tizen_src/ewk/efl_integration/private/ewk_context_private.h index 2d29d69..c4fcb01 100644 --- a/tizen_src/ewk/efl_integration/private/ewk_context_private.h +++ b/tizen_src/ewk/efl_integration/private/ewk_context_private.h @@ -133,6 +133,10 @@ struct Ewk_Context : public base::RefCounted { Ewk_Context_Notification_Cancel_Callback cancel_callback, void* user_data); + void SetContextInterceptRequestCallback( + Ewk_Context_Intercept_Request_Callback callback, + void* user_data); + private: EWebContext* impl; diff --git a/tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.cc b/tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.cc new file mode 100644 index 0000000..2de0d02 --- /dev/null +++ b/tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.cc @@ -0,0 +1,222 @@ +// Copyright 2016 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ewk_intercept_request_private.h" + +#include + +#include "base/bind.h" +#include "base/strings/string_number_conversions.h" +#include "browser/intercept_request_params.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/upload_data_stream.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_status_code.h" + +using content::BrowserThread; + +namespace { +void _headers_entry_free_cb(void* data) { + free(data); +} +} // namespace + +_Ewk_Intercept_Request::_Ewk_Intercept_Request( + const InterceptRequestParams& params) + // API user might want to receive even invalid urls + : delegate_(nullptr), + request_scheme_(params.url.scheme()), + request_url_(params.url.possibly_invalid_spec()), + request_http_method_(params.method), + request_upload_(params.upload), + request_body_length_(-1), + response_body_length_(0), + response_status_code_(-1), + ignored_(false), + chunked_write_(false) { + request_headers_ = eina_hash_string_small_new(_headers_entry_free_cb); + net::HttpRequestHeaders::Iterator current_header(params.headers); + while (current_header.GetNext()) { + if (!eina_hash_add(request_headers_, current_header.name().c_str(), + strdup(current_header.value().c_str()))) { + LOG(ERROR) << "Failed to add header to Eina_Hash"; + } + } +} + +_Ewk_Intercept_Request::~_Ewk_Intercept_Request() { + eina_hash_free(request_headers_); +} + +Eina_Bool _Ewk_Intercept_Request::response_status_set( + int status_code, + const char* custom_status_text) { + response_status_code_ = status_code; + if (custom_status_text) { + response_status_text_ = custom_status_text; + } else { + response_status_text_ = + net::GetHttpReasonPhrase(net::HttpStatusCode(status_code)); + } + return EINA_TRUE; +} + +Eina_Bool _Ewk_Intercept_Request::response_header_add(const char* field_name, + const char* field_value) { + response_headers_map_.push_back( + std::pair(field_name, field_value)); + return EINA_TRUE; +} + +Eina_Bool _Ewk_Intercept_Request::response_header_map_add( + const Eina_Hash* headers) { + Eina_Iterator* it = eina_hash_iterator_tuple_new(headers); + if (!it) + return EINA_FALSE; + void* data; + while (eina_iterator_next(it, &data)) { + Eina_Hash_Tuple* tuple = static_cast(data); + std::string field_name = static_cast(tuple->key); + std::string field_value = static_cast(tuple->data); + response_headers_map_.push_back(std::pair( + std::move(field_name), std::move(field_value))); + } + eina_iterator_free(it); + return EINA_TRUE; +} + +void _Ewk_Intercept_Request::headers_generate() { + response_headers_.append("HTTP/1.1 "); + response_headers_.append(base::NumberToString(response_status_code_)); + response_headers_.append(" "); + response_headers_.append(response_status_text_); + response_headers_.append("\r\n"); + for (const auto& header : response_headers_map_) { + response_headers_.append(header.first); + response_headers_.append(": "); + response_headers_.append(header.second); + response_headers_.append("\r\n"); + } + response_headers_.append("\r\n"); +} + +Eina_Bool _Ewk_Intercept_Request::response_body_set(const void* data, + size_t length) { + headers_generate(); + set_data(data, length); + post_response_decided(); + return EINA_TRUE; +} + +Eina_Bool _Ewk_Intercept_Request::response_set(const char* headers, + const void* data, + size_t length) { + response_headers_ = headers; + set_data(data, length); + post_response_decided(); + return EINA_TRUE; +} + +void _Ewk_Intercept_Request::set_data(const void* data, size_t length) { + response_body_.reset(new char[length]); + memcpy(response_body_.get(), data, length); + response_body_length_ = length; +} + +Eina_Bool _Ewk_Intercept_Request::request_ignore() { + ignored_ = true; + return EINA_TRUE; +} + +Eina_Bool _Ewk_Intercept_Request::response_write_chunk(const void* data, + size_t length) { + // On first chunked write generate headers and signal job. + if (!chunked_write_) { + chunked_write_ = true; + headers_generate(); + post_response_decided(); + } + + // Zero length data means end of response body. Also there is possibility of + // early finish if engine doesn't want response to the request anymore. + if (length == 0 || chunked_write_early_exit_.IsSet()) { + post_chunked_read_done(); + return EINA_FALSE; + } + + char* data_copy = new char[length]; + memcpy(data_copy, data, length); + post_put_chunk(data_copy, length); + return EINA_TRUE; +} + +void _Ewk_Intercept_Request::response_decided() { + if (delegate_) + delegate_->ResponseDecided(); +} + +void _Ewk_Intercept_Request::post_response_decided() { + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindRepeating(&_Ewk_Intercept_Request::response_decided, + base::Unretained(this))); +} + +void _Ewk_Intercept_Request::put_chunk(const char* data, size_t length) { + if (delegate_) + delegate_->PutChunk(data, length); +} + +void _Ewk_Intercept_Request::post_put_chunk(const char* data, size_t length) { + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindRepeating(&_Ewk_Intercept_Request::put_chunk, + base::Unretained(this), data, length)); +} + +void _Ewk_Intercept_Request::chunked_read_done() { + if (delegate_) + delegate_->ChunkedReadDone(); +} + +void _Ewk_Intercept_Request::post_chunked_read_done() { + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindRepeating(&_Ewk_Intercept_Request::chunked_read_done, + base::Unretained(this))); +} + +const char* _Ewk_Intercept_Request::request_body_get() const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!request_body_.empty()) + return request_body_.data(); + + if (!request_upload_) { + LOG(ERROR) << "Trying to get request body outside of " + "Ewk_Context_Intercept_Request_Callback"; + return nullptr; + } + + std::string http_body; + if (!request_upload_->DumpUploadData(http_body)) + return nullptr; + + request_body_ = http_body; + return request_body_.data(); +} + +int64_t _Ewk_Intercept_Request::request_body_length_get() const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (request_body_length_ >= 0) + return request_body_length_; + + if (!request_upload_) { + LOG(ERROR) << "Trying to get request body length outside of " + "Ewk_Context_Intercept_Request_Callback"; + return -1; + } + + request_body_length_ = request_upload_->GetSizeSync(); + return request_body_length_; +} diff --git a/tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.h b/tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.h new file mode 100644 index 0000000..a829724 --- /dev/null +++ b/tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.h @@ -0,0 +1,116 @@ +// Copyright 2016 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EWK_EFL_INTEGRATION_PRIVATE_EWK_INTERCEPT_REQUEST_PRIVATE_H_ +#define EWK_EFL_INTEGRATION_PRIVATE_EWK_INTERCEPT_REQUEST_PRIVATE_H_ + +#include +#include +#include +#include + +#include + +#include "base/synchronization/atomic_flag.h" + +namespace net { +class UploadDataStream; +} + +struct InterceptRequestParams; + +struct _Ewk_Intercept_Request { + public: + class Delegate { + public: + virtual void ResponseDecided() = 0; + + virtual void PutChunk(const char* data, size_t length) = 0; + + virtual void ChunkedReadDone() = 0; + + protected: + virtual ~Delegate() {} + }; + + explicit _Ewk_Intercept_Request(const InterceptRequestParams& params); + ~_Ewk_Intercept_Request(); + + // functions so user can use EWK API to get info about intercepted request + // to make a decision + const char* request_scheme_get() const { return request_scheme_.c_str(); } + const char* request_url_get() const { return request_url_.c_str(); } + const char* request_http_method_get() const { + return request_http_method_.c_str(); + } + const Eina_Hash* request_headers_get() const { return request_headers_; } + const char* request_body_get() const; + int64_t request_body_length_get() const; + + // functions so user can use EWK API to write response for intercepted + // request + Eina_Bool response_set(const char* headers, const void* data, size_t length); + Eina_Bool request_ignore(); + Eina_Bool response_status_set(int status_code, + const char* custom_status_text); + Eina_Bool response_header_add(const char* field_name, + const char* field_value); + Eina_Bool response_header_map_add(const Eina_Hash* headers); + Eina_Bool response_body_set(const void* data, size_t length); + Eina_Bool response_write_chunk(const void* data, size_t length); + + // functions so URLRequestJobEFL can get info about headers and body of + // custom response + std::string&& response_headers_take() { return std::move(response_headers_); } + std::unique_ptr response_body_take() { + return std::move(response_body_); + } + size_t response_body_length_get() const { return response_body_length_; } + bool is_ignored() const { return ignored_; } + bool is_chunked_write() const { return chunked_write_; } + void request_early_exit() { chunked_write_early_exit_.Set(); } + void set_delegate(Delegate* delegate) { delegate_ = delegate; } + void callback_ended() { request_upload_ = nullptr; } + + private: + void set_data(const void* data, size_t length); + void headers_generate(); + void response_decided(); + void post_response_decided(); + void put_chunk(const char* data, size_t length); + void post_put_chunk(const char* data, size_t length); + void chunked_read_done(); + void post_chunked_read_done(); + + Delegate* delegate_; + + std::string request_scheme_; + std::string request_url_; + std::string request_http_method_; + Eina_Hash* request_headers_; + + // request_upload_ is valid only during the callback. It is saved to allow + // retrieving request body. We don't save request's body every time in + // constructor (like other data), because data uploaded in request body may + // have excessive size. + net::UploadDataStream* request_upload_; + + // mutable because request_body_ is lazily loaded in its const getter + mutable std::string request_body_; + // mutable because request_body_size_ is lazily loaded in its const getter + mutable int64_t request_body_length_; + + std::string response_headers_; + std::unique_ptr response_body_; + size_t response_body_length_; + int response_status_code_; + std::string response_status_text_; + std::vector> response_headers_map_; + + bool ignored_; + bool chunked_write_; + base::AtomicFlag chunked_write_early_exit_; +}; + +#endif // EWK_EFL_INTEGRATION_PRIVATE_EWK_INTERCEPT_REQUEST_PRIVATE_H_ diff --git a/tizen_src/ewk/efl_integration/public/ewk_context.cc b/tizen_src/ewk/efl_integration/public/ewk_context.cc index c7c1e79..9b13510 100644 --- a/tizen_src/ewk/efl_integration/public/ewk_context.cc +++ b/tizen_src/ewk/efl_integration/public/ewk_context.cc @@ -687,6 +687,14 @@ Eina_Bool ewk_context_notification_callbacks_reset(Ewk_Context* context) return EINA_TRUE; } +void ewk_context_intercept_request_callback_set( + Ewk_Context* context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data) { + EINA_SAFETY_ON_NULL_RETURN(context); + context->SetContextInterceptRequestCallback(callback, user_data); +} + void ewk_context_compression_proxy_enabled_set(Ewk_Context* context, Eina_Bool enabled) { LOG_EWK_API_MOCKUP(); diff --git a/tizen_src/ewk/efl_integration/public/ewk_intercept_request.cc b/tizen_src/ewk/efl_integration/public/ewk_intercept_request.cc index 1c70b4f..b29fbb8 100644 --- a/tizen_src/ewk/efl_integration/public/ewk_intercept_request.cc +++ b/tizen_src/ewk/efl_integration/public/ewk_intercept_request.cc @@ -25,52 +25,108 @@ #include "ewk_intercept_request_internal.h" +#include "private/ewk_intercept_request_private.h" #include "private/ewk_private.h" +const char* ewk_intercept_request_url_get( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, NULL); + return intercept_request->request_url_get(); +} + +const char* ewk_intercept_request_http_method_get( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, NULL); + return intercept_request->request_http_method_get(); +} + +const Eina_Hash* ewk_intercept_request_headers_get( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, NULL); + return intercept_request->request_headers_get(); +} -Eina_Bool ewk_intercept_request_ignore(Ewk_Intercept_Request* interceptRequest) -{ - LOG_EWK_API_MOCKUP(); - return true; +Eina_Bool ewk_intercept_request_ignore( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + return intercept_request->request_ignore(); } -const char* ewk_intercept_request_url_get(Ewk_Intercept_Request* interceptRequest) -{ - LOG_EWK_API_MOCKUP(); - return NULL; +Eina_Bool ewk_intercept_request_response_set( + Ewk_Intercept_Request* intercept_request, + const char* headers, + const char* body, + size_t length) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(headers, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(body, EINA_FALSE); + return intercept_request->response_set(headers, body, length); } -const char* ewk_intercept_request_http_method_get(Ewk_Intercept_Request* interceptRequest) -{ - LOG_EWK_API_MOCKUP(); - return NULL; +Eina_Bool ewk_intercept_request_response_status_set( + Ewk_Intercept_Request* intercept_request, + int status_code, + const char* custom_status_text) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + return intercept_request->response_status_set(status_code, + custom_status_text); } -const Eina_Hash* ewk_intercept_request_headers_get(Ewk_Intercept_Request* interceptRequest) -{ - LOG_EWK_API_MOCKUP(); - return NULL; +Eina_Bool ewk_intercept_request_response_header_add( + Ewk_Intercept_Request* intercept_request, + const char* field_name, + const char* field_value) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(field_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(field_value, EINA_FALSE); + return intercept_request->response_header_add(field_name, field_value); } -Eina_Bool ewk_intercept_request_response_set(Ewk_Intercept_Request* interceptRequest, const char* headers, const char* body, int length) -{ - LOG_EWK_API_MOCKUP(); - return false; +Eina_Bool ewk_intercept_request_response_header_map_add( + Ewk_Intercept_Request* intercept_request, + const Eina_Hash* headers) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(headers, EINA_FALSE); + return intercept_request->response_header_map_add(headers); } -const char* ewk_intercept_request_scheme_get(Ewk_Intercept_Request* intercept_request) -{ - LOG_EWK_API_MOCKUP(); - return NULL; +Eina_Bool ewk_intercept_request_response_body_set( + Ewk_Intercept_Request* intercept_request, + const char* body, + size_t length) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(body, EINA_FALSE); + return intercept_request->response_body_set(body, length); } -const char* ewk_intercept_request_body_get(Ewk_Intercept_Request* intercept_request) -{ - LOG_EWK_API_MOCKUP(); - return NULL; +Eina_Bool ewk_intercept_request_response_write_chunk( + Ewk_Intercept_Request* intercept_request, + const char* chunk, + size_t length) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, EINA_FALSE); + // (chunk == NULL && length == 0) means to end of data, handled inside + // |response_write_chunk|. + if (chunk != NULL || length != 0) { + EINA_SAFETY_ON_NULL_RETURN_VAL(chunk, EINA_FALSE); + EINA_SAFETY_ON_TRUE_RETURN_VAL(length == 0, EINA_FALSE); + } + return intercept_request->response_write_chunk(chunk, length); } -int64_t ewk_intercept_request_body_length_get(Ewk_Intercept_Request* intercept_request) -{ - LOG_EWK_API_MOCKUP(); - return -1; + +const char* ewk_intercept_request_scheme_get( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, NULL); + return intercept_request->request_scheme_get(); +} + +const char* ewk_intercept_request_body_get( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, NULL); + return intercept_request->request_body_get(); +} + +int64_t ewk_intercept_request_body_length_get( + Ewk_Intercept_Request* intercept_request) { + EINA_SAFETY_ON_NULL_RETURN_VAL(intercept_request, -1); + return intercept_request->request_body_length_get(); } diff --git a/tizen_src/ewk/efl_integration/url_request_context_getter_efl.cc b/tizen_src/ewk/efl_integration/url_request_context_getter_efl.cc index 844ea4c..b79993d 100644 --- a/tizen_src/ewk/efl_integration/url_request_context_getter_efl.cc +++ b/tizen_src/ewk/efl_integration/url_request_context_getter_efl.cc @@ -36,6 +36,7 @@ #include "net/url_request/url_request_job_factory.h" #include "network_delegate_efl.h" #include "services/network/public/cpp/network_switches.h" +#include "url_request_interceptor_efl.h" #include "wrt/wrt_file_protocol_handler.h" using net::SQLitePersistentCookieStore; @@ -209,19 +210,16 @@ net::URLRequestContext* URLRequestContextGetterEfl::GetURLRequestContext() { base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}) .get()))); DCHECK(set_protocol); +#endif // Set up interceptors in the reverse order. std::unique_ptr top_job_factory = std::move(job_factory); - for (URLRequestInterceptorScopedVector::reverse_iterator i = - request_interceptors_.rbegin(); - i != request_interceptors_.rend(); ++i) { - top_job_factory.reset(new net::URLRequestInterceptingJobFactory( - std::move(top_job_factory), std::move(*i))); - } + request_interceptors_.push_back( + base::WrapUnique(new URLRequestInterceptorEFL())); + request_interceptors_.clear(); -#endif - storage_->set_job_factory(std::move(job_factory)); + storage_->set_job_factory(std::move(top_job_factory)); #else net::URLRequestContextBuilder builder; url_request_context_ = builder.Build(); @@ -343,4 +341,16 @@ void URLRequestContextGetterEfl::CreatePersistentCookieStore( #endif } +void URLRequestContextGetterEfl::SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (network_delegate_.get()) { + (static_cast(network_delegate_.get())) + ->SetInterceptRequestCallback(ewk_context, callback, user_data); + } +} + }; // namespace content diff --git a/tizen_src/ewk/efl_integration/url_request_context_getter_efl.h b/tizen_src/ewk/efl_integration/url_request_context_getter_efl.h index 111a753..112d191 100644 --- a/tizen_src/ewk/efl_integration/url_request_context_getter_efl.h +++ b/tizen_src/ewk/efl_integration/url_request_context_getter_efl.h @@ -17,6 +17,8 @@ #include "net/http/http_network_session.h" #include "net/proxy_resolution/proxy_config_service.h" #include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_interceptor.h" +#include "public/ewk_context.h" #include "public/ewk_cookie_manager_internal.h" namespace net { @@ -57,6 +59,11 @@ class URLRequestContextGetterEfl : public net::URLRequestContextGetter { void NotifyContextShuttingDown(); + void SetInterceptRequestCallback( + Ewk_Context* ewk_context, + Ewk_Context_Intercept_Request_Callback callback, + void* user_data); + protected: virtual ~URLRequestContextGetterEfl(); @@ -87,6 +94,8 @@ class URLRequestContextGetterEfl : public net::URLRequestContextGetter { std::unique_ptr network_delegate_; std::unique_ptr cert_verifier_; std::unique_ptr url_request_context_; + std::vector> + request_interceptors_; base::WeakPtrFactory weak_ptr_factory_; }; diff --git a/tizen_src/ewk/efl_integration/url_request_interceptor_efl.cc b/tizen_src/ewk/efl_integration/url_request_interceptor_efl.cc new file mode 100644 index 0000000..284e0ac --- /dev/null +++ b/tizen_src/ewk/efl_integration/url_request_interceptor_efl.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2016 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url_request_interceptor_efl.h" + +#include "base/supports_user_data.h" +#include "browser/intercept_request_params.h" +#include "content/public/browser/browser_thread.h" +#include "net/http/http_request_headers.h" +#include "net/url_request/url_request_job.h" +#include "network_delegate_efl.h" +#include "private/ewk_intercept_request_private.h" +#include "url/gurl.h" +#include "url_request_job_efl.h" + +class WebContents; + +namespace content { + +namespace { + +const void* const kRequestAlreadyHasJobDataKey = &kRequestAlreadyHasJobDataKey; + +} // namespace + +URLRequestInterceptorEFL::URLRequestInterceptorEFL() {} + +URLRequestInterceptorEFL::~URLRequestInterceptorEFL() {} + +std::unique_ptr +URLRequestInterceptorEFL::MaybeInterceptRequest( + net::URLRequest* request) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // MaybeInterceptRequest can be called multiple times for the same request. + if (request->GetUserData(kRequestAlreadyHasJobDataKey)) + return nullptr; + + auto network_delegate_efl = + static_cast(request->network_delegate()); + + if (!network_delegate_efl->HasInterceptRequestCallback()) + return nullptr; + + InterceptRequestParams params; + params.url = request->url(); + // Code for getting request headers ported from android webview impl, see + // android_webview/native/aw_contents_io_thread_client_impl.cc. + // Headers for http requests we get here will not be the same as headers + // which would be sent if request have been executed normally. + // + // Getting the same http headers would require creating URLRequestHttpJob, + // which fills http headers. See HttpNetworkTransaction::BuildRequestHeaders + // in net/http/http_network_transaction.cc. + params.method = request->method(); + params.headers = request->extra_request_headers(); + params.upload = + const_cast(request->get_upload_for_testing()); + + auto intercept_request = new _Ewk_Intercept_Request(params); + network_delegate_efl->RunInterceptRequestCallback(intercept_request); + intercept_request->callback_ended(); + if (intercept_request->is_ignored()) { + delete intercept_request; + return nullptr; + } + + GURL referrer(request->referrer()); + if (referrer.is_valid() && + (!request->is_pending() || request->is_redirecting())) { + request->SetExtraRequestHeaderByName(net::HttpRequestHeaders::kReferer, + referrer.spec(), true); + } + request->SetUserData(kRequestAlreadyHasJobDataKey, + std::unique_ptr( + new base::SupportsUserData::Data())); + + return base::WrapUnique(new URLRequestJobEFL(request, intercept_request)); +} + +} // namespace content diff --git a/tizen_src/ewk/efl_integration/url_request_interceptor_efl.h b/tizen_src/ewk/efl_integration/url_request_interceptor_efl.h new file mode 100644 index 0000000..7e62975 --- /dev/null +++ b/tizen_src/ewk/efl_integration/url_request_interceptor_efl.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2016 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EWK_EFL_INTEGRATION_URL_REQUEST_INTERCEPTOR_EFL_H_ +#define EWK_EFL_INTEGRATION_URL_REQUEST_INTERCEPTOR_EFL_H_ + +#include "net/url_request/url_request_interceptor.h" +#include "public/ewk_context.h" + +namespace net { +class URLRequest; +class URLRequestJob; +class NetworkDelegate; +} // namespace net + +struct _Ewk_Intercept_Request; + +namespace content { + +class URLRequestInterceptorEFL : public net::URLRequestInterceptor { + public: + URLRequestInterceptorEFL(); + ~URLRequestInterceptorEFL() override; + + URLRequestInterceptorEFL(const URLRequestInterceptorEFL&) = delete; + URLRequestInterceptorEFL& operator=(const URLRequestInterceptorEFL&) = delete; + + std::unique_ptr MaybeInterceptRequest( + net::URLRequest* request) const override; +}; + +} // namespace content + +#endif // EWK_EFL_INTEGRATION_URL_REQUEST_INTERCEPTOR_EFL_H_ diff --git a/tizen_src/ewk/efl_integration/url_request_job_efl.cc b/tizen_src/ewk/efl_integration/url_request_job_efl.cc new file mode 100644 index 0000000..fc05f6d --- /dev/null +++ b/tizen_src/ewk/efl_integration/url_request_job_efl.cc @@ -0,0 +1,207 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Copyright 2016 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url_request_job_efl.h" + +#include +#include + +#include "base/callback.h" +#include "base/strings/string_util.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" + +using content::BrowserThread; + +URLRequestJobEFL::URLRequestJobEFL(net::URLRequest* request, + _Ewk_Intercept_Request* intercept_request) + : URLRequestJob(request), + intercept_request_(intercept_request), + response_info_(), + bytes_read_(0), + response_body_length_(0), + killed_(false), + waiting_done_(false), + communication_done_(false), + started_(false), + available_data_(0), + buf_(nullptr), + buf_size_(0) { + intercept_request_->set_delegate(this); +} + +URLRequestJobEFL::~URLRequestJobEFL() {} + +URLRequestJobEFL::Chunk::Chunk(const char* data, size_t length) + : data_(data), data_pos_(data), length_(length) {} + +URLRequestJobEFL::Chunk::~Chunk() {} + +void URLRequestJobEFL::HeadersComplete() { + // Converts \r\n separated headers to \0 separated as expected by + // net::HttpResponseHeaders. We can't really expect \0 separated headers from + // EWK C API. + base::ReplaceSubstringsAfterOffset(&response_headers_, 0, "\r\n", + std::string("\0", 1)); + response_info_.headers = new net::HttpResponseHeaders(response_headers_); + + NotifyHeadersComplete(); +} + +void URLRequestJobEFL::TakeResponseData() { + response_headers_ = intercept_request_->response_headers_take(); + if (!intercept_request_->is_chunked_write()) { + response_body_ = intercept_request_->response_body_take(); + response_body_length_ = intercept_request_->response_body_length_get(); + } +} + +void URLRequestJobEFL::Start() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + started_ = true; + + // User have already sent all data or started chunked writing. + if (waiting_done_) + HeadersComplete(); +} + +int URLRequestJobEFL::ReadRawData(net::IOBuffer* buf, int buf_size) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // It is safe to call is_chunked_write here on IO thread, despite it being + // set in UI/user thread. Due to URLRequestJob contract we won't enter + // ReadRawData until we call NotifyHeadersComplete, which is called in + // response to ResponseDecided task posted on IO thread by + // Ewk_Intercept_Request. is_chunked_write internal state is always decided + // before ResponseDecided is posted. The same reasoning applies to call to + // is_chunked_write in ResponseDecided task. + if (intercept_request_->is_chunked_write()) { + // If user signaled end of writing before all data was read by engine + // we couldn't call ::NotifyDone() in ::ChunkedReadDone(), so we can signal + // end of data here by setting |bytes_read| to 0 and returning true. + if (communication_done_ && !available_data_) + return 0; + + if (available_data_) { + // If user wrote some chunks of data before ::ReadRawData() we can read + // it already here. + return static_cast(ReadChunkedData(buf, buf_size)); + } else { + // If there is no chunk of data we save |buf| and wait for a write. + buf_ = buf; + buf_size_ = buf_size; + return net::ERR_IO_PENDING; + } + } else { + return static_cast(ReadData(buf, buf_size)); + } +} + +void URLRequestJobEFL::Kill() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (killed_) + return; + killed_ = true; + buf_ = nullptr; + buf_size_ = -1; + intercept_request_->request_early_exit(); + URLRequestJob::Kill(); +} + +void URLRequestJobEFL::ResponseDecided() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + waiting_done_ = true; + TakeResponseData(); + if (started_ && !killed_) { + // No point in notifing if job is dead. + HeadersComplete(); + } +} + +void URLRequestJobEFL::PutChunk(const char* data, size_t length) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (killed_) { + // No point doing any work if job is already killed, but events are still + // scheduled. + delete data; + return; + } + chunks_.emplace(data, length); + available_data_ += length; + if (buf_) { + // A buffer is ready, we can write now. + int result = static_cast(ReadChunkedData(buf_, buf_size_)); + buf_ = nullptr; + buf_size_ = -1; + ReadRawDataComplete(result); + } +} + +void URLRequestJobEFL::ChunkedReadDone() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + communication_done_ = true; + // If user spammed engine with data, and called ::ChunkedReadDone() before + // all chunks have been read, then we might not be done yet. End of reading + // will be signaled from ::ReadRawData(). + if (chunks_.empty() && !killed_) + ReadRawDataComplete(0); +} + +size_t URLRequestJobEFL::ReadChunkedData(net::IOBuffer* buf, int buf_size) { + size_t to_read = std::min(static_cast(buf_size), available_data_); + available_data_ -= to_read; + char* buf_ptr = buf->data(); + while (!chunks_.empty()) { + if (chunks_.front().Available() > to_read) { + memcpy(buf_ptr, chunks_.front().Data(), to_read); + buf_ptr += to_read; + chunks_.front().Advance(to_read); + to_read = 0; + break; + } else { + memcpy(buf_ptr, chunks_.front().Data(), chunks_.front().Available()); + buf_ptr += chunks_.front().Available(); + to_read -= chunks_.front().Available(); + chunks_.front().Advance(chunks_.front().Available()); + chunks_.pop(); + if (!to_read) + break; + } + } + return buf_ptr - buf->data(); +} + +size_t URLRequestJobEFL::ReadData(net::IOBuffer* buf, int buf_size) { + size_t to_read = std::min(static_cast(buf_size), + response_body_length_ - bytes_read_); + if (to_read) { + memcpy(buf->data(), response_body_.get() + bytes_read_, to_read); + bytes_read_ += to_read; + } + + return to_read; +} + +bool URLRequestJobEFL::GetMimeType(std::string* mime_type) const { + return response_info_.headers->GetMimeType(mime_type); +} + +bool URLRequestJobEFL::GetCharset(std::string* charset) { + *charset = std::string("utf8"); + return true; +} + +int URLRequestJobEFL::GetResponseCode() const { + return response_info_.headers->response_code(); +} + +void URLRequestJobEFL::GetResponseInfo(net::HttpResponseInfo* info) { + *info = response_info_; +} diff --git a/tizen_src/ewk/efl_integration/url_request_job_efl.h b/tizen_src/ewk/efl_integration/url_request_job_efl.h new file mode 100644 index 0000000..a9457ca --- /dev/null +++ b/tizen_src/ewk/efl_integration/url_request_job_efl.h @@ -0,0 +1,94 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Copyright 2016 Samsung Electronics. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EWK_EFL_INTEGRATION_URL_REQUEST_JOB_EFL_H_ +#define EWK_EFL_INTEGRATION_URL_REQUEST_JOB_EFL_H_ + +#include +#include +#include + +#include "net/http/http_response_info.h" +#include "net/url_request/url_request_job.h" + +#include "private/ewk_intercept_request_private.h" + +namespace net { +class URLRequest; +class IOBuffer; +} // namespace net + +class URLRequestJobEFL : public net::URLRequestJob, + public _Ewk_Intercept_Request::Delegate { + public: + URLRequestJobEFL(net::URLRequest* request, + _Ewk_Intercept_Request* intercept_request); + ~URLRequestJobEFL() override; + + URLRequestJobEFL(const URLRequestJobEFL&) = delete; + URLRequestJobEFL& operator=(const URLRequestJobEFL&) = delete; + + // URLRequestJob: + void Start() override; + void Kill() override; + int ReadRawData(net::IOBuffer* buf, int buf_size) override; + bool GetMimeType(std::string* mime_type) const override; + bool GetCharset(std::string* charset) override; + int GetResponseCode() const override; + void GetResponseInfo(net::HttpResponseInfo* info) override; + + _Ewk_Intercept_Request* GetInterceptRequestHandle() const { + return intercept_request_.get(); + } + + private: + // _Ewk_Intercept_Request::Delegate: + void ResponseDecided() override; + void PutChunk(const char* data, size_t length) override; + void ChunkedReadDone() override; + + class Chunk { + public: + Chunk(const char* data, size_t length); + ~Chunk(); + + size_t Available() { return length_ - (data_pos_ - data_.get()); } + const char* Data() { return data_pos_; } + void Advance(size_t count) { data_pos_ += count; } + + private: + std::unique_ptr data_; + const char* data_pos_; + size_t length_; + }; + + void HeadersComplete(); + void TakeResponseData(); + size_t ReadData(net::IOBuffer* buf, int buf_size); + size_t ReadChunkedData(net::IOBuffer* buf, int buf_size); + + std::unique_ptr<_Ewk_Intercept_Request> intercept_request_; + net::HttpResponseInfo response_info_; + + size_t bytes_read_; + size_t response_body_length_; + bool killed_; + bool waiting_done_; + bool communication_done_; + bool started_; + + std::string response_headers_; + std::unique_ptr response_body_; + + size_t available_data_; + std::queue chunks_; + // |buf_| lifetime is managed by outer scope, it is alive as long as job uses + // it - until ::ReadRawData returns (with value true), or until + // ::NotifyReadComplete call in async case. + net::IOBuffer* buf_; + int buf_size_; +}; + +#endif // EWK_EFL_INTEGRATION_URL_REQUEST_JOB_EFL_H_ diff --git a/tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc b/tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc index d86cec1..844ff2c 100644 --- a/tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc +++ b/tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc @@ -169,6 +169,13 @@ WebContents* WebContentsDelegateEfl::OpenURLFromTab( void WebContentsDelegateEfl::NavigationStateChanged( WebContents* source, InvalidateTypes changed_flags) { + // We always notfiy clients about title invalidation, even if its text + // didn't actually change. This is to maintain EWK API consistency. + if (changed_flags & INVALIDATE_TYPE_TITLE) { + web_view_->SmartCallback().call( + base::UTF16ToUTF8(source->GetTitle()).c_str()); + } + // We only notify clients if visible url actually changed, because on some // pages we would get notifications with flag INVALIDATE_TYPE_URL even when // visible url did not change. -- 2.7.4