/**
* Copyright (c) 2021 Vuplex Inc. All rights reserved.
*
* Licensed under the Vuplex Commercial Software Library License, you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* https://vuplex.com/commercial-library-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if UNITY_IOS && !UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
namespace Vuplex.WebView {
///
/// The `IWebView` implementation used by 3D WebView for iOS.
/// This class also includes extra methods for iOS-specific functionality.
///
public class iOSWebView : BaseWebView, IWebView {
public WebPluginType PluginType {
get {
return WebPluginType.iOS;
}
}
public static iOSWebView Instantiate() {
return (iOSWebView) new GameObject().AddComponent();
}
public override void Init(Texture2D viewportTexture, float width, float height, Texture2D videoTexture) {
base.Init(viewportTexture, width, height, videoTexture);
_nativeWebViewPtr = WebView_new(
gameObject.name,
_nativeWidth,
_nativeHeight,
videoTexture != null,
SystemInfo.graphicsDeviceType != GraphicsDeviceType.Metal
);
}
public override void Click(Vector2 point, bool preventStealingFocus) {
_assertValidState();
if (preventStealingFocus) {
int nativeX = (int) (point.x * _nativeWidth);
int nativeY = (int) (point.y * _nativeHeight);
WebView_clickWithoutStealingFocus(_nativeWebViewPtr, nativeX, nativeY);
} else {
Click(point);
}
}
///
/// Overrides `BaseWebView.CaptureScreenshot()` because it uses too much
/// memory on iOS.
///
public override void CaptureScreenshot(Action callback) {
_assertValidState();
IntPtr unmanagedBytes = IntPtr.Zero;
int unmanagedBytesLength = 0;
WebView_captureScreenshot(_nativeWebViewPtr, ref unmanagedBytes, ref unmanagedBytesLength);
// Load the results into a managed array.
var managedBytes = new byte[unmanagedBytesLength];
Marshal.Copy(unmanagedBytes, managedBytes, 0, unmanagedBytesLength);
WebView_freeMemory(unmanagedBytes);
callback(managedBytes);
}
public override void EnableViewUpdates() {
if (_currentVideoNativeTexture != IntPtr.Zero) {
_videoTexture.UpdateExternalTexture(_currentVideoNativeTexture);
}
base.EnableViewUpdates();
}
///
/// Returns a file URL for resource included in the iOS app bundle.
///
///
/// This is useful for getting a file URL to a local file so that it
/// can be loaded via `IWebView.LoadUrl()`.
///
///
/// var fileUrl = iOSWebView.GetFileUrlForBundleResource("my-static-files/my-webpage.html");
///
[Obsolete("iOSWebView.GetFileUrlForBundleResource is now deprecated. You can now use LoadUrl(\"streaming-assets://{path}\") to load a file from StreamingAssets instead.")]
public static string GetFileUrlForBundleResource(string fileName) {
var fileNameSegments = fileName.Split(new char[] {'.'});
if (fileNameSegments.Length < 2) {
throw new ArgumentException(String.Format("The file name must include an extension, but the name provided ({0}) does not.", fileName));
}
var fileExtension = fileNameSegments[fileNameSegments.Length - 1];
var fileNameWithoutExtension = String.Join(".", fileNameSegments.ToList().GetRange(0, fileNameSegments.Length - 1).ToArray());
var stringPtr = WebView_getFileUrlForBundleResource(fileNameWithoutExtension, fileExtension);
var fileUrl = Marshal.PtrToStringAnsi(stringPtr);
return fileUrl;
}
///
/// Overrides `BaseWebView.GetRawTextureData()` because it uses too much
/// memory on iOS.
///
public override void GetRawTextureData(Action callback) {
_assertValidState();
IntPtr unmanagedBytes = IntPtr.Zero;
int unmanagedBytesLength = 0;
WebView_getRawTextureData(_nativeWebViewPtr, ref unmanagedBytes, ref unmanagedBytesLength);
// Load the results into a managed array.
var managedBytes = new byte[unmanagedBytesLength];
Marshal.Copy(unmanagedBytes, managedBytes, 0, unmanagedBytesLength);
WebView_freeMemory(unmanagedBytes);
callback(managedBytes);
}
[Obsolete("iOSWebView.SetCustomUriSchemesEnabled() has been removed. Now when a page redirects to a URI with a custom scheme, 3D WebView will automatically emit the UrlChanged and LoadProgressChanged events for the navigation, but a deep link (i.e. to an external application) won't occur.", true)]
public static void SetCustomUriSchemesEnabled(bool enabled) {}
public static void SetIgnoreCertificateErrors(bool ignore) {
WebView_setIgnoreCertificateErrors(ignore);
}
[Obsolete("iOSWebView.SetNativeKeyboardEnabled() is now deprecated. Please use Web.SetTouchScreenKeyboardEnabled() instead.")]
public static void SetNativeKeyboardEnabled(bool enabled) {
SetTouchScreenKeyboardEnabled(enabled);
}
public static void SetTouchScreenKeyboardEnabled(bool enabled) {
WebView_setTouchScreenKeyboardEnabled(enabled);
}
///
/// Like `Web.SetUserAgent(bool mobile)`, except it sets the user-agent
/// for a single webview instance instead of setting it globally.
///
///
/// If you globally set a default user-agent using `Web.SetUserAgent()`,
/// you can still use this method to override the user-agent for a
/// single webview instance.
///
public void SetUserAgent(bool mobile) {
_assertValidState();
WebView_setUserAgentToMobile(_nativeWebViewPtr, mobile);
}
///
/// Like `Web.SetUserAgent(string userAgent)`, except it sets the user-agent
/// for a single webview instance instead of setting it globally.
///
///
/// If you globally set a default user-agent using `Web.SetUserAgent()`,
/// you can still use this method to override the user-agent for a
/// single webview instance.
///
public void SetUserAgent(string userAgent) {
_assertValidState();
WebView_setUserAgent(_nativeWebViewPtr, userAgent);
}
IntPtr _currentVideoNativeTexture;
readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
void _applyVideoTexture() {
if (_currentVideoNativeTexture == IntPtr.Zero) {
return;
}
var previousNativeTexturePtr = _videoTexture.GetNativeTexturePtr();
_videoTexture.UpdateExternalTexture(_currentVideoNativeTexture);
_videoTexture.Apply();
var newNativeTexturePtr = _videoTexture.GetNativeTexturePtr();
if (!(previousNativeTexturePtr == IntPtr.Zero || previousNativeTexturePtr == newNativeTexturePtr)) {
WebView_destroyTexture(previousNativeTexturePtr, SystemInfo.graphicsDeviceType.ToString());
}
}
///
/// The native plugin invokes this method.
///
void HandleVideoTextureChanged(string textureString) {
var nativeTexture = new IntPtr(Int64.Parse(textureString));
if (nativeTexture == _currentVideoNativeTexture) {
return;
}
var previousNativeTexture = _currentVideoNativeTexture;
_currentVideoNativeTexture = nativeTexture;
if (_viewUpdatesAreEnabled) {
_videoTexture.UpdateExternalTexture(_currentVideoNativeTexture);
}
if (previousNativeTexture != IntPtr.Zero && previousNativeTexture != _currentVideoNativeTexture) {
WebView_destroyTexture(previousNativeTexture, SystemInfo.graphicsDeviceType.ToString());
}
}
void OnEnable() {
// Start the coroutine from OnEnable so that the coroutine
// is restarted if the object is deactivated and then reactivated.
StartCoroutine(_renderPluginOncePerFrame());
}
IEnumerator _renderPluginOncePerFrame() {
while (true) {
yield return _waitForEndOfFrame;
if (!_viewUpdatesAreEnabled || IsDisposed) {
continue;
}
int pointerId = WebView_depositPointer(_nativeWebViewPtr);
GL.IssuePluginEvent(WebView_getRenderFunction(), pointerId);
}
}
[DllImport(_dllName)]
private static extern void WebView_captureScreenshot(IntPtr webViewPtr, ref IntPtr bytes, ref int length);
[DllImport(_dllName)]
static extern void WebView_clickWithoutStealingFocus(IntPtr webViewPtr, int x, int y);
[DllImport(_dllName)]
static extern int WebView_depositPointer(IntPtr pointer);
[DllImport(_dllName)]
static extern void WebView_freeMemory(IntPtr bytes);
[DllImport(_dllName)]
static extern IntPtr WebView_getFileUrlForBundleResource(string fileNameWithoutExtension, string fileExtension);
[DllImport(_dllName)]
static extern void WebView_getRawTextureData(IntPtr webViewPtr, ref IntPtr bytes, ref int length);
[DllImport(_dllName)]
static extern IntPtr WebView_getRenderFunction();
[DllImport(_dllName)]
static extern IntPtr WebView_new(string gameObjectName, int width, int height, bool enableVideoSupport, bool useOpenGL);
[DllImport(_dllName)]
static extern void WebView_setIgnoreCertificateErrors(bool ignore);
[DllImport(_dllName)]
static extern void WebView_setTouchScreenKeyboardEnabled(bool enabled);
[DllImport(_dllName)]
static extern void WebView_setUserAgentToMobile(IntPtr webViewPtr, bool mobile);
[DllImport(_dllName)]
static extern void WebView_setUserAgent(IntPtr webViewPtr, string userAgent);
}
}
#endif