MockWebView.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. // Copyright (c) 2022 Vuplex Inc. All rights reserved.
  2. //
  3. // Licensed under the Vuplex Commercial Software Library License, you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // https://vuplex.com/commercial-library-license
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #pragma warning disable CS0067
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Threading.Tasks;
  18. using UnityEngine;
  19. using Vuplex.WebView.Internal;
  20. namespace Vuplex.WebView {
  21. /// <summary>
  22. /// Mock IWebView implementation used for running in the Unity editor.
  23. /// </summary>
  24. /// <remarks>
  25. /// MockWebView logs messages to the console to indicate when its methods are
  26. /// called, but it doesn't actually load or render web content.
  27. /// </remarks>
  28. partial class MockWebView : MonoBehaviour, IWebView {
  29. public event EventHandler CloseRequested;
  30. public event EventHandler<ConsoleMessageEventArgs> ConsoleMessageLogged;
  31. public event EventHandler<FocusedInputFieldChangedEventArgs> FocusedInputFieldChanged;
  32. public event EventHandler<ProgressChangedEventArgs> LoadProgressChanged;
  33. public event EventHandler<EventArgs<string>> MessageEmitted;
  34. public event EventHandler PageLoadFailed;
  35. public event EventHandler<EventArgs<string>> TitleChanged;
  36. public event EventHandler<UrlChangedEventArgs> UrlChanged;
  37. public bool IsDisposed { get; private set; }
  38. public bool IsInitialized { get; private set; }
  39. public List<string> PageLoadScripts { get; } = new List<string>();
  40. public WebPluginType PluginType { get; } = WebPluginType.Mock;
  41. public Vector2Int Size { get; private set; }
  42. public Texture2D Texture { get; private set; }
  43. public string Title { get; private set; } = "";
  44. public string Url { get; private set; } = "";
  45. public Task<bool> CanGoBack() {
  46. _log("CanGoBack()");
  47. OnCanGoBack();
  48. return Task.FromResult(false);
  49. }
  50. public Task<bool> CanGoForward() {
  51. _log("CanGoForward()");
  52. OnCanGoForward();
  53. return Task.FromResult(false);
  54. }
  55. public Task<byte[]> CaptureScreenshot() {
  56. _log("CaptureScreenshot()");
  57. OnCaptureScreenshot();
  58. return Task.FromResult(new byte[0]);
  59. }
  60. public void Click(int xInPixels, int yInPixels, bool preventStealingFocus = false) {
  61. var pointIsValid = xInPixels >= 0 && xInPixels <= Size.x && yInPixels >= 0 && yInPixels <= Size.y;
  62. if (!pointIsValid) {
  63. throw new ArgumentException($"The point provided ({xInPixels}px, {yInPixels}px) is not within the bounds of the webview (width: {Size.x}px, height: {Size.y}px).");
  64. }
  65. _log($"Click({xInPixels}, {yInPixels}, {preventStealingFocus})");
  66. }
  67. public void Click(Vector2 point, bool preventStealingFocus = false) {
  68. _assertValidNormalizedPoint(point);
  69. _log($"Click({point.ToString("n4")}, {preventStealingFocus})");
  70. }
  71. public void Copy() {
  72. _log("Copy()");
  73. OnCopy();
  74. }
  75. public Material CreateMaterial() {
  76. #if UNITY_SERVER
  77. return null;
  78. #else
  79. var material = new Material(Resources.Load<Material>("MockViewportMaterial"));
  80. // Create a copy of the texture so that an Exception won't be thrown when the prefab destroys it.
  81. // Also, explicitly use RGBA32 here so that the texture will be converted to RGBA32 if the editor
  82. // imported it as a different format. For example, when Texture Compression is set to ASTC in Android build settings,
  83. // the editor automatically imports new textures as ASTC, even though the Windows editor doesn't support that format.
  84. var texture = new Texture2D(material.mainTexture.width, material.mainTexture.height, TextureFormat.RGBA32, true);
  85. texture.SetPixels((material.mainTexture as Texture2D).GetPixels());
  86. texture.Apply();
  87. material.mainTexture = texture;
  88. return material;
  89. #endif
  90. }
  91. public void Cut() {
  92. _log("Cut()");
  93. OnCut();
  94. }
  95. public static Task<bool> DeleteCookies(string url, string cookieName = null) {
  96. if (url == null) {
  97. throw new ArgumentException("The url cannot be null.");
  98. }
  99. _log($"DeleteCookies(\"{url}\", \"{cookieName}\")");
  100. return Task.FromResult(true);
  101. }
  102. public void Dispose() {
  103. IsDisposed = true;
  104. _log("Dispose()");
  105. if (this != null) {
  106. Destroy(gameObject);
  107. }
  108. }
  109. public Task<string> ExecuteJavaScript(string javaScript) {
  110. var taskSource = new TaskCompletionSource<string>();
  111. ExecuteJavaScript(javaScript, taskSource.SetResult);
  112. return taskSource.Task;
  113. }
  114. public void ExecuteJavaScript(string javaScript, Action<string> callback) {
  115. _log($"ExecuteJavaScript(\"{_truncateIfNeeded(javaScript)}\")");
  116. callback("");
  117. OnExecuteJavaScript();
  118. }
  119. public static Task<Cookie[]> GetCookies(string url, string cookieName = null) {
  120. if (url == null) {
  121. throw new ArgumentException("The url cannot be null.");
  122. }
  123. _log($"GetCookies(\"{url}\", \"{cookieName}\")");
  124. var taskSource = new TaskCompletionSource<Cookie[]>();
  125. return Task.FromResult(new Cookie[0]);
  126. }
  127. public Task<byte[]> GetRawTextureData() {
  128. _log("GetRawTextureData()");
  129. OnGetRawTextureData();
  130. return Task.FromResult(new byte[0]);
  131. }
  132. public void GoBack() {
  133. _log("GoBack()");
  134. OnGoBack();
  135. }
  136. public void GoForward() {
  137. _log("GoForward()");
  138. OnGoForward();
  139. }
  140. public Task Init(int width, int height) {
  141. Texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
  142. Size = new Vector2Int(width, height);
  143. IsInitialized = true;
  144. DontDestroyOnLoad(gameObject);
  145. _log($"Init() width: {width.ToString("n4")}, height: {height.ToString("n4")}");
  146. return Task.FromResult(true);
  147. }
  148. public static MockWebView Instantiate() => new GameObject("MockWebView").AddComponent<MockWebView>();
  149. public virtual void LoadHtml(string html) {
  150. var truncatedHtml = _truncateIfNeeded(html);
  151. Url = truncatedHtml;
  152. _log($"LoadHtml(\"{truncatedHtml}...\")");
  153. OnLoadHtml();
  154. _handlePageLoad(truncatedHtml);
  155. }
  156. public virtual void LoadUrl(string url) => LoadUrl(url, null);
  157. public virtual void LoadUrl(string url, Dictionary<string, string> additionalHttpHeaders) {
  158. Url = url;
  159. _log($"LoadUrl(\"{url}\")");
  160. OnLoadUrl(url);
  161. _handlePageLoad(url);
  162. }
  163. public Vector2Int NormalizedToPoint(Vector2 normalizedPoint) {
  164. return new Vector2Int((int)(normalizedPoint.x * (float)Size.x), (int)(normalizedPoint.y * (float)Size.y));
  165. }
  166. public void Paste() {
  167. _log("Paste()");
  168. OnPaste();
  169. }
  170. public Vector2 PointToNormalized(int xInPixels, int yInPixels) {
  171. return new Vector2((float)xInPixels / Size.x, (float)yInPixels / Size.y);
  172. }
  173. public void PostMessage(string data) => _log($"PostMessage(\"{data}\")");
  174. public void Reload() => _log("Reload()");
  175. public void Resize(int width, int height) {
  176. Size = new Vector2Int(width, height);
  177. _log($"Resize({width.ToString("n4")}, {height.ToString("n4")})");
  178. }
  179. public void Scroll(int scrollDeltaX, int scrollDeltaY) => _log($"Scroll({scrollDeltaX}, {scrollDeltaY})");
  180. public void Scroll(Vector2 delta) => _log($"Scroll({delta.ToString("n4")})");
  181. public void Scroll(Vector2 delta, Vector2 point) {
  182. _assertValidNormalizedPoint(point);
  183. _log($"Scroll({delta.ToString("n4")}, {point.ToString("n4")})");
  184. }
  185. public void SelectAll() => _log("SelectAll()");
  186. public void SendKey(string input) => _log($"SendKey(\"{input}\")");
  187. public static Task<bool> SetCookie(Cookie cookie) {
  188. if (cookie == null) {
  189. throw new ArgumentException("Cookie cannot be null.");
  190. }
  191. if (!cookie.IsValid) {
  192. throw new ArgumentException("Cannot set invalid cookie: " + cookie);
  193. }
  194. _log($"SetCookie({cookie}");
  195. return Task.FromResult(true);
  196. }
  197. public void SetFocused(bool focused) => _log($"SetFocused({focused})");
  198. public void SetRenderingEnabled(bool enabled) => _log($"SetRenderingEnabled({enabled})");
  199. public void StopLoad() => _log("StopLoad()");
  200. public Task WaitForNextPageLoadToFinish() {
  201. if (_pageLoadFinishedTaskSource == null) {
  202. _pageLoadFinishedTaskSource = new TaskCompletionSource<bool>();
  203. }
  204. return _pageLoadFinishedTaskSource.Task;
  205. }
  206. public void ZoomIn() => _log("ZoomIn()");
  207. public void ZoomOut() => _log("ZoomOut()");
  208. TaskCompletionSource<bool> _pageLoadFinishedTaskSource;
  209. // Partial methods implemented by other 3D WebView packages
  210. // to provide platform-specific warnings in the editor.
  211. partial void OnCanGoBack();
  212. partial void OnCanGoForward();
  213. partial void OnCaptureScreenshot();
  214. partial void OnCopy();
  215. partial void OnCut();
  216. partial void OnExecuteJavaScript();
  217. partial void OnGetRawTextureData();
  218. partial void OnGoBack();
  219. partial void OnGoForward();
  220. partial void OnLoadHtml();
  221. partial void OnLoadUrl(string url);
  222. partial void OnPaste();
  223. void _assertValidNormalizedPoint(Vector2 normalizedPoint) {
  224. var isValid = normalizedPoint.x >= 0f && normalizedPoint.x <= 1f && normalizedPoint.y >= 0f && normalizedPoint.y <= 1f;
  225. if (!isValid) {
  226. throw new ArgumentException($"The normalized point provided is invalid. The x and y values of normalized points must be in the range of [0, 1], but the value provided was {normalizedPoint.ToString("n4")}. For more info, please see https://support.vuplex.com/articles/normalized-points");
  227. }
  228. }
  229. void _handlePageLoad(string url) {
  230. UrlChanged?.Invoke(this, new UrlChangedEventArgs(url, UrlActionType.Load));
  231. LoadProgressChanged?.Invoke(this, new ProgressChangedEventArgs(ProgressChangeType.Started, 0));
  232. LoadProgressChanged?.Invoke(this, new ProgressChangedEventArgs(ProgressChangeType.Finished, 1));
  233. _pageLoadFinishedTaskSource?.SetResult(true);
  234. _pageLoadFinishedTaskSource = null;
  235. }
  236. static void _log(string message) {
  237. #if !VUPLEX_DISABLE_MOCK_WEBVIEW_LOGGING
  238. WebViewLogger.Log("[MockWebView] " + message);
  239. #endif
  240. }
  241. string _truncateIfNeeded(string str) {
  242. var maxLength = 25;
  243. if (str.Length <= maxLength) {
  244. return str;
  245. }
  246. return str.Substring(0, maxLength) + "...";
  247. }
  248. [Obsolete(ObsoletionMessages.Blur, true)]
  249. public void Blur() {}
  250. [Obsolete(ObsoletionMessages.CanGoBack, true)]
  251. public void CanGoBack(Action<bool> callback) {}
  252. [Obsolete(ObsoletionMessages.CanGoForward, true)]
  253. public void CanGoForward(Action<bool> callback) {}
  254. [Obsolete(ObsoletionMessages.CaptureScreenshot, true)]
  255. public void CaptureScreenshot(Action<byte[]> callback) {}
  256. [Obsolete(ObsoletionMessages.DisableViewUpdates, true)]
  257. public void DisableViewUpdates() {}
  258. [Obsolete(ObsoletionMessages.EnableViewUpdates, true)]
  259. public void EnableViewUpdates() {}
  260. [Obsolete(ObsoletionMessages.Focus, true)]
  261. public void Focus() {}
  262. [Obsolete(ObsoletionMessages.GetRawTextureData, true)]
  263. public void GetRawTextureData(Action<byte[]> callback) {}
  264. [Obsolete(ObsoletionMessages.HandleKeyboardInput)]
  265. public void HandleKeyboardInput(string key) => SendKey(key);
  266. [Obsolete(ObsoletionMessages.Init, true)]
  267. public void Init(Texture2D texture, float width, float height) {}
  268. [Obsolete(ObsoletionMessages.Init2, true)]
  269. public void Init(Texture2D texture, float width, float height, Texture2D videoTexture) {}
  270. [Obsolete(ObsoletionMessages.Resolution, true)]
  271. public float Resolution { get; }
  272. [Obsolete(ObsoletionMessages.SetResolution, true)]
  273. public void SetResolution(float pixelsPerUnityUnit) {}
  274. [Obsolete(ObsoletionMessages.SizeInPixels)]
  275. public Vector2 SizeInPixels { get { return (Vector2)Size; }}
  276. [Obsolete(ObsoletionMessages.VideoRectChanged, true)]
  277. public event EventHandler<EventArgs<Rect>> VideoRectChanged;
  278. [Obsolete(ObsoletionMessages.VideoTexture, true)]
  279. public Texture2D VideoTexture { get; }
  280. }
  281. }