BaseWebView.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. /**
  2. * Copyright (c) 2021 Vuplex Inc. All rights reserved.
  3. *
  4. * Licensed under the Vuplex Commercial Software Library License, you may
  5. * not use this file except in compliance with the License. You may obtain
  6. * a copy of the License at
  7. *
  8. * https://vuplex.com/commercial-library-license
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. // Only define BaseWebView.cs on supported platforms to avoid IL2CPP linking
  17. // errors on unsupported platforms.
  18. #if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_ANDROID || (UNITY_IOS && !VUPLEX_OMIT_IOS) || UNITY_WSA ||UNITY_WEBGL
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Globalization;
  22. using System.IO;
  23. using System.Linq;
  24. using System.Runtime.InteropServices;
  25. using System.Text.RegularExpressions;
  26. using UnityEngine;
  27. #if NET_4_6 || NET_STANDARD_2_0
  28. using System.Threading.Tasks;
  29. #endif
  30. namespace Vuplex.WebView {
  31. /// <summary>
  32. /// The base `IWebView` implementation, which is extended for each platform.
  33. /// </summary>
  34. public abstract class BaseWebView : MonoBehaviour {
  35. public event EventHandler CloseRequested;
  36. public event EventHandler<ConsoleMessageEventArgs> ConsoleMessageLogged {
  37. add {
  38. _consoleMessageLogged += value;
  39. if (_consoleMessageLogged.GetInvocationList().Length == 1) {
  40. _setConsoleMessageEventsEnabled(true);
  41. }
  42. }
  43. remove {
  44. _consoleMessageLogged -= value;
  45. if (_consoleMessageLogged.GetInvocationList().Length == 0) {
  46. _setConsoleMessageEventsEnabled(false);
  47. }
  48. }
  49. }
  50. public event EventHandler<FocusedInputFieldChangedEventArgs> FocusedInputFieldChanged {
  51. add {
  52. _focusedInputFieldChanged += value;
  53. if (_focusedInputFieldChanged.GetInvocationList().Length == 1) {
  54. _setFocusedInputFieldEventsEnabled(true);
  55. }
  56. }
  57. remove {
  58. _focusedInputFieldChanged -= value;
  59. if (_focusedInputFieldChanged.GetInvocationList().Length == 0) {
  60. _setFocusedInputFieldEventsEnabled(false);
  61. }
  62. }
  63. }
  64. public event EventHandler<ProgressChangedEventArgs> LoadProgressChanged;
  65. public event EventHandler<EventArgs<string>> MessageEmitted;
  66. public event EventHandler PageLoadFailed;
  67. public event EventHandler<EventArgs<string>> TitleChanged;
  68. public event EventHandler<UrlChangedEventArgs> UrlChanged;
  69. public event EventHandler<EventArgs<Rect>> VideoRectChanged;
  70. public bool IsDisposed { get; protected set; }
  71. public bool IsInitialized { get; private set; }
  72. public List<string> PageLoadScripts {
  73. get {
  74. return _pageLoadScripts;
  75. }
  76. }
  77. public float Resolution {
  78. get {
  79. return _numberOfPixelsPerUnityUnit;
  80. }
  81. }
  82. public Vector2 Size {
  83. get {
  84. return new Vector2(_width, _height);
  85. }
  86. }
  87. public virtual Vector2 SizeInPixels {
  88. get {
  89. return new Vector2(_nativeWidth, _nativeHeight);
  90. }
  91. }
  92. public Texture2D Texture {
  93. get {
  94. return _viewportTexture;
  95. }
  96. }
  97. public string Url { get; private set; }
  98. public Texture2D VideoTexture {
  99. get {
  100. return _videoTexture;
  101. }
  102. }
  103. public void Init(Texture2D texture, float width, float height) {
  104. Init(texture, width, height, null);
  105. }
  106. public virtual void Init(Texture2D viewportTexture, float width, float height, Texture2D videoTexture) {
  107. if (IsInitialized) {
  108. throw new InvalidOperationException("Init() cannot be called on a webview that has already been initialized.");
  109. }
  110. _viewportTexture = viewportTexture;
  111. _videoTexture = videoTexture;
  112. // Assign the game object a unique name so that the native view can send it messages.
  113. gameObject.name = String.Format("WebView-{0}", Guid.NewGuid().ToString());
  114. _width = width;
  115. _height = height;
  116. Utils.ThrowExceptionIfAbnormallyLarge(_nativeWidth, _nativeHeight);
  117. IsInitialized = true;
  118. // Prevent the script from automatically being destroyed when a new scene is loaded.
  119. DontDestroyOnLoad(gameObject);
  120. }
  121. public virtual void Blur() {
  122. _assertValidState();
  123. WebView_blur(_nativeWebViewPtr);
  124. }
  125. #if NET_4_6 || NET_STANDARD_2_0
  126. public Task<bool> CanGoBack() {
  127. var task = new TaskCompletionSource<bool>();
  128. CanGoBack(task.SetResult);
  129. return task.Task;
  130. }
  131. public Task<bool> CanGoForward() {
  132. var task = new TaskCompletionSource<bool>();
  133. CanGoForward(task.SetResult);
  134. return task.Task;
  135. }
  136. #endif
  137. public virtual void CanGoBack(Action<bool> callback) {
  138. _assertValidState();
  139. _pendingCanGoBackCallbacks.Add(callback);
  140. WebView_canGoBack(_nativeWebViewPtr);
  141. }
  142. public virtual void CanGoForward(Action<bool> callback) {
  143. _assertValidState();
  144. _pendingCanGoForwardCallbacks.Add(callback);
  145. WebView_canGoForward(_nativeWebViewPtr);
  146. }
  147. #if NET_4_6 || NET_STANDARD_2_0
  148. public Task<byte[]> CaptureScreenshot() {
  149. var task = new TaskCompletionSource<byte[]>();
  150. CaptureScreenshot(task.SetResult);
  151. return task.Task;
  152. }
  153. #endif
  154. public virtual void CaptureScreenshot(Action<byte[]> callback) {
  155. var bytes = new byte[0];
  156. try {
  157. var texture = _getReadableTexture();
  158. bytes = ImageConversion.EncodeToPNG(texture);
  159. Destroy(texture);
  160. } catch (Exception e) {
  161. WebViewLogger.LogError("An exception occurred while capturing the screenshot: " + e);
  162. }
  163. callback(bytes);
  164. }
  165. public virtual void Click(Vector2 point) {
  166. _assertValidState();
  167. int nativeX = (int) (point.x * _nativeWidth);
  168. int nativeY = (int) (point.y * _nativeHeight);
  169. WebView_click(_nativeWebViewPtr, nativeX, nativeY);
  170. }
  171. public virtual void Click(Vector2 point, bool preventStealingFocus) {
  172. // On most platforms, the regular `Click()` method
  173. // doesn't steal focus.
  174. Click(point);
  175. }
  176. public static void ClearAllData() {
  177. WebView_clearAllData();
  178. }
  179. public static void CreateTexture(float width, float height, Action<Texture2D> callback) {
  180. int nativeWidth = (int)(width * Config.NumberOfPixelsPerUnityUnit);
  181. int nativeHeight = (int)(height * Config.NumberOfPixelsPerUnityUnit);
  182. Utils.ThrowExceptionIfAbnormallyLarge(nativeWidth, nativeHeight);
  183. var texture = new Texture2D(
  184. nativeWidth,
  185. nativeHeight,
  186. TextureFormat.RGBA32,
  187. false,
  188. false
  189. );
  190. #if UNITY_2020_2_OR_NEWER
  191. // In Unity 2020.2, Unity's internal TexturesD3D11.cpp class on Windows logs an error if
  192. // UpdateExternalTexture() is called on a Texture2D created from the constructor
  193. // rather than from Texture2D.CreateExternalTexture(). So, rather than returning
  194. // the original Texture2D created via the constructor, we return a copy created
  195. // via CreateExternalTexture(). This approach is only used for 2020.2 and newer because
  196. // it doesn't work in 2018.4 and instead causes a crash.
  197. texture = Texture2D.CreateExternalTexture(
  198. nativeWidth,
  199. nativeHeight,
  200. TextureFormat.RGBA32,
  201. false,
  202. false,
  203. texture.GetNativeTexturePtr()
  204. );
  205. #endif
  206. // Invoke the callback asynchronously in order to match the async
  207. // behavior that's required for Android.
  208. Dispatcher.RunOnMainThread(() => callback(texture));
  209. }
  210. public virtual void Copy() {
  211. _assertValidState();
  212. _getSelectedText(text => GUIUtility.systemCopyBuffer = text);
  213. }
  214. public virtual void Cut() {
  215. _assertValidState();
  216. _getSelectedText(text => {
  217. GUIUtility.systemCopyBuffer = text;
  218. HandleKeyboardInput("Backspace");
  219. });
  220. }
  221. public virtual void DisableViewUpdates() {
  222. _assertValidState();
  223. WebView_disableViewUpdates(_nativeWebViewPtr);
  224. _viewUpdatesAreEnabled = false;
  225. }
  226. public virtual void Dispose() {
  227. _assertValidState();
  228. IsDisposed = true;
  229. WebView_destroy(_nativeWebViewPtr);
  230. _nativeWebViewPtr = IntPtr.Zero;
  231. // To avoid a MissingReferenceException, verify that this script
  232. // hasn't already been destroyed prior to accessing gameObject.
  233. if (this != null) {
  234. Destroy(gameObject);
  235. }
  236. }
  237. public virtual void EnableViewUpdates() {
  238. _assertValidState();
  239. if (_currentViewportNativeTexture != IntPtr.Zero) {
  240. _viewportTexture.UpdateExternalTexture(_currentViewportNativeTexture);
  241. }
  242. WebView_enableViewUpdates(_nativeWebViewPtr);
  243. _viewUpdatesAreEnabled = true;
  244. }
  245. #if NET_4_6 || NET_STANDARD_2_0
  246. public Task<string> ExecuteJavaScript(string javaScript) {
  247. var task = new TaskCompletionSource<string>();
  248. ExecuteJavaScript(javaScript, task.SetResult);
  249. return task.Task;
  250. }
  251. #else
  252. public void ExecuteJavaScript(string javaScript) {
  253. ExecuteJavaScript(javaScript, null);
  254. }
  255. #endif
  256. public virtual void ExecuteJavaScript(string javaScript, Action<string> callback) {
  257. _assertValidState();
  258. string resultCallbackId = null;
  259. if (callback != null) {
  260. resultCallbackId = Guid.NewGuid().ToString();
  261. _pendingJavaScriptResultCallbacks[resultCallbackId] = callback;
  262. }
  263. WebView_executeJavaScript(_nativeWebViewPtr, javaScript, resultCallbackId);
  264. }
  265. public virtual void Focus() {
  266. _assertValidState();
  267. WebView_focus(_nativeWebViewPtr);
  268. }
  269. #if NET_4_6 || NET_STANDARD_2_0
  270. public Task<byte[]> GetRawTextureData() {
  271. var task = new TaskCompletionSource<byte[]>();
  272. GetRawTextureData(task.SetResult);
  273. return task.Task;
  274. }
  275. #endif
  276. public virtual void GetRawTextureData(Action<byte[]> callback) {
  277. var bytes = new byte[0];
  278. try {
  279. var texture = _getReadableTexture();
  280. bytes = texture.GetRawTextureData();
  281. Destroy(texture);
  282. } catch (Exception e) {
  283. WebViewLogger.LogError("An exception occurred while getting the raw texture data: " + e);
  284. }
  285. callback(bytes);
  286. }
  287. public static void GloballySetUserAgent(bool mobile) {
  288. var success = WebView_globallySetUserAgentToMobile(mobile);
  289. if (!success) {
  290. throw new InvalidOperationException(USER_AGENT_EXCEPTION_MESSAGE);
  291. }
  292. }
  293. public static void GloballySetUserAgent(string userAgent) {
  294. var success = WebView_globallySetUserAgent(userAgent);
  295. if (!success) {
  296. throw new InvalidOperationException(USER_AGENT_EXCEPTION_MESSAGE);
  297. }
  298. }
  299. public virtual void GoBack() {
  300. _assertValidState();
  301. WebView_goBack(_nativeWebViewPtr);
  302. }
  303. public virtual void GoForward() {
  304. _assertValidState();
  305. WebView_goForward(_nativeWebViewPtr);
  306. }
  307. public virtual void HandleKeyboardInput(string input) {
  308. _assertValidState();
  309. WebView_handleKeyboardInput(_nativeWebViewPtr, input);
  310. }
  311. public virtual void LoadHtml(string html) {
  312. _assertValidState();
  313. WebView_loadHtml(_nativeWebViewPtr, html);
  314. }
  315. public virtual void LoadUrl(string url) {
  316. _assertValidState();
  317. WebView_loadUrl(_nativeWebViewPtr, _transformStreamingAssetsUrlIfNeeded(url));
  318. }
  319. public virtual void LoadUrl(string url, Dictionary<string, string> additionalHttpHeaders) {
  320. _assertValidState();
  321. if (additionalHttpHeaders == null) {
  322. LoadUrl(url);
  323. } else {
  324. var headerStrings = additionalHttpHeaders.Keys.Select(key => String.Format("{0}: {1}", key, additionalHttpHeaders[key])).ToArray();
  325. var newlineDelimitedHttpHeaders = String.Join("\n", headerStrings);
  326. WebView_loadUrlWithHeaders(_nativeWebViewPtr, url, newlineDelimitedHttpHeaders);
  327. }
  328. }
  329. public virtual void Paste() {
  330. _assertValidState();
  331. var text = GUIUtility.systemCopyBuffer;
  332. foreach (var character in text) {
  333. HandleKeyboardInput(char.ToString(character));
  334. }
  335. }
  336. public void PostMessage(string data) {
  337. var escapedString = data.Replace("'", "\\'").Replace("\n", "\\n");
  338. ExecuteJavaScript(String.Format("vuplex._emit('message', {{ data: \'{0}\' }})", escapedString));
  339. }
  340. public virtual void Reload() {
  341. _assertValidState();
  342. WebView_reload(_nativeWebViewPtr);
  343. }
  344. public virtual void Resize(float width, float height) {
  345. if (IsDisposed || (width == _width && height == _height)) {
  346. return;
  347. }
  348. _width = width;
  349. _height = height;
  350. _resize();
  351. }
  352. public virtual void Scroll(Vector2 delta) {
  353. _assertValidState();
  354. var deltaX = (int)(delta.x * _numberOfPixelsPerUnityUnit);
  355. var deltaY = (int)(delta.y * _numberOfPixelsPerUnityUnit);
  356. WebView_scroll(_nativeWebViewPtr, deltaX, deltaY);
  357. }
  358. public virtual void Scroll(Vector2 scrollDelta, Vector2 point) {
  359. _assertValidState();
  360. var deltaX = (int)(scrollDelta.x * _numberOfPixelsPerUnityUnit);
  361. var deltaY = (int)(scrollDelta.y * _numberOfPixelsPerUnityUnit);
  362. var pointerX = (int)(point.x * _nativeWidth);
  363. var pointerY = (int)(point.y * _nativeHeight);
  364. WebView_scrollAtPoint(_nativeWebViewPtr, deltaX, deltaY, pointerX, pointerY);
  365. }
  366. public virtual void SelectAll() {
  367. _assertValidState();
  368. // If the focused element is an input with a select() method, then use that.
  369. // Otherwise, travel up the DOM until we get to the body or a contenteditable
  370. // element, and then select its contents.
  371. ExecuteJavaScript(
  372. @"(function() {
  373. var element = document.activeElement || document.body;
  374. while (!(element === document.body || element.getAttribute('contenteditable') === 'true')) {
  375. if (typeof element.select === 'function') {
  376. element.select();
  377. return;
  378. }
  379. element = element.parentElement;
  380. }
  381. var range = document.createRange();
  382. range.selectNodeContents(element);
  383. var selection = window.getSelection();
  384. selection.removeAllRanges();
  385. selection.addRange(range);
  386. })();",
  387. null
  388. );
  389. }
  390. public void SetResolution(float pixelsPerUnityUnit) {
  391. // Note: this method can be called prior to initialization.
  392. _numberOfPixelsPerUnityUnit = pixelsPerUnityUnit;
  393. _resize();
  394. }
  395. public static void SetStorageEnabled(bool enabled) {
  396. WebView_setStorageEnabled(enabled);
  397. }
  398. public virtual void ZoomIn() {
  399. _assertValidState();
  400. WebView_zoomIn(_nativeWebViewPtr);
  401. }
  402. public virtual void ZoomOut() {
  403. _assertValidState();
  404. WebView_zoomOut(_nativeWebViewPtr);
  405. }
  406. EventHandler<ConsoleMessageEventArgs> _consoleMessageLogged;
  407. protected IntPtr _currentViewportNativeTexture;
  408. #if (UNITY_STANDALONE_WIN && !UNITY_EDITOR) || UNITY_EDITOR_WIN ||UNITY_WEBGL
  409. protected const string _dllName = "VuplexWebViewWindows";
  410. #elif (UNITY_STANDALONE_OSX && !UNITY_EDITOR) || UNITY_EDITOR_OSX
  411. protected const string _dllName = "VuplexWebViewMac";
  412. #elif UNITY_WSA
  413. protected const string _dllName = "VuplexWebViewUwp";
  414. #elif UNITY_ANDROID
  415. protected const string _dllName = "VuplexWebViewAndroid";
  416. #else
  417. protected const string _dllName = "__Internal";
  418. #endif
  419. EventHandler<FocusedInputFieldChangedEventArgs> _focusedInputFieldChanged;
  420. FocusedInputFieldType _focusedInputFieldType = FocusedInputFieldType.None;
  421. protected float _height; // in Unity units
  422. Material _materialForBlitting;
  423. // Height in pixels.
  424. protected int _nativeHeight {
  425. get {
  426. // Height must be non-zero
  427. return Math.Max(1, (int)(_height * _numberOfPixelsPerUnityUnit));
  428. }
  429. }
  430. protected IntPtr _nativeWebViewPtr = IntPtr.Zero;
  431. // Width in pixels.
  432. protected int _nativeWidth {
  433. get {
  434. // Width must be non-zero
  435. return Math.Max(1, (int)(_width * _numberOfPixelsPerUnityUnit));
  436. }
  437. }
  438. protected float _numberOfPixelsPerUnityUnit = Config.NumberOfPixelsPerUnityUnit;
  439. List<string> _pageLoadScripts = new List<string>();
  440. List<Action<bool>> _pendingCanGoBackCallbacks = new List<Action<bool>>();
  441. List<Action<bool>> _pendingCanGoForwardCallbacks = new List<Action<bool>>();
  442. protected Dictionary<string, Action<string>> _pendingJavaScriptResultCallbacks = new Dictionary<string, Action<string>>();
  443. static readonly Regex STREAMING_ASSETS_URL_REGEX = new Regex(@"^streaming-assets:(//)?(.*)$", RegexOptions.IgnoreCase);
  444. const string USER_AGENT_EXCEPTION_MESSAGE = "Unable to set the User-Agent string, because a webview has already been created with the default User-Agent. On Windows and macOS, SetUserAgent() can only be called prior to creating any webviews.";
  445. Rect _videoRect = Rect.zero;
  446. protected Texture2D _videoTexture;
  447. protected bool _viewUpdatesAreEnabled = true;
  448. protected Texture2D _viewportTexture;
  449. protected float _width; // in Unity units
  450. protected void _assertValidState(){
  451. if (!IsInitialized) {
  452. throw new InvalidOperationException("Methods cannot be called on an uninitialized webview. Please initialize it first with IWebView.Init().");
  453. }
  454. if (IsDisposed) {
  455. throw new InvalidOperationException("Methods cannot be called on a disposed webview.");
  456. }
  457. }
  458. protected virtual Material _createMaterialForBlitting() {
  459. return Utils.CreateDefaultMaterial();
  460. }
  461. Texture2D _getReadableTexture() {
  462. // https://support.unity3d.com/hc/en-us/articles/206486626-How-can-I-get-pixels-from-unreadable-textures-
  463. // Create a temporary RenderTexture of the same size as the texture
  464. RenderTexture tempRenderTexture = RenderTexture.GetTemporary(
  465. _nativeWidth,
  466. _nativeHeight,
  467. 0,
  468. RenderTextureFormat.Default,
  469. RenderTextureReadWrite.Linear
  470. );
  471. if (_materialForBlitting == null) {
  472. _materialForBlitting = _createMaterialForBlitting();
  473. }
  474. // Use the version of Graphics.Blit() that accepts a material
  475. // so that any transformations needed are performed with the shader.
  476. Graphics.Blit(Texture,tempRenderTexture,_materialForBlitting);
  477. // Backup the currently set RenderTexture
  478. RenderTexture previousRenderTexture = RenderTexture.active;
  479. // Set the current RenderTexture to the temporary one we created
  480. RenderTexture.active = tempRenderTexture;
  481. // Create a new readable Texture2D to copy the pixels to it
  482. Texture2D readableTexture = new Texture2D(_nativeWidth, _nativeHeight);
  483. // Copy the pixels from the RenderTexture to the new Texture
  484. readableTexture.ReadPixels(new Rect(0, 0, tempRenderTexture.width, tempRenderTexture.height), 0, 0);
  485. readableTexture.Apply();
  486. // Reset the active RenderTexture
  487. RenderTexture.active = previousRenderTexture;
  488. // Release the temporary RenderTexture
  489. RenderTexture.ReleaseTemporary(tempRenderTexture);
  490. return readableTexture;
  491. }
  492. void _getSelectedText(Action<string> callback) {
  493. // window.getSelection() doesn't work on the content of <textarea> and <input> elements in
  494. // Gecko and legacy Edge.
  495. // https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection#Related_objects
  496. ExecuteJavaScript(
  497. @"var element = document.activeElement;
  498. if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
  499. element.value.substring(element.selectionStart, element.selectionEnd);
  500. } else {
  501. window.getSelection().toString();
  502. }",
  503. callback
  504. );
  505. }
  506. /// <summary>
  507. /// The native plugin invokes this method.
  508. /// </summary>
  509. void HandleCanGoBackResult(string message) {
  510. var result = Boolean.Parse(message);
  511. var callbacks = new List<Action<bool>>(_pendingCanGoBackCallbacks);
  512. _pendingCanGoBackCallbacks.Clear();
  513. foreach (var callback in callbacks) {
  514. try {
  515. callback(result);
  516. } catch (Exception e) {
  517. WebViewLogger.LogError("An exception occurred while calling the callback for CanGoBack: " + e);
  518. }
  519. }
  520. }
  521. /// <summary>
  522. /// The native plugin invokes this method.
  523. /// </summary>
  524. void HandleCanGoForwardResult(string message) {
  525. var result = Boolean.Parse(message);
  526. var callBacks = new List<Action<bool>>(_pendingCanGoForwardCallbacks);
  527. _pendingCanGoForwardCallbacks.Clear();
  528. foreach (var callBack in callBacks) {
  529. try {
  530. callBack(result);
  531. } catch (Exception e) {
  532. WebViewLogger.LogError("An exception occurred while calling the callForward for CanGoForward: " + e);
  533. }
  534. }
  535. }
  536. /// <summary>
  537. /// The native plugin invokes this method.
  538. /// </summary>
  539. void HandleCloseRequested(string message) {
  540. var handler = CloseRequested;
  541. if (handler != null) {
  542. CloseRequested(this, EventArgs.Empty);
  543. }
  544. }
  545. /// <summary>
  546. /// The native plugin invokes this method.
  547. /// </summary>
  548. void HandleJavaScriptResult(string message) {
  549. var components = message.Split(new char[] { ',' }, 2);
  550. var resultCallbackId = components[0];
  551. var result = components[1];
  552. _handleJavaScriptResult(resultCallbackId, result);
  553. }
  554. void _handleJavaScriptResult(string resultCallbackId, string result) {
  555. var callback = _pendingJavaScriptResultCallbacks[resultCallbackId];
  556. _pendingJavaScriptResultCallbacks.Remove(resultCallbackId);
  557. callback(result);
  558. }
  559. /// <summary>
  560. /// The native plugin invokes this method.
  561. /// </summary>
  562. void HandleLoadFailed(string unusedParam) {
  563. if (PageLoadFailed != null) {
  564. PageLoadFailed(this, EventArgs.Empty);
  565. }
  566. var e = new ProgressChangedEventArgs(ProgressChangeType.Failed, 1.0f);
  567. OnLoadProgressChanged(e);
  568. }
  569. /// <summary>
  570. /// The native plugin invokes this method.
  571. /// </summary>
  572. void HandleLoadFinished(string unusedParam) {
  573. var e = new ProgressChangedEventArgs(ProgressChangeType.Finished, 1.0f);
  574. OnLoadProgressChanged(e);
  575. foreach (var script in _pageLoadScripts) {
  576. ExecuteJavaScript(script, null);
  577. }
  578. }
  579. /// <summary>
  580. /// The native plugin invokes this method.
  581. /// </summary>
  582. void HandleLoadStarted(string unusedParam) {
  583. var e = new ProgressChangedEventArgs(ProgressChangeType.Started, 0.0f);
  584. OnLoadProgressChanged(e);
  585. }
  586. /// <summary>
  587. /// The native plugin invokes this method.
  588. /// </summary>
  589. void HandleLoadProgressUpdate(string progressString) {
  590. var progress = float.Parse(progressString, CultureInfo.InvariantCulture);
  591. var e = new ProgressChangedEventArgs(ProgressChangeType.Updated, progress);
  592. OnLoadProgressChanged(e);
  593. }
  594. /// <summary>
  595. /// The native plugin invokes this method.
  596. /// </summary>
  597. void HandleMessageEmitted(string serializedMessage) {
  598. // For performance, only try to deserialize the message if it's one we're listening for.
  599. var messageType = serializedMessage.Contains("vuplex.webview") ? BridgeMessage.ParseType(serializedMessage) : null;
  600. switch (messageType) {
  601. case "vuplex.webview.consoleMessageLogged": {
  602. var handler = _consoleMessageLogged;
  603. if (handler != null) {
  604. var consoleMessage = JsonUtility.FromJson<ConsoleBridgeMessage>(serializedMessage);
  605. handler(this, consoleMessage.ToEventArgs());
  606. }
  607. break;
  608. }
  609. case "vuplex.webview.focusedInputFieldChanged": {
  610. var typeString = StringBridgeMessage.ParseValue(serializedMessage);
  611. var type = FocusedInputFieldChangedEventArgs.ParseType(typeString);
  612. if (_focusedInputFieldType != type) {
  613. _focusedInputFieldType = type;
  614. var handler = _focusedInputFieldChanged;
  615. if (handler != null) {
  616. handler(this, new FocusedInputFieldChangedEventArgs(type));
  617. }
  618. }
  619. break;
  620. }
  621. case "vuplex.webview.javaScriptResult": {
  622. var message = JsonUtility.FromJson<StringWithIdBridgeMessage>(serializedMessage);
  623. _handleJavaScriptResult(message.id, message.value);
  624. break;
  625. }
  626. case "vuplex.webview.titleChanged": {
  627. var handler = TitleChanged;
  628. if (handler != null) {
  629. var title = StringBridgeMessage.ParseValue(serializedMessage);
  630. handler(this, new EventArgs<string>(title));
  631. }
  632. break;
  633. }
  634. case "vuplex.webview.urlChanged": {
  635. var action = JsonUtility.FromJson<UrlChangedMessage>(serializedMessage).urlAction;
  636. if (Url == action.Url) {
  637. return;
  638. }
  639. Url = action.Url;
  640. var handler = UrlChanged;
  641. if (handler != null) {
  642. handler(this, new UrlChangedEventArgs(action.Url, action.Title, action.Type));
  643. }
  644. break;
  645. }
  646. case "vuplex.webview.videoRectChanged": {
  647. var value = JsonUtility.FromJson<VideoRectChangedMessage>(serializedMessage).value;
  648. var newRect = value.rect.toRect();
  649. if (_videoRect != newRect) {
  650. _videoRect = newRect;
  651. var handler = VideoRectChanged;
  652. if (handler != null) {
  653. handler(this, new EventArgs<Rect>(newRect));
  654. }
  655. }
  656. break;
  657. }
  658. default: {
  659. var handler = MessageEmitted;
  660. if (handler != null) {
  661. handler(this, new EventArgs<string>(serializedMessage));
  662. }
  663. break;
  664. }
  665. }
  666. }
  667. /// <summary>
  668. /// The native plugin invokes this method.
  669. /// </summary>
  670. void HandleTextureChanged(string textureString) {
  671. var nativeTexture = new IntPtr(Int64.Parse(textureString));
  672. if (nativeTexture == _currentViewportNativeTexture) {
  673. return;
  674. }
  675. var previousNativeTexture = _currentViewportNativeTexture;
  676. _currentViewportNativeTexture = nativeTexture;
  677. if (_viewUpdatesAreEnabled) {
  678. _viewportTexture.UpdateExternalTexture(_currentViewportNativeTexture);
  679. }
  680. if (previousNativeTexture != IntPtr.Zero && previousNativeTexture != _currentViewportNativeTexture) {
  681. WebView_destroyTexture(previousNativeTexture, SystemInfo.graphicsDeviceType.ToString());
  682. }
  683. }
  684. protected virtual void OnLoadProgressChanged(ProgressChangedEventArgs eventArgs) {
  685. var handler = LoadProgressChanged;
  686. if (handler != null) {
  687. handler(this, eventArgs);
  688. }
  689. }
  690. protected ConsoleMessageLevel _parseConsoleMessageLevel(string levelString) {
  691. switch (levelString) {
  692. case "DEBUG":
  693. return ConsoleMessageLevel.Debug;
  694. case "ERROR":
  695. return ConsoleMessageLevel.Error;
  696. case "LOG":
  697. return ConsoleMessageLevel.Log;
  698. case "WARNING":
  699. return ConsoleMessageLevel.Warning;
  700. default:
  701. WebViewLogger.LogWarning("Unrecognized console message level: " + levelString);
  702. return ConsoleMessageLevel.Log;
  703. }
  704. }
  705. protected virtual void _resize() {
  706. // Only trigger a resize if the webview has been initialized
  707. if (_viewportTexture) {
  708. _assertValidState();
  709. Utils.ThrowExceptionIfAbnormallyLarge(_nativeWidth, _nativeHeight);
  710. WebView_resize(_nativeWebViewPtr, _nativeWidth, _nativeHeight);
  711. }
  712. }
  713. protected virtual void _setConsoleMessageEventsEnabled(bool enabled) {
  714. _assertValidState();
  715. WebView_setConsoleMessageEventsEnabled(_nativeWebViewPtr, enabled);
  716. }
  717. protected virtual void _setFocusedInputFieldEventsEnabled(bool enabled) {
  718. _assertValidState();
  719. WebView_setFocusedInputFieldEventsEnabled(_nativeWebViewPtr, enabled);
  720. }
  721. protected string _transformStreamingAssetsUrlIfNeeded(string originalUrl) {
  722. if (originalUrl == null) {
  723. throw new ArgumentException("URL cannot be null.");
  724. }
  725. var match = STREAMING_ASSETS_URL_REGEX.Match(originalUrl);
  726. if (!match.Success) {
  727. return originalUrl;
  728. }
  729. var urlPath = match.Groups[2].Captures[0].Value;
  730. return "file://" + Path.Combine(Application.streamingAssetsPath, urlPath);
  731. }
  732. static void _unitySendMessage(string gameObjectName, string methodName, string message) {
  733. Dispatcher.RunOnMainThread(() => {
  734. var gameObj = GameObject.Find(gameObjectName);
  735. if (gameObj == null) {
  736. WebViewLogger.LogErrorFormat("Unable to send the message, because there is no GameObject named '{0}'", gameObjectName);
  737. return;
  738. }
  739. gameObj.SendMessage(methodName, message);
  740. });
  741. }
  742. [DllImport(_dllName)]
  743. static extern void WebView_blur(IntPtr webViewPtr);
  744. [DllImport(_dllName)]
  745. static extern void WebView_canGoBack(IntPtr webViewPtr);
  746. [DllImport(_dllName)]
  747. static extern void WebView_canGoForward(IntPtr webViewPtr);
  748. [DllImport(_dllName)]
  749. static extern void WebView_clearAllData();
  750. [DllImport(_dllName)]
  751. static extern void WebView_click(IntPtr webViewPtr, int x, int y);
  752. [DllImport(_dllName)]
  753. protected static extern void WebView_destroyTexture(IntPtr texture, string graphicsApi);
  754. [DllImport(_dllName)]
  755. static extern void WebView_destroy(IntPtr webViewPtr);
  756. [DllImport(_dllName)]
  757. static extern void WebView_disableViewUpdates(IntPtr webViewPtr);
  758. [DllImport(_dllName)]
  759. static extern void WebView_enableViewUpdates(IntPtr webViewPtr);
  760. [DllImport(_dllName)]
  761. static extern void WebView_executeJavaScript(IntPtr webViewPtr, string javaScript, string resultCallbackId);
  762. [DllImport(_dllName)]
  763. static extern void WebView_focus(IntPtr webViewPtr);
  764. [DllImport(_dllName)]
  765. static extern bool WebView_globallySetUserAgentToMobile(bool mobile);
  766. [DllImport(_dllName)]
  767. static extern bool WebView_globallySetUserAgent(string userAgent);
  768. [DllImport(_dllName)]
  769. static extern void WebView_goBack(IntPtr webViewPtr);
  770. [DllImport(_dllName)]
  771. static extern void WebView_goForward(IntPtr webViewPtr);
  772. [DllImport(_dllName)]
  773. static extern void WebView_handleKeyboardInput(IntPtr webViewPtr, string input);
  774. [DllImport(_dllName)]
  775. static extern void WebView_loadHtml(IntPtr webViewPtr, string html);
  776. [DllImport(_dllName)]
  777. static extern void WebView_loadUrl(IntPtr webViewPtr, string url);
  778. [DllImport(_dllName)]
  779. static extern void WebView_loadUrlWithHeaders(IntPtr webViewPtr, string url, string newlineDelimitedHttpHeaders);
  780. [DllImport(_dllName)]
  781. static extern void WebView_reload(IntPtr webViewPtr);
  782. [DllImport(_dllName)]
  783. protected static extern void WebView_resize(IntPtr webViewPtr, int width, int height);
  784. [DllImport(_dllName)]
  785. static extern void WebView_scroll(IntPtr webViewPtr, int deltaX, int deltaY);
  786. [DllImport(_dllName)]
  787. static extern void WebView_scrollAtPoint(IntPtr webViewPtr, int deltaX, int deltaY, int pointerX, int pointerY);
  788. [DllImport(_dllName)]
  789. static extern void WebView_setConsoleMessageEventsEnabled(IntPtr webViewPtr, bool enabled);
  790. [DllImport(_dllName)]
  791. static extern void WebView_setFocusedInputFieldEventsEnabled(IntPtr webViewPtr, bool enabled);
  792. [DllImport(_dllName)]
  793. static extern void WebView_setStorageEnabled(bool enabled);
  794. [DllImport(_dllName)]
  795. static extern void WebView_zoomIn(IntPtr webViewPtr);
  796. [DllImport(_dllName)]
  797. static extern void WebView_zoomOut(IntPtr webViewPtr);
  798. }
  799. }
  800. #endif