BaseWebViewPrefab.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  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. using System;
  17. using UnityEngine;
  18. using UnityEngine.EventSystems;
  19. #if NET_4_6 || NET_STANDARD_2_0
  20. using System.Threading.Tasks;
  21. #endif
  22. namespace Vuplex.WebView {
  23. public abstract class BaseWebViewPrefab : MonoBehaviour {
  24. /// <summary>
  25. /// Indicates that the prefab was clicked. Note that the prefab automatically
  26. /// calls the `IWebView.Click()` method for you.
  27. /// </summary>
  28. public event EventHandler<ClickedEventArgs> Clicked;
  29. /// <summary>
  30. /// Indicates that the prefab finished initializing,
  31. /// so its `WebView` property is ready to use.
  32. /// </summary>
  33. /// <seealso cref="WaitUntilInitialized"/>
  34. public event EventHandler Initialized;
  35. /// <summary>
  36. /// Indicates that the prefab was scrolled. Note that the prefab automatically
  37. /// calls the `IWebView.Scroll()` method for you.
  38. /// </summary>
  39. public event EventHandler<ScrolledEventArgs> Scrolled;
  40. /// <summary>
  41. /// If you drag the prefab into the scene via the editor,
  42. /// you can set this property to make it so that the instance
  43. /// automatically initializes itself with the given URL. To load a new URL
  44. /// after the prefab has been initialized, use `IWebView.LoadUrl()` instead.
  45. /// </summary>
  46. [Label("Initial URL to load (optional)")]
  47. [Tooltip("Or you can leave the Initial URL blank if you want to initialize the prefab programmatically by calling Init().")]
  48. [HideInInspector]
  49. public string InitialUrl;
  50. [Header("Other Settings")]
  51. /// <summary>
  52. /// Determines how the prefab handles drag interactions.
  53. /// </summary>
  54. [Tooltip("Note: \"Drag Within Page\" is not supported on iOS or UWP.")]
  55. public DragMode DragMode = DragMode.DragToScroll;
  56. /// <summary>
  57. /// Clicking is enabled by default, but can be disabled by
  58. /// setting this property to `false`.
  59. /// </summary>
  60. public bool ClickingEnabled = true;
  61. /// <summary>
  62. /// Hover interactions are enabled by default, but can be disabled by
  63. /// setting this property to `false`.
  64. /// Note that hovering only works for webview implementations that
  65. /// support the `IWithMovablePointer` interface (i.e. Android, Windows, and macOS).
  66. /// </summary>
  67. [Tooltip("Note: Hovering is not supported on iOS or UWP.")]
  68. public bool HoveringEnabled = true;
  69. /// <summary>
  70. /// Scrolling is enabled by default, but can be disabled by
  71. /// setting this property to `false`.
  72. /// </summary>
  73. public bool ScrollingEnabled = true;
  74. /// <summary>
  75. /// Determines the threshold (in web pixels) for triggering a drag. The default is 20.
  76. /// </summary>
  77. /// <remarks>
  78. /// When the `DragMode` is set to `DragToScroll`, this property determines
  79. /// the distance that the pointer must drag before it's no longer
  80. /// considered a click.
  81. /// </remarks>
  82. /// <remarks>
  83. /// When the `DragMode` is set to `DragWithinPage`, this property determines
  84. /// the distance that the pointer must drag before it triggers
  85. /// a drag within the page.
  86. /// </remarks>
  87. [Label("Drag Threshold (px)")]
  88. [Tooltip("Determines the threshold (in web pixels) for triggering a drag.")]
  89. public float DragThreshold = 20;
  90. [Obsolete("The WebViewPrefab.DragToScrollThreshold property is obsolete. Please use DragThreshold instead.")]
  91. public float DragToScrollThreshold { get; set; }
  92. [Header("Debugging")]
  93. /// <summary>
  94. /// Determines whether remote debugging is enabled with `Web.EnableRemoteDebugging()`.
  95. /// </summary>
  96. [Tooltip("Determines whether remote debugging is enabled with Web.EnableRemoteDebugging().")]
  97. public bool RemoteDebuggingEnabled = false;
  98. /// <summary>
  99. /// Determines whether JavaScript console messages from `IWebView.ConsoleMessageLogged`
  100. /// are printed to the Unity logs.
  101. /// <summary>
  102. [Tooltip("Determines whether JavaScript console messages are printed to the Unity logs.")]
  103. public bool LogConsoleMessages = false;
  104. /// <summary>
  105. /// The prefab's material.
  106. /// </summary>
  107. public Material Material {
  108. get {
  109. return _view.Material;
  110. }
  111. set {
  112. _view.Material = value;
  113. }
  114. }
  115. /// <summary>
  116. /// Controls whether the instance is visible or hidden.
  117. /// </summary>
  118. public virtual bool Visible {
  119. get {
  120. return _view.Visible;
  121. }
  122. set {
  123. _view.Visible = value;
  124. if (_videoLayer != null) {
  125. _videoLayer.Visible = value;
  126. }
  127. }
  128. }
  129. /// <summary>
  130. /// A reference to the prefab's `IWebView` instance, which
  131. /// is available after the `Initialized` event is raised.
  132. /// Before initialization is complete, this property is `null`.
  133. /// </summary>
  134. public IWebView WebView { get { return _webView; }}
  135. /// <summary>
  136. /// Destroys the instance and its children. Note that you don't have
  137. /// to call this method if you destroy the instance's parent with
  138. /// `Object.Destroy()`.
  139. /// </summary>
  140. public void Destroy() {
  141. UnityEngine.Object.Destroy(gameObject);
  142. }
  143. public void SetCutoutRect(Rect rect) {
  144. _view.SetCutoutRect(rect);
  145. }
  146. /// <summary>
  147. /// By default, the prefab detects pointer input events like clicks through
  148. /// Unity's event system, but you can use this method to override the way that
  149. /// input events are detected.
  150. /// </summary>
  151. public void SetPointerInputDetector(IPointerInputDetector pointerInputDetector) {
  152. var previousPointerInputDetector = _pointerInputDetector;
  153. _pointerInputDetector = pointerInputDetector;
  154. // If _webView hasn't been set yet, then _initPointerInputDetector
  155. // will get called before it's set to initialize _pointerInputDetector.
  156. if (_webView != null) {
  157. _initPointerInputDetector(_webView, previousPointerInputDetector);
  158. }
  159. }
  160. #if NET_4_6 || NET_STANDARD_2_0
  161. /// <summary>
  162. /// Returns a task that resolves when the prefab is initialized
  163. /// (i.e. when its `WebView` property is ready for use).
  164. /// </summary>
  165. /// <seealso cref="Initialized"/>
  166. public Task WaitUntilInitialized() {
  167. var taskCompletionSource = new TaskCompletionSource<bool>();
  168. var isInitialized = _webView != null;
  169. if (isInitialized) {
  170. taskCompletionSource.SetResult(true);
  171. } else {
  172. Initialized += (sender, e) => taskCompletionSource.SetResult(true);
  173. }
  174. return taskCompletionSource.Task;
  175. }
  176. #endif
  177. [SerializeField]
  178. [HideInInspector]
  179. ViewportMaterialView _cachedVideoLayer;
  180. [SerializeField]
  181. [HideInInspector]
  182. ViewportMaterialView _cachedView;
  183. IWebView _cachedWebView;
  184. // Used for DragMode.DragToScroll and DragMode.Disabled
  185. bool _clickIsPending;
  186. bool _consoleMessageLoggedHandlerAttached;
  187. bool _loggedDragWarning;
  188. WebViewOptions _options;
  189. [SerializeField]
  190. [HideInInspector]
  191. GameObject _pointerInputDetectorGameObject;
  192. IPointerInputDetector _pointerInputDetector {
  193. get {
  194. return _pointerInputDetectorGameObject == null ? null :
  195. _pointerInputDetectorGameObject.GetComponent<IPointerInputDetector>();
  196. }
  197. set {
  198. var monoBehaviour = value as MonoBehaviour;
  199. if (monoBehaviour == null) {
  200. throw new ArgumentException("The provided IPointerInputDetector can't be successfully set because it's not a MonoBehaviour");
  201. }
  202. _pointerInputDetectorGameObject = monoBehaviour.gameObject;
  203. }
  204. }
  205. bool _pointerIsDown;
  206. Vector2 _pointerDownRatioPoint;
  207. Vector2 _previousDragPoint;
  208. Vector2 _previousMovePointerPoint;
  209. static bool _remoteDebuggingEnabled;
  210. protected ViewportMaterialView _videoLayer {
  211. get {
  212. if (_cachedVideoLayer == null) {
  213. _cachedVideoLayer = _getVideoLayer();
  214. }
  215. return _cachedVideoLayer;
  216. }
  217. }
  218. bool _videoLayerDisabled;
  219. Material _videoMaterial;
  220. protected ViewportMaterialView _view {
  221. get {
  222. if (_cachedView == null) {
  223. _cachedView = _getView();
  224. }
  225. return _cachedView;
  226. }
  227. }
  228. Material _viewMaterial;
  229. [SerializeField]
  230. [HideInInspector]
  231. GameObject _webViewGameObject;
  232. protected IWebView _webView {
  233. get {
  234. if (_cachedWebView == null) {
  235. if (_webViewGameObject == null) {
  236. return null;
  237. }
  238. _cachedWebView = _webViewGameObject.GetComponent<IWebView>();
  239. }
  240. return _cachedWebView;
  241. }
  242. set {
  243. var monoBehaviour = value as MonoBehaviour;
  244. if (monoBehaviour == null) {
  245. throw new ArgumentException("The IWebView cannot be set successfully because it's not a MonoBehaviour.");
  246. }
  247. _webViewGameObject = monoBehaviour.gameObject;
  248. _cachedWebView = value;
  249. }
  250. }
  251. void _attachWebViewEventHandlers(IWebView webView) {
  252. if (!_options.disableVideo) {
  253. webView.VideoRectChanged += (sender, e) => _setVideoRect(e.Value);
  254. }
  255. if (LogConsoleMessages) {
  256. _consoleMessageLoggedHandlerAttached = true;
  257. webView.ConsoleMessageLogged += WebView_ConsoleMessageLogged;
  258. }
  259. }
  260. protected abstract Vector2 _convertRatioPointToUnityUnits(Vector2 point);
  261. protected abstract float _getInitialResolution();
  262. protected abstract float _getScrollingSensitivity();
  263. protected abstract ViewportMaterialView _getVideoLayer();
  264. protected abstract ViewportMaterialView _getView();
  265. protected void _init(float width, float height, WebViewOptions options = new WebViewOptions(), IWebView initializedWebView = null) {
  266. _throwExceptionIfInitialized();
  267. // Remote debugging can only be enabled once, before any webviews are initialized.
  268. if (RemoteDebuggingEnabled && !_remoteDebuggingEnabled) {
  269. _remoteDebuggingEnabled = true;
  270. Web.EnableRemoteDebugging();
  271. }
  272. _options = options;
  273. // Only set _webView *after* the webview has been initialized to guarantee
  274. // that WebViewPrefab.WebView is ready to use as long as it's not null.
  275. var webView = initializedWebView == null ? Web.CreateWebView(_options.preferredPlugins) : initializedWebView;
  276. Web.CreateMaterial(viewMaterial => {
  277. _viewMaterial = viewMaterial;
  278. _view.Material = viewMaterial;
  279. _initWebViewIfReady(webView, width, height);
  280. });
  281. if (_options.disableVideo) {
  282. _videoLayerDisabled = true;
  283. if (_videoLayer != null) {
  284. _videoLayer.Visible = false;
  285. }
  286. _initWebViewIfReady(webView, width, height);
  287. } else {
  288. Web.CreateVideoMaterial(videoMaterial => {
  289. if (videoMaterial == null) {
  290. _videoLayerDisabled = true;
  291. if (_videoLayer != null) {
  292. _videoLayer.Visible = false;
  293. }
  294. } else {
  295. _videoMaterial = videoMaterial;
  296. _videoLayer.Material = videoMaterial;
  297. _setVideoRect(new Rect(0, 0, 0, 0));
  298. }
  299. _initWebViewIfReady(webView, width, height);
  300. });
  301. }
  302. }
  303. void _initPointerInputDetector(IWebView webView, IPointerInputDetector previousPointerInputDetector = null) {
  304. if (previousPointerInputDetector != null) {
  305. previousPointerInputDetector.BeganDrag -= InputDetector_BeganDrag;
  306. previousPointerInputDetector.Dragged -= InputDetector_Dragged;
  307. previousPointerInputDetector.PointerDown -= InputDetector_PointerDown;
  308. previousPointerInputDetector.PointerExited -= InputDetector_PointerExited;
  309. previousPointerInputDetector.PointerMoved -= InputDetector_PointerMoved;
  310. previousPointerInputDetector.PointerUp -= InputDetector_PointerUp;
  311. previousPointerInputDetector.Scrolled -= InputDetector_Scrolled;
  312. }
  313. if (_pointerInputDetector == null) {
  314. _pointerInputDetector = GetComponentInChildren<IPointerInputDetector>();
  315. }
  316. // Only enable the PointerMoved event if the webview implementation has MovePointer().
  317. _pointerInputDetector.PointerMovedEnabled = (webView as IWithMovablePointer) != null;
  318. _pointerInputDetector.BeganDrag += InputDetector_BeganDrag;
  319. _pointerInputDetector.Dragged += InputDetector_Dragged;
  320. _pointerInputDetector.PointerDown += InputDetector_PointerDown;
  321. _pointerInputDetector.PointerExited += InputDetector_PointerExited;
  322. _pointerInputDetector.PointerMoved += InputDetector_PointerMoved;
  323. _pointerInputDetector.PointerUp += InputDetector_PointerUp;
  324. _pointerInputDetector.Scrolled += InputDetector_Scrolled;
  325. }
  326. void _initWebViewIfReady(IWebView webView, float width, float height) {
  327. if (_view.Texture == null || (!_videoLayerDisabled && _videoLayer.Texture == null)) {
  328. // Wait until both views' textures are ready.
  329. return;
  330. }
  331. var initializedWebViewWasProvided = webView.IsInitialized;
  332. if (initializedWebViewWasProvided) {
  333. // An initialized webview was provided via WebViewPrefab.Init(IWebView),
  334. // so just hook up its existing textures.
  335. _view.Texture = webView.Texture;
  336. if (_videoLayer != null) {
  337. _videoLayer.Texture = webView.VideoTexture;
  338. }
  339. } else {
  340. // Set the resolution prior to initializing the webview
  341. // so the initial size is correct.
  342. var initialResolution = _getInitialResolution();
  343. if (initialResolution <= 0) {
  344. WebViewLogger.LogWarningFormat("Invalid value for InitialResolution ({0}) will be ignored.", initialResolution);
  345. } else {
  346. webView.SetResolution(initialResolution);
  347. }
  348. var videoTexture = _videoLayer == null ? null : _videoLayer.Texture;
  349. webView.Init(_view.Texture, width, height, videoTexture);
  350. }
  351. _attachWebViewEventHandlers(webView);
  352. // Init the pointer input detector just before setting _webView so that
  353. // SetPointerInputDetector() will behave correctly if it's called before _webView is set.
  354. _initPointerInputDetector(webView);
  355. // _webView can be set now that the webview is initialized.
  356. _webView = webView;
  357. var handler = Initialized;
  358. if (handler != null) {
  359. handler(this, EventArgs.Empty);
  360. }
  361. if (!String.IsNullOrEmpty(InitialUrl)) {
  362. if (initializedWebViewWasProvided) {
  363. WebViewLogger.LogWarning("Custom InitialUrl value will be ignored because an initialized webview was provided.");
  364. } else {
  365. var url = InitialUrl.Trim();
  366. if (!url.Contains(":")) {
  367. url = "http://" + url;
  368. }
  369. webView.LoadUrl(url);
  370. }
  371. }
  372. }
  373. void InputDetector_BeganDrag(object sender, EventArgs<Vector2> eventArgs) {
  374. _previousDragPoint = _convertRatioPointToUnityUnits(_pointerDownRatioPoint);
  375. }
  376. void InputDetector_Dragged(object sender, EventArgs<Vector2> eventArgs) {
  377. if (DragMode == DragMode.Disabled || _webView == null) {
  378. return;
  379. }
  380. var point = eventArgs.Value;
  381. var previousDragPoint = _previousDragPoint;
  382. var newDragPoint = _convertRatioPointToUnityUnits(point);
  383. _previousDragPoint = newDragPoint;
  384. var totalDragDelta = _convertRatioPointToUnityUnits(_pointerDownRatioPoint) - newDragPoint;
  385. if (DragMode == DragMode.DragWithinPage) {
  386. var dragThresholdReached = totalDragDelta.magnitude * _webView.Resolution > DragThreshold;
  387. if (dragThresholdReached) {
  388. _movePointerIfNeeded(point);
  389. }
  390. return;
  391. }
  392. // DragMode == DragMode.DragToScroll
  393. var dragDelta = previousDragPoint - newDragPoint;
  394. _scrollIfNeeded(dragDelta, _pointerDownRatioPoint);
  395. // Check whether to cancel a pending viewport click so that drag-to-scroll
  396. // doesn't unintentionally trigger a click.
  397. if (_clickIsPending) {
  398. var dragThresholdReached = totalDragDelta.magnitude * _webView.Resolution > DragThreshold;
  399. if (dragThresholdReached) {
  400. _clickIsPending = false;
  401. }
  402. }
  403. }
  404. protected virtual void InputDetector_PointerDown(object sender, PointerEventArgs eventArgs) {
  405. _pointerIsDown = true;
  406. _pointerDownRatioPoint = eventArgs.Point;
  407. if (!ClickingEnabled || _webView == null) {
  408. return;
  409. }
  410. if (DragMode == DragMode.DragWithinPage) {
  411. var webViewWithPointerDown = _webView as IWithPointerDownAndUp;
  412. if (webViewWithPointerDown != null) {
  413. webViewWithPointerDown.PointerDown(eventArgs.Point, eventArgs.ToPointerOptions());
  414. return;
  415. } else if (!_loggedDragWarning) {
  416. _loggedDragWarning = true;
  417. WebViewLogger.LogWarningFormat("The WebViewPrefab's DragMode is set to DragWithinPage, but the webview implementation for this platform ({0}) doesn't support the PointerDown and PointerUp methods needed for dragging within a page. For more info, see <em>https://developer.vuplex.com/webview/IWithPointerDownAndUp</em>.", _webView.PluginType);
  418. // Fallback to setting _clickIsPending so Click() can be called.
  419. }
  420. }
  421. // Defer calling PointerDown() for DragToScroll so that the click can
  422. // be cancelled if drag exceeds the threshold needed to become a scroll.
  423. _clickIsPending = true;
  424. }
  425. void InputDetector_PointerExited(object sender, EventArgs eventArgs) {
  426. if (HoveringEnabled) {
  427. // Remove the hover state when the pointer exits.
  428. _movePointerIfNeeded(Vector2.zero);
  429. }
  430. }
  431. void InputDetector_PointerMoved(object sender, EventArgs<Vector2> eventArgs) {
  432. // InputDetector_Dragged handles calling MovePointer while dragging.
  433. if (_pointerIsDown || !HoveringEnabled) {
  434. return;
  435. }
  436. _movePointerIfNeeded(eventArgs.Value);
  437. }
  438. protected virtual void InputDetector_PointerUp(object sender, PointerEventArgs eventArgs) {
  439. _pointerIsDown = false;
  440. if (!ClickingEnabled || _webView == null) {
  441. return;
  442. }
  443. var webViewWithPointerDownAndUp = _webView as IWithPointerDownAndUp;
  444. if (DragMode == DragMode.DragWithinPage && webViewWithPointerDownAndUp != null) {
  445. var totalDragDelta = _convertRatioPointToUnityUnits(_pointerDownRatioPoint) - _convertRatioPointToUnityUnits(eventArgs.Point);
  446. var dragThresholdReached = totalDragDelta.magnitude * _webView.Resolution > DragThreshold;
  447. var pointerUpPoint = dragThresholdReached ? eventArgs.Point : _pointerDownRatioPoint;
  448. webViewWithPointerDownAndUp.PointerUp(pointerUpPoint, eventArgs.ToPointerOptions());
  449. } else {
  450. if (!_clickIsPending) {
  451. return;
  452. }
  453. _clickIsPending = false;
  454. // PointerDown() and PointerUp() don't support the preventStealingFocus parameter.
  455. if (webViewWithPointerDownAndUp == null || _options.clickWithoutStealingFocus) {
  456. _webView.Click(eventArgs.Point, _options.clickWithoutStealingFocus);
  457. } else {
  458. var pointerOptions = eventArgs.ToPointerOptions();
  459. webViewWithPointerDownAndUp.PointerDown(eventArgs.Point, pointerOptions);
  460. webViewWithPointerDownAndUp.PointerUp(eventArgs.Point, pointerOptions);
  461. }
  462. }
  463. var handler = Clicked;
  464. if (handler != null) {
  465. handler(this, new ClickedEventArgs(eventArgs.Point));
  466. }
  467. }
  468. void InputDetector_Scrolled(object sender, ScrolledEventArgs eventArgs) {
  469. var sensitivity = _getScrollingSensitivity();
  470. var scaledScrollDelta = new Vector2(
  471. eventArgs.ScrollDelta.x * sensitivity,
  472. eventArgs.ScrollDelta.y * sensitivity
  473. );
  474. _scrollIfNeeded(scaledScrollDelta, eventArgs.Point);
  475. }
  476. void _movePointerIfNeeded(Vector2 point) {
  477. var webViewWithMovablePointer = _webView as IWithMovablePointer;
  478. if (webViewWithMovablePointer == null) {
  479. return;
  480. }
  481. if (point != _previousMovePointerPoint) {
  482. _previousMovePointerPoint = point;
  483. webViewWithMovablePointer.MovePointer(point);
  484. }
  485. }
  486. void OnDestroy() {
  487. if (_webView != null && !_webView.IsDisposed) {
  488. _webView.Dispose();
  489. }
  490. Destroy();
  491. // Unity doesn't automatically destroy materials and textures
  492. // when the GameObject is destroyed.
  493. if (_viewMaterial != null) {
  494. Destroy(_viewMaterial.mainTexture);
  495. Destroy(_viewMaterial);
  496. }
  497. if (_videoMaterial != null) {
  498. Destroy(_videoMaterial.mainTexture);
  499. Destroy(_videoMaterial);
  500. }
  501. }
  502. void _scrollIfNeeded(Vector2 scrollDelta, Vector2 point) {
  503. // scrollDelta can be zero when the user drags the cursor off the screen.
  504. if (!ScrollingEnabled || _webView == null || scrollDelta == Vector2.zero) {
  505. return;
  506. }
  507. _webView.Scroll(scrollDelta, point);
  508. var handler = Scrolled;
  509. if (handler != null) {
  510. handler(this, new ScrolledEventArgs(scrollDelta, point));
  511. }
  512. }
  513. protected abstract void _setVideoLayerPosition(Rect videoRect);
  514. void _setVideoRect(Rect videoRect) {
  515. _view.SetCutoutRect(videoRect);
  516. _setVideoLayerPosition(videoRect);
  517. // This code applies a cropping rect to the video layer's shader based on what part of the video (if any)
  518. // falls outside of the viewport and therefore needs to be hidden. Note that the dimensions here are divided
  519. // by the videoRect's width or height, because in the videoLayer shader, the width of the videoRect is 1
  520. // and the height is 1 (i.e. the dimensions are normalized).
  521. float videoRectXMin = Math.Max(0, - 1 * videoRect.x / videoRect.width);
  522. float videoRectYMin = Math.Max(0, -1 * videoRect.y / videoRect.height);
  523. float videoRectXMax = Math.Min(1, (1 - videoRect.x) / videoRect.width);
  524. float videoRectYMax = Math.Min(1, (1 - videoRect.y) / videoRect.height);
  525. var videoCropRect = Rect.MinMaxRect(videoRectXMin, videoRectYMin, videoRectXMax, videoRectYMax);
  526. if (videoCropRect == new Rect(0, 0, 1, 1)) {
  527. // The entire video rect fits within the viewport, so set the cropt rect to zero to disable it.
  528. videoCropRect = new Rect(0, 0, 0, 0);
  529. }
  530. _videoLayer.SetCropRect(videoCropRect);
  531. }
  532. void _throwExceptionIfInitialized() {
  533. if (_webView != null) {
  534. throw new InvalidOperationException("Init() cannot be called on a WebViewPrefab that has already been initialized.");
  535. }
  536. }
  537. void Update() {
  538. // Check if LogConsoleMessages is changed from false to true at runtime.
  539. if (LogConsoleMessages && !_consoleMessageLoggedHandlerAttached && _webView != null) {
  540. _consoleMessageLoggedHandlerAttached = true;
  541. _webView.ConsoleMessageLogged += WebView_ConsoleMessageLogged;
  542. }
  543. }
  544. void WebView_ConsoleMessageLogged(object sender, ConsoleMessageEventArgs eventArgs) {
  545. if (!LogConsoleMessages) {
  546. return;
  547. }
  548. var message = "[Web Console] " + eventArgs.Message;
  549. if (eventArgs.Source != null) {
  550. message += String.Format(" ({0}:{1})", eventArgs.Source, eventArgs.Line);
  551. }
  552. switch (eventArgs.Level) {
  553. case ConsoleMessageLevel.Error:
  554. WebViewLogger.LogError(message, false);
  555. break;
  556. case ConsoleMessageLevel.Warning:
  557. WebViewLogger.LogWarning(message, false);
  558. break;
  559. default:
  560. WebViewLogger.Log(message, false);
  561. break;
  562. }
  563. }
  564. }
  565. }