WebGLWebView.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. // Copyright (c) 2024 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. #if UNITY_WEBGL && !UNITY_EDITOR
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Runtime.InteropServices;
  18. using System.Threading.Tasks;
  19. using UnityEngine;
  20. using Vuplex.WebView.Internal;
  21. namespace Vuplex.WebView {
  22. /// <summary>
  23. /// WebGLWebView is the IWebView implementation used by 2D WebView for WebGL.
  24. /// It also includes additional APIs for WebGL-specific functionality.
  25. /// </summary>
  26. public class WebGLWebView : BaseWebView,
  27. IWebView,
  28. IWithNative2DMode {
  29. /// <summary>
  30. /// Gets the unique `id` attribute of the webview's &lt;iframe&gt; element.
  31. /// </summary>
  32. /// <example>
  33. /// <code>
  34. /// await canvasWebViewPrefab.WaitUntilInitialized();
  35. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  36. /// var webGLWebView = canvasWebViewPrefab.WebView as WebGLWebView;
  37. /// Debug.Log("IFrame ID: " + webGLWebView.IFrameElementID);
  38. /// #endif
  39. /// </code>
  40. /// </example>
  41. public string IFrameElementID { get; private set; }
  42. /// <see cref="IWithNative2DMode"/>
  43. public bool Native2DModeEnabled { get { return _native2DModeEnabled; }}
  44. public WebPluginType PluginType { get; } = WebPluginType.WebGL;
  45. /// <see cref="IWithNative2DMode"/>
  46. public Rect Rect { get { return _rect; }}
  47. /// <see cref="IWithNative2DMode"/>
  48. public bool Visible { get; private set; }
  49. /// <seealso cref="IWithNative2DMode"/>
  50. public void BringToFront() {
  51. _assertValidState();
  52. _assertNative2DModeEnabled();
  53. WebView_bringToFront(_nativeWebViewPtr);
  54. }
  55. /// <summary>
  56. /// Indicates whether 2D WebView can access the content in the
  57. /// webview's iframe. If the iframe's content can't be accessed,
  58. /// then most of the IWebView APIs become disabled. For more
  59. /// information, please see [this article](https://support.vuplex.com/articles/webgl-limitations#cross-origin-limitation).
  60. /// </summary>
  61. /// <example>
  62. /// <code>
  63. /// await canvasWebViewPrefab.WaitUntilInitialized();
  64. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  65. /// var webGLWebView = canvasWebViewPrefab.WebView as WebGLWebView;
  66. /// if (webGLWebView.CanAccessIFrameContent()) {
  67. /// Debug.Log("The iframe content can be accessed 👍");
  68. /// }
  69. /// #endif
  70. /// </code>
  71. /// </example>
  72. public bool CanAccessIFrameContent() {
  73. _assertValidState();
  74. return WebView_canAccessIFrameContent(_nativeWebViewPtr);
  75. }
  76. public override Task<bool> CanGoBack() => _canGoBackOrForward("CanGoBack");
  77. public override Task<bool> CanGoForward() => _canGoBackOrForward("CanGoForward");
  78. public override Task<byte[]> CaptureScreenshot() {
  79. WebGLWarnings.LogCaptureScreenshotError();
  80. return Task.FromResult(new byte[0]);
  81. }
  82. public override void Click(int xInPixels, int yInPixels, bool preventStealingFocus = false) {
  83. if (_verifyIFrameCanBeAccessed("Click")) {
  84. base.Click(xInPixels, yInPixels, preventStealingFocus);
  85. }
  86. }
  87. public override void Copy() => WebGLWarnings.LogCopyError();
  88. public override void Cut() => WebGLWarnings.LogCutError();
  89. public override void ExecuteJavaScript(string javaScript, Action<string> callback) {
  90. _assertValidState();
  91. if (_verifyIFrameCanBeAccessed("ExecuteJavaScript")) {
  92. var result = WebView_executeJavaScriptSync(_nativeWebViewPtr, javaScript);
  93. callback?.Invoke(result);
  94. }
  95. }
  96. /// <summary>
  97. /// Executes the given JavaScript locally in the Unity app's window and returns the result.
  98. /// </summary>
  99. /// <example>
  100. /// <code>
  101. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  102. /// // Gets the Unity app's web page title.
  103. /// var title = WebGLWebView.ExecuteJavaScriptLocally("document.title");
  104. /// Debug.Log("Title: " + title);
  105. /// #endif
  106. /// </code>
  107. /// </example>
  108. public static string ExecuteJavaScriptLocally(string javaScript) => WebView_executeJavaScriptLocally(javaScript);
  109. public static WebGLWebView Instantiate() => new GameObject().AddComponent<WebGLWebView>();
  110. public override Task<byte[]> GetRawTextureData() {
  111. WebGLWarnings.LogGetRawTextureDataError();
  112. return Task.FromResult(new byte[0]);
  113. }
  114. public override void GoBack() {
  115. if (_verifyIFrameCanBeAccessed("GoBack")) {
  116. base.GoBack();
  117. }
  118. }
  119. public override void GoForward() {
  120. if (_verifyIFrameCanBeAccessed("GoForward")) {
  121. base.GoForward();
  122. }
  123. }
  124. public Task Init(int width, int height) {
  125. var message = "2D WebView for WebGL only supports 2D, so Native 2D Mode must be enabled." + WebGLWarnings.GetArticleLinkText(false);
  126. WebViewLogger.LogError(message);
  127. throw new NotImplementedException(message);
  128. }
  129. /// <see cref="IWithNative2DMode"/>
  130. public async Task InitInNative2DMode(Rect rect) {
  131. _native2DModeEnabled = true;
  132. _rect = rect;
  133. Visible = true;
  134. await _initBase((int)rect.width, (int)rect.height, createTexture: false);
  135. // Set IFrameElementID *after* base.Init() because it sets gameObject.name.
  136. IFrameElementID = gameObject.name;
  137. // Prior to Unity 2019.3, Unity's UI used a pixel density of
  138. // 1 instead of using window.devicePixelRatio.
  139. #if UNITY_2019_3_OR_NEWER
  140. var ignoreDevicePixelRatio = false;
  141. #else
  142. var ignoreDevicePixelRatio = true;
  143. #endif
  144. _nativeWebViewPtr = WebView_newInNative2DMode(
  145. gameObject.name,
  146. (int)rect.x,
  147. (int)rect.y,
  148. (int)rect.width,
  149. (int)rect.height,
  150. ignoreDevicePixelRatio
  151. );
  152. if (_nativeWebViewPtr == IntPtr.Zero) {
  153. throw new TrialExpiredException("Your trial of 2D WebView for WebGL has expired. Please purchase a license to continue using it.");
  154. }
  155. }
  156. public override void LoadHtml(string html) {
  157. WebGLWarnings.LogLoadHtmlWarning();
  158. base.LoadHtml(html);
  159. }
  160. public override void LoadUrl(string url, Dictionary<string, string> additionalHttpHeaders) {
  161. WebViewLogger.LogWarning("LoadUrl(url, headers) was called, but 2D WebView for WebGL is unable to send additional headers when loading a URL due to browser limitations. So, the URL will be loaded without additional headers using LoadUrl(url) instead.");
  162. LoadUrl(url);
  163. }
  164. public override void Paste() => WebGLWarnings.LogPasteError();
  165. public override void PostMessage(string message) {
  166. _assertValidState();
  167. WebView_postMessage(_nativeWebViewPtr, message);
  168. }
  169. public override void Scroll(int scrollDeltaXInPixels, int scrollDeltaYInPixels) {
  170. if (_verifyIFrameCanBeAccessed("Scroll")) {
  171. base.Scroll(scrollDeltaXInPixels, scrollDeltaYInPixels);
  172. }
  173. }
  174. public override void Scroll(Vector2 normalizedScrollDelta, Vector2 normalizedPoint) {
  175. if (_verifyIFrameCanBeAccessed("Scroll")) {
  176. base.Scroll(normalizedScrollDelta, normalizedPoint);
  177. }
  178. }
  179. public override void SendKey(string key) {
  180. if (_verifyIFrameCanBeAccessed("SendKey")) {
  181. base.SendKey(key);
  182. }
  183. }
  184. /// <summary>
  185. /// Overrides the value of `devicePixelRatio` that 2D WebView uses to determine the correct
  186. /// size and coordinates of webviews.
  187. /// </summary>
  188. /// <summary>
  189. /// Starting in Unity 2019.3, Unity scales the UI by default based on the browser's window.devicePixelRatio
  190. /// value. However, it's possible for an application to override the `devicePixelRatio` value
  191. /// by passing a value for `config.devicePixelRatio` to Unity's `createUnityInstance()` JavaScript function. In some
  192. /// versions of Unity, the default index.html template sets `config.devicePixelRatio = 1` on mobile.
  193. /// In order for 2D WebView to size and position webviews correctly, it must determine the value of `devicePixelRatio`
  194. /// to use. Since there's no API to get a reference to the Unity instance that the application created with `createUnityInstance()`,
  195. /// 2D WebView tries to detect if `config.devicePixelRatio` was passed to `createUnityInstance()` by checking for the presence of a
  196. /// global `config` JavaScript variable. If there is a global variable named `config` with a `devicePixelRatio` property, then 2D WebView
  197. /// uses that value, otherwise it defaults to using `window.devicePixelRatio`. This approach works for Unity's default
  198. /// index.html templates, but it may not work if the application uses a custom HTML template or changes the name of the `config`
  199. /// variable in the default template. In those cases, the application can use this method to pass the overridden value of `devicePixelRatio` to
  200. /// 2D WebView.
  201. /// </summary>
  202. /// <example>
  203. /// <code>
  204. /// void Awake() {
  205. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  206. /// WebGLWebView.SetDevicePixelRatio(1);
  207. /// #endif
  208. /// }
  209. /// </code>
  210. /// </example>
  211. public static void SetDevicePixelRatio(float devicePixelRatio) {
  212. #if UNITY_2019_3_OR_NEWER
  213. WebView_setDevicePixelRatio(devicePixelRatio);
  214. #else
  215. WebViewLogger.LogWarning("The call to WebGLWebView.SetDevicePixelRatio() will be ignored because a version of Unity older than 2019.3 is being used, which always uses a devicePixelRatio of 1.");
  216. #endif
  217. }
  218. /// <summary>
  219. /// Sets whether the JavaScript Fullscreen API is enabled for webviews. The default is `false`.
  220. /// </summary>
  221. /// <example>
  222. /// <code>
  223. /// void Awake() {
  224. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  225. /// WebGLWebView.SetFullscreenEnabled(true);
  226. /// #endif
  227. /// }
  228. /// </code>
  229. /// </example>
  230. public static void SetFullscreenEnabled(bool enabled) => WebView_setFullscreenEnabled(enabled);
  231. /// <summary>
  232. /// Sets whether the JavaScript Geolocation API is enabled for webviews. The default is `false`.
  233. /// </summary>
  234. /// <example>
  235. /// <code>
  236. /// void Awake() {
  237. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  238. /// WebGLWebView.SetGeolocationEnabled(true);
  239. /// #endif
  240. /// }
  241. /// </code>
  242. /// </example>
  243. public static void SetGeolocationEnabled(bool enabled) => WebView_setGeolocationEnabled(enabled);
  244. public void SetNativeZoomEnabled(bool enabled) {
  245. WebViewLogger.LogWarning("2D WebView for WebGL doesn't support native zooming, so the call to IWithNative2DMode.SetNativeZoomEnabled() will be ignored.");
  246. }
  247. /// <see cref="IWithNative2DMode"/>
  248. public void SetRect(Rect rect) {
  249. _assertValidState();
  250. _assertNative2DModeEnabled();
  251. _rect = rect;
  252. WebView_setRect(_nativeWebViewPtr, (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
  253. }
  254. /// <summary>
  255. /// Explicitly sets the HTML element that 2D WebView should use as the Unity app container.
  256. /// 2D WebView automatically detects the Unity app container element if its ID is set to one of the default values of
  257. /// "unityContainer" or "unity-container". However, if your app uses a custom WebGL template that
  258. /// uses a different ID for the container element, you must call this method to set the container element ID.
  259. /// 2D WebView for WebGL works by adding &lt;iframe&gt; elements to the app container, so it's unable to function correctly
  260. /// if it's unable to find the Unity app container element.
  261. /// </summary>
  262. /// <example>
  263. /// <code>
  264. /// void Awake() {
  265. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  266. /// WebGLWebView.SetUnityContainerElementId("your-custom-id");
  267. /// #endif
  268. /// }
  269. /// </code>
  270. /// </example>
  271. public static void SetUnityContainerElementId(string containerId) => WebView_setUnityContainerElementId(containerId);
  272. /// <summary>
  273. /// Sets whether screen sharing is enabled for webviews. The default is `false`.
  274. /// </summary>
  275. /// <example>
  276. /// <code>
  277. /// void Awake() {
  278. /// #if UNITY_WEBGL &amp;&amp; !UNITY_EDITOR
  279. /// WebGLWebView.SetScreenSharingEnabled(true);
  280. /// #endif
  281. /// }
  282. /// </code>
  283. /// </example>
  284. public static void SetScreenSharingEnabled(bool enabled) => WebView_setScreenSharingEnabled(enabled);
  285. /// <see cref="IWithNative2DMode"/>
  286. public void SetVisible(bool visible) {
  287. _assertValidState();
  288. _assertNative2DModeEnabled();
  289. Visible = visible;
  290. WebView_setVisible(_nativeWebViewPtr, visible);
  291. }
  292. public override void ZoomIn() => WebGLWarnings.LogZoomInError();
  293. public override void ZoomOut() => WebGLWarnings.LogZoomOutError();
  294. #region Non-public members
  295. readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
  296. Task<bool> _canGoBackOrForward(string methodName) {
  297. _assertValidState();
  298. if (_verifyIFrameCanBeAccessed(methodName)) {
  299. WebGLWarnings.LogCanGoBackOrForwardWarning();
  300. return Task.FromResult(WebView_canGoBackOrForward(_nativeWebViewPtr));
  301. }
  302. return Task.FromResult(false);
  303. }
  304. bool _verifyIFrameCanBeAccessed(string methodName) {
  305. if (CanAccessIFrameContent()) {
  306. return true;
  307. }
  308. // Log an error instead of throwing an exception because
  309. // exceptions are disabled by default for WebGL.
  310. WebGLWarnings.LogIFrameContentAccessWarning(methodName);
  311. return false;
  312. }
  313. [DllImport(_dllName)]
  314. static extern void WebView_bringToFront(IntPtr webViewPtr);
  315. [DllImport(_dllName)]
  316. static extern bool WebView_canAccessIFrameContent(IntPtr webViewPtr);
  317. [DllImport(_dllName)]
  318. static extern bool WebView_canGoBackOrForward(IntPtr webViewPtr);
  319. [DllImport(_dllName)]
  320. static extern string WebView_executeJavaScriptSync(IntPtr webViewPtr, string javaScript);
  321. [DllImport(_dllName)]
  322. static extern string WebView_executeJavaScriptLocally(string javaScript);
  323. [DllImport(_dllName)]
  324. static extern IntPtr WebView_newInNative2DMode(string gameObjectName, int x, int y, int width, int height, bool ignoreDevicePixelRatio);
  325. [DllImport (_dllName)]
  326. static extern void WebView_postMessage(IntPtr webViewPtr, string message);
  327. [DllImport (_dllName)]
  328. static extern void WebView_setDevicePixelRatio(float devicePixelRatio);
  329. [DllImport (_dllName)]
  330. static extern void WebView_setFullscreenEnabled(bool enabled);
  331. [DllImport (_dllName)]
  332. static extern void WebView_setGeolocationEnabled(bool enabled);
  333. [DllImport (_dllName)]
  334. static extern void WebView_setRect(IntPtr webViewPtr, int x, int y, int width, int height);
  335. [DllImport (_dllName)]
  336. static extern void WebView_setScreenSharingEnabled(bool enabled);
  337. [DllImport (_dllName)]
  338. static extern void WebView_setUnityContainerElementId(string containerId);
  339. [DllImport (_dllName)]
  340. static extern void WebView_setVisible(IntPtr webViewPtr, bool visible);
  341. #endregion
  342. }
  343. }
  344. #else
  345. namespace Vuplex.WebView {
  346. [System.Obsolete("To use the WebGLWebView class, you must use the directive `#if UNITY_WEBGL && !UNITY_EDITOR` like described here: https://support.vuplex.com/articles/how-to-call-platform-specific-apis#webgl . Note: WebGLWebView isn't actually obsolete. This compiler error just reports it's obsolete because 3D WebView generated the error with System.ObsoleteAttribute.", true)]
  347. public class WebGLWebView {}
  348. }
  349. #endif