[WIP][WebView] Support request intecept (#2486)
authoryounghajung <35090305+younghajung@users.noreply.github.com>
Wed, 17 Feb 2021 07:31:23 +0000 (16:31 +0900)
committerGitHub <noreply@github.com>
Wed, 17 Feb 2021 07:31:23 +0000 (16:31 +0900)
This patch adds internal APIs to support request intercept.
Plus, adds APIs for remote inspect.

Signed-off-by: yh106.jung <yh106.jung@samsung.com>
src/Tizen.WebView/Interop/Interop.ChromiumEwk.Context.cs
src/Tizen.WebView/Interop/Interop.ChromiumEwk.InterceptRequest.cs [new file with mode: 0644]
src/Tizen.WebView/Tizen.WebView.sln
src/Tizen.WebView/Tizen.WebView/Context.cs
src/Tizen.WebView/Tizen.WebView/RequestInterceptor.cs [new file with mode: 0644]

index 60021f4871aa156471a56d4677f32890bf7198c5..4ca04fb04e607f4d7fe42c809d400ed9ef35262a 100644 (file)
@@ -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 (file)
index 0000000..900b894
--- /dev/null
@@ -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);
+        }
+    }
+}
index d4a4aadededf8fca6b201821459432491229d73f..0a065e34ca83fb93c84e9f79d8c2b3a2d729a5aa 100755 (executable)
@@ -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
index c1ed8efa8517943d60bf3e88b1e61f1f88e3bb62..0f1a86fbee4a9dba6467bd56b84d422692d21383 100644 (file)
@@ -53,7 +53,8 @@ namespace Tizen.WebView
         private CookieManager _cookieManager;
 
         private Interop.ChromiumEwk.DownloadStartCallback _downloadStartCallback;
-        
+        private Interop.ChromiumEwk.InterceptRequestCallback _interceptRequestCallback;
+
         /// <summary>
         /// The delegate for handling download request.
         /// </summary>
@@ -62,6 +63,14 @@ namespace Tizen.WebView
         [EditorBrowsable(EditorBrowsableState.Never)]
         public delegate void DownloadRequestDelegate(string url);
 
+        /// <summary>
+        /// The delegate for intercepting and handling a resource request.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        /// <param name="interceptor"> The object which can handle a intercepted request. </param>
+        [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);
         }
+
+        /// <summary>
+        /// Sets the delegate function for intercepting a resource request.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        /// <param name="interceptRequestCb"> The delegate function for intercepting a resource request. </param>
+        [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);
+
+        }
+
+        /// <summary>
+        /// Starts the inspector server.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        /// <param name="port"> The port number. </param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public uint StartInspectorServer(uint port)
+        {
+            return Interop.ChromiumEwk.ewk_context_inspector_server_start(_handle, port);
+        }
+
+        /// <summary>
+        /// Stops the inspector server.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        [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 (file)
index 0000000..96c0e6a
--- /dev/null
@@ -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
+{
+    /// <summary>
+    /// This class provides methods and properties to handle a interpected request.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    [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;
+        }
+
+        /// <summary>
+        /// The URL of the request.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Uri Url
+        {
+            get
+            {
+                return new Uri(Interop.ChromiumEwk.ewk_intercept_request_url_get(_handle));
+            }
+        }
+
+        /// <summary>
+        /// Sets headers and data for the response.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        /// <param name="mimeType"> Response's mime type. </param>
+        /// <param name="encoding"> Response's character encoding. </param>
+        /// <param name="statusCode"> HTTP response status code. </param>
+        /// <param name="reasonPhrase"> HTTP response reason phrase. </param>
+        /// <param name="responseHeaders"> Headers Map from HTTP header field names to field values. </param>
+        /// <param name="data"> The streiam that provides the response's data. </param>
+        /// <exception cref="ArgumentNullException">Thrown when <paramref name="data"/> is null.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the native operation failed to set response.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SetResponse(string mimeType, string encoding, int statusCode, string reasonPhrase, IDictionary<string, string> 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.");
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Ignores the request, so WebView will load it.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Ignore()
+        {
+            Interop.ChromiumEwk.ewk_intercept_request_ignore(_handle);
+        }
+    }
+}