[M108 Migration][API] Implement Intercept Request EWK APIs 87/286587/4
authorayush.k123 <ayush.k123@samsung.com>
Tue, 10 Jan 2023 03:51:29 +0000 (09:21 +0530)
committerAyush Kumar <ayush.k123@samsung.com>
Tue, 10 Jan 2023 09:27:57 +0000 (14:57 +0530)
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 <ayush.k123@samsung.com>
39 files changed:
net/base/chunked_upload_data_stream.cc
net/base/chunked_upload_data_stream.h
net/base/elements_upload_data_stream.cc
net/base/elements_upload_data_stream.h
net/base/upload_bytes_element_reader.cc
net/base/upload_bytes_element_reader.h
net/base/upload_data_stream.cc
net/base/upload_data_stream.h
net/base/upload_element_reader.cc
net/base/upload_element_reader.h
net/base/upload_file_element_reader.cc
net/base/upload_file_element_reader.h
tizen_src/ewk/efl_integration/BUILD.gn
tizen_src/ewk/efl_integration/browser/intercept_request_params.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_efl.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/browser/network_service/proxying_url_loader_factory_efl.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/browser_context_efl.cc
tizen_src/ewk/efl_integration/browser_context_efl.h
tizen_src/ewk/efl_integration/content_browser_client_efl.cc
tizen_src/ewk/efl_integration/content_browser_client_efl.h
tizen_src/ewk/efl_integration/eweb_context.cc
tizen_src/ewk/efl_integration/eweb_context.h
tizen_src/ewk/efl_integration/network_delegate_efl.cc
tizen_src/ewk/efl_integration/network_delegate_efl.h
tizen_src/ewk/efl_integration/private/ewk_context_private.cc
tizen_src/ewk/efl_integration/private/ewk_context_private.h
tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/private/ewk_intercept_request_private.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/public/ewk_context.cc
tizen_src/ewk/efl_integration/public/ewk_intercept_request.cc
tizen_src/ewk/efl_integration/url_request_context_getter_efl.cc
tizen_src/ewk/efl_integration/url_request_context_getter_efl.h
tizen_src/ewk/efl_integration/url_request_interceptor_efl.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/url_request_interceptor_efl.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/url_request_job_efl.cc [new file with mode: 0644]
tizen_src/ewk/efl_integration/url_request_job_efl.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc

index 3c6521a..3b5aa8e 100644 (file)
@@ -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
index 36f87c4..ea0d5e9 100644 (file)
@@ -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;
index f034db8..c2d3e51 100644 (file)
@@ -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
index 3b13c25..ef296a1 100644 (file)
@@ -41,6 +41,11 @@ class NET_EXPORT ElementsUploadDataStream : public UploadDataStream {
       std::unique_ptr<UploadElementReader> 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;
index 19b195e..887c664 100644 (file)
@@ -70,4 +70,20 @@ UploadOwnedBytesElementReader::CreateWithString(const std::string& string) {
   return std::make_unique<UploadOwnedBytesElementReader>(&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
index bb8459b..54beba2 100644 (file)
@@ -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_;
index 2038f39..44e620f 100644 (file)
@@ -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
index ae538f8..65b88d4 100644 (file)
@@ -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.
index bc45006..b291dc5 100644 (file)
@@ -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
index 8877532..0770445 100644 (file)
@@ -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
index 8aabd90..decaa97 100644 (file)
@@ -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
index 197ef31..65b9b51 100644 (file)
@@ -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.
index 1a195b6..eb52644 100644 (file)
@@ -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 (file)
index 0000000..0f0b753
--- /dev/null
@@ -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 <string>
+
+#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 (file)
index 0000000..a7c85f9
--- /dev/null
@@ -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<network::mojom::URLLoaderClient> 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<network::mojom::URLLoaderClient> 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::HttpResponseHeaders>(
+      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<mojo::SimpleWatcher>(
+      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<char[]> 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<net::IOBuffer> buffer =
+      base::MakeRefCounted<net::IOBuffer>(length);
+  memcpy(buffer->data(), data, length);
+  buffers_.push(
+      base::MakeRefCounted<net::DrainableIOBuffer>(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<uint32_t>(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 (file)
index 0000000..50b79e9
--- /dev/null
@@ -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<network::mojom::URLLoaderClient> client);
+
+  ~ProxyingURLLoaderEfl() override = default;
+
+ private:
+  // network::mojom::URLLoader:
+  void FollowRedirect(
+      const std::vector<std::string>& removed_headers,
+      const net::HttpRequestHeaders& modified_headers,
+      const net::HttpRequestHeaders& modified_cors_exempt_headers,
+      const absl::optional<GURL>& 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<network::mojom::URLLoaderClient> 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<network::mojom::URLLoader> binding_;
+  mojo::Remote<network::mojom::URLLoaderClient> client_;
+  mojo::ScopedDataPipeProducerHandle producer_handle_;
+  std::unique_ptr<mojo::SimpleWatcher> producer_handle_watcher_;
+
+  base::queue<scoped_refptr<net::DrainableIOBuffer>> buffers_;
+  bool read_complete_ = false;
+
+  base::WeakPtrFactory<ProxyingURLLoaderEfl> 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 (file)
index 0000000..df5dea7
--- /dev/null
@@ -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<ResourceRequestBody> 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<ResourceRequestBody> resource_request_body_;
+};
+
+}  // namespace
+
+// static
+void ProxyingURLLoaderFactoryEfl::CreateProxy(
+    content::BrowserContextEfl::ResourceContextEfl* resource_context,
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
+    mojo::PendingRemote<network::mojom::URLLoaderFactory>
+        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<network::mojom::URLLoaderFactory> loader_receiver,
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> 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<net::UploadDataStream>
+ProxyingURLLoaderFactoryEfl::BuildUploadDataStream(
+    network::ResourceRequestBody* body,
+    base::SingleThreadTaskRunner* file_task_runner) {
+  std::vector<std::unique_ptr<net::UploadElementReader>> element_readers;
+  for (const auto& element : *body->elements()) {
+    switch (element.type()) {
+      case network::mojom::DataElementDataView::Tag::kBytes:
+        element_readers.push_back(std::make_unique<BytesElementReader>(
+            body, element.As<network::DataElementBytes>()));
+        break;
+      case network::mojom::DataElementDataView::Tag::kFile:
+        element_readers.push_back(std::make_unique<FileElementReader>(
+            body, file_task_runner, element.As<network::DataElementFile>()));
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  return std::make_unique<net::ElementsUploadDataStream>(
+      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<network::mojom::URLLoaderClient> client,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  InterceptRequestParams params;
+  params.url = request.url;
+  params.method = request.method;
+  params.headers = request.headers;
+
+  std::unique_ptr<net::UploadDataStream> 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<network::mojom::URLLoaderFactory> 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 (file)
index 0000000..2626a3d
--- /dev/null
@@ -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<network::mojom::URLLoaderFactory> loader_receiver,
+      mojo::PendingRemote<network::mojom::URLLoaderFactory>
+          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<network::mojom::URLLoaderClient> client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      override;
+  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory>
+                 loader_receiver) override;
+
+  ProxyingURLLoaderFactoryEfl(
+      content::BrowserContextEfl::ResourceContextEfl* resource_context,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
+      mojo::PendingRemote<network::mojom::URLLoaderFactory>
+          target_factory_remote);
+
+  ProxyingURLLoaderFactoryEfl(const ProxyingURLLoaderFactoryEfl&) = delete;
+  ProxyingURLLoaderFactoryEfl& operator=(const ProxyingURLLoaderFactoryEfl&) =
+      delete;
+
+  void OnTargetFactoryError();
+  void OnProxyBindingError();
+  std::unique_ptr<net::UploadDataStream> BuildUploadDataStream(
+      network::ResourceRequestBody* body,
+      base::SingleThreadTaskRunner* file_task_runner);
+
+  content::BrowserContextEfl::ResourceContextEfl* resource_context_;
+
+  mojo::ReceiverSet<network::mojom::URLLoaderFactory> proxy_receivers_;
+  mojo::Remote<network::mojom::URLLoaderFactory> target_factory_;
+
+  base::WeakPtrFactory<ProxyingURLLoaderFactoryEfl> weak_ptr_factory_;
+};
+
+#endif  // PROXYING_URL_LOADER_FACTORY_EFL_H_
index 8145294..ff0ea68 100644 (file)
@@ -52,7 +52,8 @@ static void CreateNetworkDelegateOnIOThread(BrowserContextEfl* context,
 
 BrowserContextEfl::ResourceContextEfl::ResourceContextEfl(
     scoped_refptr<CookieManager> 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<CookieManager>
 BrowserContextEfl::ResourceContextEfl::GetCookieManager() const {
   return cookie_manager_;
index 90ac364..1f88ac0 100644 (file)
@@ -50,10 +50,31 @@ class BrowserContextEfl : public BrowserContext,
 
     scoped_refptr<CookieManager> 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<CookieManager> 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);
index 05d477a..bbe1fa0 100644 (file)
@@ -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<LoginDelegate> 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<int64_t> navigation_id,
+    ukm::SourceIdObj ukm_source_id,
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
+    mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
+        header_client,
+    bool* bypass_redirect_checks,
+    bool* disable_secure_dns,
+    network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
+  auto* resource_context_efl =
+      static_cast<content::BrowserContextEfl*>(browser_context)
+          ->GetResourceContextEfl();
+  if (!resource_context_efl->HasInterceptRequestCallback())
+    return false;
+
+  auto proxied_receiver = std::move(*factory_receiver);
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> 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
index 9071a2d..b0615ec 100644 (file)
@@ -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<int64_t> navigation_id,
+      ukm::SourceIdObj ukm_source_id,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
+      mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
+          header_client,
+      bool* bypass_redirect_checks,
+      bool* disable_secure_dns,
+      network::mojom::URLLoaderFactoryOverridePtr* factory_override) override;
+
   scoped_refptr<network::SharedURLLoaderFactory>
   GetSystemSharedURLLoaderFactory() override;
 
index 10786a8..8e9fbda 100644 (file)
@@ -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);
+}
index f600b6b..65a4168 100644 (file)
@@ -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);
index 3b94127..0db1df1 100644 (file)
@@ -13,7 +13,8 @@ namespace net {
 
 NetworkDelegateEfl::NetworkDelegateEfl(
     base::WeakPtr<CookieManager> 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
index 3048563..1281ed9 100644 (file)
@@ -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<CookieManager> 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<CookieManager> cookie_manager_;
 };
 
index 95ea267..dce115c 100644 (file)
@@ -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);
+}
index 2d29d69..c4fcb01 100644 (file)
@@ -133,6 +133,10 @@ struct Ewk_Context : public base::RefCounted<Ewk_Context> {
     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 (file)
index 0000000..2de0d02
--- /dev/null
@@ -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 <cstring>
+
+#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<std::string, std::string>(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<Eina_Hash_Tuple*>(data);
+    std::string field_name = static_cast<const char*>(tuple->key);
+    std::string field_value = static_cast<const char*>(tuple->data);
+    response_headers_map_.push_back(std::pair<std::string, std::string>(
+        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 (file)
index 0000000..a829724
--- /dev/null
@@ -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 <cstddef>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <Eina.h>
+
+#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<char[]> 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<char[]> response_body_;
+  size_t response_body_length_;
+  int response_status_code_;
+  std::string response_status_text_;
+  std::vector<std::pair<std::string, std::string>> response_headers_map_;
+
+  bool ignored_;
+  bool chunked_write_;
+  base::AtomicFlag chunked_write_early_exit_;
+};
+
+#endif  // EWK_EFL_INTEGRATION_PRIVATE_EWK_INTERCEPT_REQUEST_PRIVATE_H_
index c7c1e79..9b13510 100644 (file)
@@ -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();
index 1c70b4f..b29fbb8 100644 (file)
 
 #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();
 }
index 844ea4c..b79993d 100644 (file)
@@ -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<net::URLRequestJobFactory> 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<net::NetworkDelegateEfl*>(network_delegate_.get()))
+        ->SetInterceptRequestCallback(ewk_context, callback, user_data);
+  }
+}
+
 };  // namespace content
index 111a753..112d191 100644 (file)
@@ -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<net::NetworkDelegate> network_delegate_;
   std::unique_ptr<net::CertVerifier> cert_verifier_;
   std::unique_ptr<net::URLRequestContext> url_request_context_;
+  std::vector<std::unique_ptr<net::URLRequestInterceptor>>
+      request_interceptors_;
   base::WeakPtrFactory<URLRequestContextGetterEfl> 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 (file)
index 0000000..284e0ac
--- /dev/null
@@ -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<net::URLRequestJob>
+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<net::NetworkDelegateEfl*>(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<net::UploadDataStream*>(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<base::SupportsUserData::Data>(
+                           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 (file)
index 0000000..7e62975
--- /dev/null
@@ -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<net::URLRequestJob> 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 (file)
index 0000000..fc05f6d
--- /dev/null
@@ -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 <algorithm>
+#include <cstring>
+
+#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<int>(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<int>(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<int>(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<size_t>(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<size_t>(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 (file)
index 0000000..a9457ca
--- /dev/null
@@ -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 <cstddef>
+#include <queue>
+#include <string>
+
+#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<const char[]> 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<char[]> response_body_;
+
+  size_t available_data_;
+  std::queue<Chunk> 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_
index d86cec1..844ff2c 100644 (file)
@@ -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<EWebViewCallbacks::TitleChange>().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.