From 9c663c08e4c66971c18d2b61b5cfeeb302773e1b Mon Sep 17 00:00:00 2001 From: younghajung <35090305+younghajung@users.noreply.github.com> Date: Mon, 12 Apr 2021 14:49:16 +0900 Subject: [PATCH] [WebView] Support request intercept (#2842) This patch is cherry-picked from API8. This patch adds internal APIs to support request intercept. Plus, adds APIs for remote inspect. Signed-off-by: yh106.jung --- .../Interop/Interop.ChromiumEwk.Context.cs | 15 ++- .../Interop.ChromiumEwk.InterceptRequest.cs | 41 ++++++++ src/Tizen.WebView/Tizen.WebView.sln | 15 +++ src/Tizen.WebView/Tizen.WebView/Context.cs | 48 ++++++++- .../Tizen.WebView/RequestInterceptor.cs | 116 +++++++++++++++++++++ 5 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 src/Tizen.WebView/Interop/Interop.ChromiumEwk.InterceptRequest.cs create mode 100644 src/Tizen.WebView/Tizen.WebView/RequestInterceptor.cs diff --git a/src/Tizen.WebView/Interop/Interop.ChromiumEwk.Context.cs b/src/Tizen.WebView/Interop/Interop.ChromiumEwk.Context.cs index 60021f4..4ca04fb 100644 --- a/src/Tizen.WebView/Interop/Interop.ChromiumEwk.Context.cs +++ b/src/Tizen.WebView/Interop/Interop.ChromiumEwk.Context.cs @@ -43,11 +43,24 @@ internal static partial class Interop [DllImport(Libraries.ChromiumEwk)] internal static extern void ewk_context_notify_low_memory(IntPtr context); - + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void DownloadStartCallback(string url, IntPtr userData); [DllImport(Libraries.ChromiumEwk)] internal static extern void ewk_context_did_start_download_callback_set(IntPtr context, DownloadStartCallback callback, IntPtr userData); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void InterceptRequestCallback(IntPtr context, IntPtr request, IntPtr userData); + + [DllImport(Libraries.ChromiumEwk)] + internal static extern void ewk_context_intercept_request_callback_set(IntPtr context, InterceptRequestCallback callback, IntPtr userData); + + [DllImport(Libraries.ChromiumEwk)] + internal static extern uint ewk_context_inspector_server_start(IntPtr context, uint port); + + [DllImport(Libraries.ChromiumEwk)] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool ewk_context_inspector_server_stop(IntPtr context); } } diff --git a/src/Tizen.WebView/Interop/Interop.ChromiumEwk.InterceptRequest.cs b/src/Tizen.WebView/Interop/Interop.ChromiumEwk.InterceptRequest.cs new file mode 100644 index 0000000..900b894 --- /dev/null +++ b/src/Tizen.WebView/Interop/Interop.ChromiumEwk.InterceptRequest.cs @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class ChromiumEwk + { + [DllImport(Libraries.ChromiumEwk)] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool ewk_intercept_request_ignore(IntPtr request); + + [DllImport(Libraries.ChromiumEwk)] + [return: MarshalAs(UnmanagedType.U1)] + internal unsafe static extern bool ewk_intercept_request_response_set(IntPtr request, string headers, byte* body, uint length); + + [DllImport(Libraries.ChromiumEwk, EntryPoint = "ewk_intercept_request_url_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] + internal static extern IntPtr _ewk_intercept_request_url_get(IntPtr request); + + internal static string ewk_intercept_request_url_get(IntPtr request) + { + IntPtr ptr = _ewk_intercept_request_url_get(request); + return Marshal.PtrToStringAnsi(ptr); + } + } +} diff --git a/src/Tizen.WebView/Tizen.WebView.sln b/src/Tizen.WebView/Tizen.WebView.sln index d4a4aad..0a065e3 100755 --- a/src/Tizen.WebView/Tizen.WebView.sln +++ b/src/Tizen.WebView/Tizen.WebView.sln @@ -7,6 +7,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.WebView", "Tizen.WebV EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElmSharp", "..\ElmSharp\ElmSharp.csproj", "{2F66C9E2-BE24-4096-96DA-F86CCFBBB059}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen", "..\Tizen\Tizen.csproj", "{B1B8B7CA-5E39-42BA-90EF-4F4DDA51F37C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Log", "..\Tizen.Log\Tizen.Log.csproj", "{7C6A8E2F-7DF0-4994-8230-FB1234F3DF4C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,8 +25,19 @@ Global {2F66C9E2-BE24-4096-96DA-F86CCFBBB059}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F66C9E2-BE24-4096-96DA-F86CCFBBB059}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F66C9E2-BE24-4096-96DA-F86CCFBBB059}.Release|Any CPU.Build.0 = Release|Any CPU + {B1B8B7CA-5E39-42BA-90EF-4F4DDA51F37C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1B8B7CA-5E39-42BA-90EF-4F4DDA51F37C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1B8B7CA-5E39-42BA-90EF-4F4DDA51F37C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1B8B7CA-5E39-42BA-90EF-4F4DDA51F37C}.Release|Any CPU.Build.0 = Release|Any CPU + {7C6A8E2F-7DF0-4994-8230-FB1234F3DF4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C6A8E2F-7DF0-4994-8230-FB1234F3DF4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C6A8E2F-7DF0-4994-8230-FB1234F3DF4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C6A8E2F-7DF0-4994-8230-FB1234F3DF4C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D2305D88-057A-4565-9162-906A79DEF26B} + EndGlobalSection EndGlobal diff --git a/src/Tizen.WebView/Tizen.WebView/Context.cs b/src/Tizen.WebView/Tizen.WebView/Context.cs index c1ed8ef..0f1a86f 100644 --- a/src/Tizen.WebView/Tizen.WebView/Context.cs +++ b/src/Tizen.WebView/Tizen.WebView/Context.cs @@ -53,7 +53,8 @@ namespace Tizen.WebView private CookieManager _cookieManager; private Interop.ChromiumEwk.DownloadStartCallback _downloadStartCallback; - + private Interop.ChromiumEwk.InterceptRequestCallback _interceptRequestCallback; + /// /// The delegate for handling download request. /// @@ -62,6 +63,14 @@ namespace Tizen.WebView [EditorBrowsable(EditorBrowsableState.Never)] public delegate void DownloadRequestDelegate(string url); + /// + /// The delegate for intercepting and handling a resource request. + /// + /// 8 + /// The object which can handle a intercepted request. + [EditorBrowsable(EditorBrowsableState.Never)] + public delegate void InterceptRequestDelegate(RequestInterceptor interceptor); + internal Context(IntPtr handle) { _handle = handle; @@ -138,5 +147,42 @@ namespace Tizen.WebView }; Interop.ChromiumEwk.ewk_context_did_start_download_callback_set(_handle, _downloadStartCallback, IntPtr.Zero); } + + /// + /// Sets the delegate function for intercepting a resource request. + /// + /// 8 + /// The delegate function for intercepting a resource request. + [EditorBrowsable(EditorBrowsableState.Never)] + public void SetInterceptRequestDelegate(InterceptRequestDelegate interceptRequestCb) + { + _interceptRequestCallback = (IntPtr context, IntPtr request, IntPtr userData) => + { + interceptRequestCb(new RequestInterceptor(request)); + }; + Interop.ChromiumEwk.ewk_context_intercept_request_callback_set(_handle, _interceptRequestCallback, IntPtr.Zero); + + } + + /// + /// Starts the inspector server. + /// + /// 8 + /// The port number. + [EditorBrowsable(EditorBrowsableState.Never)] + public uint StartInspectorServer(uint port) + { + return Interop.ChromiumEwk.ewk_context_inspector_server_start(_handle, port); + } + + /// + /// Stops the inspector server. + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void StopInspectorServer() + { + Interop.ChromiumEwk.ewk_context_inspector_server_stop(_handle); + } } } diff --git a/src/Tizen.WebView/Tizen.WebView/RequestInterceptor.cs b/src/Tizen.WebView/Tizen.WebView/RequestInterceptor.cs new file mode 100644 index 0000000..96c0e6a --- /dev/null +++ b/src/Tizen.WebView/Tizen.WebView/RequestInterceptor.cs @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Text; + +namespace Tizen.WebView +{ + /// + /// This class provides methods and properties to handle a interpected request. + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public class RequestInterceptor { + private const string ResponseHeaderTemplate = + "HTTP/1.1 {0} {1}\r\n" + + "Content-Type: {2}; charset={3}\r\n" + + "Content-Length: {4}\r\n"; + + private IntPtr _handle; + + internal RequestInterceptor(IntPtr handle) + { + _handle = handle; + } + + /// + /// The URL of the request. + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public Uri Url + { + get + { + return new Uri(Interop.ChromiumEwk.ewk_intercept_request_url_get(_handle)); + } + } + + /// + /// Sets headers and data for the response. + /// + /// 8 + /// Response's mime type. + /// Response's character encoding. + /// HTTP response status code. + /// HTTP response reason phrase. + /// Headers Map from HTTP header field names to field values. + /// The streiam that provides the response's data. + /// Thrown when is null. + /// Thrown when the native operation failed to set response. + [EditorBrowsable(EditorBrowsableState.Never)] + public void SetResponse(string mimeType, string encoding, int statusCode, string reasonPhrase, IDictionary responseHeaders, Stream data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + byte[] body; + using (MemoryStream ms = new MemoryStream()) + { + data.CopyTo(ms); + body = ms.ToArray(); + + var headers = String.Format(ResponseHeaderTemplate, statusCode, reasonPhrase, mimeType, encoding, body.Length); + if (responseHeaders != null) + { + foreach(var header in responseHeaders) + { + headers += $"{header.Key}: {header.Value}\r\n"; + } + } + headers += "\r\n"; + + unsafe + { + fixed (byte* bodyPtr = body) + { + var ret = Interop.ChromiumEwk.ewk_intercept_request_response_set(_handle, headers, bodyPtr, (uint)body.Length); + if (!ret) + { + throw new InvalidOperationException("Failed to set response."); + } + } + } + } + } + + /// + /// Ignores the request, so WebView will load it. + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void Ignore() + { + Interop.ChromiumEwk.ewk_intercept_request_ignore(_handle); + } + } +} -- 2.7.4