TLabWebViewVR icon indicating copy to clipboard operation
TLabWebViewVR copied to clipboard

YouTube Music freezing the webviews

Open Priyanshu-CODERX opened this issue 11 months ago • 23 comments

I’ve integrated the TLabWebView plugin with the Meta XR SDK, but I’m encountering an issue with YouTube Music. When I open the YouTube Music website, it freezes the entire webview system. After that, no other URLs can be loaded, even if I destroy the webview instance at runtime. However, the music continues playing in the background. If I try to instantiate a new webview, it fails to load, and this sometimes leads to the app crashing. Even when the app doesn’t crash, the music persists in the background, and closing the app doesn’t stop it. I ultimately have to restart the headset to resolve the issue.

Any solution to this would be highly appreciated.

Thanks

Priyanshu-CODERX avatar Jan 02 '25 09:01 Priyanshu-CODERX

Hi @Priyanshu-CODERX , thanks for the report. I just read and not everything you mentioned, but I tested music.youtube.com on my sample project in oculus quest 2 and meta quest, meta quest 2.

The following issues are I previously noticed and similar the issue that you mentioned. Maybe thier related and maybe issues are already fixed in the latest version.


  1. Can't load url after load first page.
  2. Media is still playing even after destroying the WebView instance.

Additionally, please paying attention that it is impossible to play multiple media with multiple WebView. If you playing media with WebView, other WebView instance's media will be paused.


What plugin version are you currently using ? TLabWebViewVR uses TLabWebView and latest version is v1.0.2. It is destractive update for v0.* to v1.* (especially event callback and javascript interface) and if you currently using v0.* (it can check via package.json), may it will be a hard work to migrate to v1.*. Please tell me about update plugin and how to support to latest api if you have any question.


Currently, I couldn't check the issue for app crash and media still playing even if close app. If it is possible, it is preferble to attach logcat's output. And if problem could reproduce on my sample project, it is most preferable to show step of how reproduce error and attach logcat's output of it.

TLabAltoh avatar Jan 02 '25 10:01 TLabAltoh

Hi @TLabAltoh, thank you for your prompt response. I am currently using version 1.0.1 of the plugin. The pages are loading correctly, including YouTube and other sites. However, when I visit YouTube Music and play a song, the entire WebView freezes. Interestingly, no errors are being logged in logcat.

For YouTube, it is expected behavior that when multiple WebView instances are open, playing media in one instance pauses playback in the others. However, with YouTube Music, the website completely freezes not after media starts playing but when I try to search for something else through the search bar. Even attempting to destroy the frozen instance doesn’t stop the playback, and this causes every other WebView instance to freeze as well. Any new instance that I try to open after this issue, the page remains completely blank.

Previously, there was an issue where destroying a WebView instance while a YouTube video was playing caused the app to crash. My workaround for that was to redirect the WebView to a static page before destroying the instance. This solution worked well for other media players. However, with YouTube Music, the WebView does not even load the static page, making this workaround ineffective.

I couldn’t find anything unusual in the logcat to indicate why the freeze and crash occurred, but I will recheck it. Meanwhile, here’s how you can reproduce the issue:

  1. Open YouTube Music in a browser instance within the WebView and play a song.
  2. Attempt to search for something else using the search bar.
  3. Then try to add more Webview instances

When you do this, all the WebView instances freeze. If you try to use other WebView instances or create new ones, you’ll notice the entire system freezes. In some cases, it crashes. When a crash occurs, the media continues to play quite abruptly.

I am attaching a couple of recordings for your reference. https://youtu.be/yP_5Aydb-sg https://youtu.be/G_b8o4EpKOM

Also, I would like to know if there is a better way to destroy these instances? cos currently I am just using Destroy() any cleanups that I would have to do?

Priyanshu-CODERX avatar Jan 02 '25 12:01 Priyanshu-CODERX

Thanks for the details ... I'll check them again !

TLabAltoh avatar Jan 02 '25 12:01 TLabAltoh

This is just a progress report, but I was able to reproduce the error (currently, I only checked https://youtu.be/yP_5Aydb-sg yet) ... I think this issue is related to the Horizon OS problem. Horizon OS doesn't support standard android widget components in 3d app, so popup windows commonly used in android app could not be used in (oculus/meta) quest's 3d app. This causes WebView's some popup not to work on (oculus/meta) quest (like datatime-picker, and this case) and additionally it also causes problem that app couldn't close properly when it's problem called.

In this video, I was asked "Can you leave this site? Changes you have made may not be saved. (cancel / leave the page)". It didn't show up in the (oculus/meta) quest's WebView.


Maybe the above problem can't be solved from my side because it's an OS layer problem. So I suggest to use GeckoView plugin instead of WebView as browser engine. It is experimental and poor documentation, but I support GeckoView as browser engine in TLabWebView. It is mostly the same as WebView, but could handle the browser's popup window event as a customisable uGUI.

Please note that changing the browser engine from WebView to GeckoView may result in compromises. For example, WebView has a more powerful javascript interface than GeckoView, it could send and receive data from both javascript and Unity C#. But GeckoView only has the function of sending data from C# to javascript. And WebView's plugin interface has been in development longer than GeckoView's it. GeckoView feature is experimental, so possibility to include annoying problem in other part. Also, GeckoView plugin is not a system service like WebView. It is binary file and need to be included in application. So it cause increase application size (maybe +200MB).

Please see here for replace browser engine from WebView to GeckoView (Maybe the explanation is too rough to understand).

In the meantime, after a little time, I will check the other issue (https://youtu.be/G_b8o4EpKOM). But please don't hesitate to ask me about replacing the browser engine from WebView to GeckoView or any other opinion on this topic.


Also, I would like to know if there is a better way to destroy these instances? cos currently I am just using Destroy() any cleanups that I would have to do?

↑ using Destroy() is the intended and correct way !

TLabAltoh avatar Jan 02 '25 20:01 TLabAltoh

Hey, thanks for the update! I tried using GeckoView as you suggested, and it resolved the issue with destroying the YouTube Music instance—there are no longer any problems when I do so.

However, I encountered another issue. After playing music on YouTube Music and performing a search through the search bar, a popup appears as expected when leaving the site. But when I click on any of the buttons in the popup, the app crashes.

I’m attaching the crash log for your reference. Let me know if you need additional details or further testing!

2025-01-03 10:39:29.322 30461 30461 Error TLabWebView (Gecko) loadUrl: https://www.google.com/search?q=gmail
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime FATAL EXCEPTION: main
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime Process: com.PriyanshuBhattacharjee.Widgets, PID: 30461
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime java.lang.RuntimeException: org.json.JSONException: End of input at character 0 of 
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at com.tlab.webkit.BaseOffscreenBrowser.lambda$PostDialogResult$0$com-tlab-webkit-BaseOffscreenBrowser(BaseOffscreenBrowser.java:95)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at com.tlab.webkit.BaseOffscreenBrowser$$ExternalSyntheticLambda4.run(Unknown Source:6)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at android.os.Handler.handleCallback(Handler.java:938)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at android.os.Handler.dispatchMessage(Handler.java:99)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at android.os.Looper.loopOnce(Looper.java:214)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at android.os.Looper.loop(Looper.java:304)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at android.app.ActivityThread.main(ActivityThread.java:7918)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at java.lang.reflect.Method.invoke(Native Method)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1010)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime Caused by: org.json.JSONException: End of input at character 0 of 
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at org.json.JSONTokener.syntaxError(JSONTokener.java:460)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at org.json.JSONTokener.nextValue(JSONTokener.java:101)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at org.json.JSONObject.<init>(JSONObject.java:168)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at org.json.JSONObject.<init>(JSONObject.java:185)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	at com.tlab.webkit.BaseOffscreenBrowser.lambda$PostDialogResult$0$com-tlab-webkit-BaseOffscreenBrowser(BaseOffscreenBrowser.java:93)
2025-01-03 10:39:31.606 30461 30461 Error AndroidRuntime 	... 9 more

Priyanshu-CODERX avatar Jan 03 '25 05:01 Priyanshu-CODERX

Also, I cannot seem to view youtube video on fullscreen and this error is popping up

2025-01-03 12:13:37.907 15802 17264 Error GeckoConsole [JavaScript Error: "TypeError: Not in fullscreen mode" {file: "resource://gre/modules/GeckoViewContent.sys.mjs" line: 176}]

Priyanshu-CODERX avatar Jan 03 '25 06:01 Priyanshu-CODERX

Please update TLabWebView to the latest version (v1.0.3). I noticed a crash yesterday when clicking the popup window button, now it is fixed.


Also, full screen is not supported at the moment in Gecko feature. I should have mentioned this in the previous comment, but I forgot.

See here for details. This developer had the same problem and discussed it with the GeckoView team. Maybe the problem is related to GeckoView's architecture.


I know it is inconvenient that there is no complete solution. I'm just trying to figure out what to do about it ...

TLabAltoh avatar Jan 03 '25 08:01 TLabAltoh

Hi! Thanks for your help on this. 😊 I’ll update to version 1.0.3.

I also wanted to ask how to inject JavaScript into WebView. I tried using EvaluateJS, but nothing seemed to happen. My goal is to block popups from appearing on these websites so that I can use WebView without any crashes.

Below is the code that blocks the popups from happening.

window.onbeforeunload = null;

// Overwrite addEventListener to block any 'beforeunload' events
const originalAddEventListener = window.addEventListener;
window.addEventListener = function (type, listener, options) {
    if (type === 'beforeunload') {
        console.warn("Blocked a 'beforeunload' listener from being added.");
        return;
    }
    originalAddEventListener.call(this, type, listener, options);
};

const removeUnloadListeners = () => {
    const eventListeners = window.getEventListeners?.(window); // Chrome-only
    if (eventListeners?.beforeunload) {
        eventListeners.beforeunload.forEach(listener => {
            window.removeEventListener('beforeunload', listener.listener);
        });
    }
};

removeUnloadListeners?.();

// Add a final 'beforeunload' handler to ensure no returnValue is set
window.addEventListener("beforeunload", (event) => {
    console.log("Ensuring no prompt is shown during unload.");
    event.preventDefault();
});

Priyanshu-CODERX avatar Jan 03 '25 15:01 Priyanshu-CODERX

I see, that's the way to do it. I have not tested the code on my environment, so it is not sure, but maybe removing the commentout will make it work. EvaluateJS executes javascript via the LoaUrl function in the native plugin. So javascript is treated as a single line string. So it is necessary to remove the commentout.

PS: I'll check myself later.

TLabAltoh avatar Jan 03 '25 16:01 TLabAltoh

I tested on my environment (only my Android phone, it is not Oculus Quest but based on the same OS so it is enough to test) and faced failure. And this problem seems to be caused by my native plugin problem, so I updated the plugin from v1.0.3 to v1.0.4. Now your javascript code works on my enviroment (but please remove comment as mentioned above). Please update plugin, maybe problem solved ...

TLabAltoh avatar Jan 04 '25 13:01 TLabAltoh

Hey! mate thanks for your support. I tried the package and I am currently getting this error

Library\PackageCache\com.tlabaltoh.webview@7d3a31ac20\Runtime\Sample\JSSnippets.cs(7,34): error CS0246: The type or namespace name 'BrowserContainer' could not be found (are you missing a using directive or an assembly reference?)

I tried deleting it but as it's a part of the package itself it keeps coming back.

Priyanshu-CODERX avatar Jan 08 '25 14:01 Priyanshu-CODERX

Thanks for the report ... Currently investigating. Could you try restarting the editor and reinstalling the package? I have not been able to reproduce the error and do not know the cause of the error ...

Your package's commit id is certainly latest version. So maybe there happened to be some git error or conflict of script file's metadata (not sure) ?

TLabAltoh avatar Jan 08 '25 14:01 TLabAltoh

Maybe this (form A) or this (form B) will help ?

form A -----------------------------------
nattybumppo
Did you try deleting your Library and letting Unity re-create it? Alternatively, you might be able to fix it by selecting Help —> Reset Packages to defaults in the menu.
form B -----------------------------------
andrew-lukasik
Open up project’s package manager and either try installing different version number or uninstall ${package name} package entirely

TLabAltoh avatar Jan 08 '25 15:01 TLabAltoh

Hi! I tried the suggested solutions, but unfortunately, they didn’t resolve the issue. I plan to explore a few more approaches to see if they work.

In the meantime, I deleted the script and built the app again, but the JavaScript code still doesn’t seem to execute. I also tested it with your sample, and the issue persists there as well.

Currently, I’m using the BrowserContainer to access the EvaluateJS() function. This function is invoked whenever the enter or search button is pressed, utilizing the OnEnter event from the SearchBar script. However, the same problem continues.

Interestingly, it worked temporarily when I tried this approach a few days ago with the update you provided. However, after building the app a second time, the issue reappeared.

It'd be great if you could take a look at that

Thanks

Priyanshu-CODERX avatar Jan 08 '25 17:01 Priyanshu-CODERX

I re-tested and noticed that beforeunload event is ignored only when loading media url with autoplay. Before, I only tested com.music.youtube/{media url} with autoplay so it seems passed but actually the problem is not solved ... (very very sorry!! 😞). Also, I found this form and it says that getEventListeners is a dev-console api, so it didn't defined in WebView's javascript engine. Maybe that's why javascript didn't work as intended on WebView (maybe it is difficult to remove the pre-added event from window).

Therefor, I have added OnPageStart callback to avoid specific events added to window in the latest update. It was also tested on oculus-quest and next time it should work (The OnPageStart callback is not synchronized with the WebView loading process, but this approach may work in most cases). Please update TLabWebView from v1.0.4 to v1.0.5. bellow is the demo movie and code example (the problem of JSSnippets.cs's compile error is still investigating).


C#

using UnityEngine;

namespace TLab.WebView.Sample
{
    public class JSSnippets : MonoBehaviour
    {
        [SerializeField] private BrowserContainer m_container;

        public void DisableBeforeUnload()
        {
            var js = Resources.Load<TextAsset>("TLab/WebView/Samples/Scripts/JS/disable-beforunload")?.ToString();
            m_container.browser.EvaluateJS(js);
        }
    }
}

javascript

// disable-beforeunload.txt
const originalAddEventListener = window.addEventListener;
window.addEventListener = function (type, listener, options) {
    if (type === 'beforeunload') {
        console.warn("Blocked a 'beforeunload' listener from being added.");
        return;
    }
    originalAddEventListener.call(this, type, listener, options);
};

TLabAltoh avatar Jan 08 '25 19:01 TLabAltoh

Hey mate, thanks a ton for all your help! The scripts are now executing properly, and I also managed to resolve the conflict error. It turned out that there were a few duplicate files in the project causing the issue. Deleting the entire package along with those duplicates fixed it.

However, there's still one issue remaining: the "GoForward" functionality isn't working for the webviews, even though the "GoBack" feature works perfectly.

Priyanshu-CODERX avatar Jan 09 '25 16:01 Priyanshu-CODERX

However, there's still one issue remaining: the "GoForward" functionality isn't working for the webviews, even though the "GoBack" feature works perfectly.

Thanks for reporting, it is a related plugin bug. So I released fixed version just now but please note that this update didn't updated package version code (treated as tiny update). Please update TLabWebView.

TLabAltoh avatar Jan 09 '25 16:01 TLabAltoh

Ah, awesome, thanks! :) Would it possible to add an event callback that gets invoked when any input field is clicked? The goal is to make the keyboard appear only when an input field is selected.

Priyanshu-CODERX avatar Jan 09 '25 17:01 Priyanshu-CODERX

It is possible to switch keybord's active state via html input's focus event, but it is with some limmitation. Here is an example and this feature has implemented on this sample repositly.


C#

original is here


using UnityEngine;
using UnityEngine.EventSystems;
using TLab.VKeyborad;


namespace TLab.WebView.Sample
{
    public class FocusInOutInteractionSample : MonoBehaviour, IPointerDownHandler
    {
        [SerializeField] private SearchBar m_searchBar;
        [SerializeField] private BaseInputField m_inputField;
        [SerializeField] private BrowserContainer m_container;

        public void OnPageFinish(string url)
        {
            var js = JSUtil.ToVariable("go", gameObject.name) + JSUtil.ToVariable("method", nameof(OnMessage));
            js += Resources.Load<TextAsset>("TLab/WebView/Samples/Scripts/JS/focus-in-out-interaction")?.ToString();

            m_container.browser.EvaluateJS(js);
        }

        public void OnMessage(string message)
        {
            Debug.Log("OnMessage: " + message);

            switch (message)
            {
                case "Focusin":
                    m_inputField.OnFocus(true);
                    break;
                case "Focusout":
                    m_inputField.OnFocus(false);
                    break;
            }
        }

        public void OnPointerDown(PointerEventData eventData) => m_searchBar.OnFocus(false);
    }
}


javascript

original is here


function searchShadowRoot(node, roots) {
    if (node == null) {
        return;
    }

    if (node.shadowRoot != undefined && node.shadowRoot != null) {
        roots.push(node.shadowRoot);
        searchShadowRoot(node.shadowRoot, roots);
    }

    for (var i = 0; i < node.childNodes.length; i++) {
        searchShadowRoot(node.childNodes[i], roots);
    }
}


function getAllRoot() {
        var roots = [document];
        searchShadowRoot(document, roots);
        return roots;
}

var roots = getAllRoot();

function focusin (e) {
    const target = e.target;
    if (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA') {
        window.tlab.unitySendMessage(go, method, 'Focusin');
    }
}


function focusout (e) {
    const target = e.target;
    if (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA') {
        window.tlab.unitySendMessage(go, method, 'Focusout');
    }
}


for (var i = 0; i < roots.length; i++) {
    roots[i].removeEventListener('focusin', focusin);
    roots[i].removeEventListener('focusout', focusout);

    roots[i].addEventListener('focusin', focusin);
    roots[i].addEventListener('focusout', focusout);
}


This approach didn't work on some website because it's impossible to add event listener to iframe's html document if iframe content's server doesn't allow crossorigin access. So above example will work on most of website, but won't work on only some iframe content.

other solution

Oculus Quest can use the system keyboard on unity and also works on the TLabWebView's html input element (this feature is not limited even if iframe). Please see here for how to setup system keyborad on unity.


WebView doesn't have feature for handling html element's event other than javascript. So I am still considering more efficient way but this is only way I came up with ...

TLabAltoh avatar Jan 09 '25 17:01 TLabAltoh

Thanks mate for the suggestions, I have switched to system keyboard for now but I will be experimenting with the other approach as well.

Thanks for all the help :)

Priyanshu-CODERX avatar Jan 12 '25 08:01 Priyanshu-CODERX

Hi! I was trying to publish the app to Meta Quest Store through RC channel and after running the basic tests, this came up

Unsafe SSL override in WebViews
Summary
This security vulnerability is important, consider fixing it.
Location of Vulnerable Code in Your Build
com.tlab.webkit.chromium.UnityConnect
Description
Your application may contain an unsafe implementation of the WebView's [onReceivedSslError() method](https://www.oculus.com/lynx/?u=https%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fwebkit%2FWebViewClient.html%23onReceivedSslError(android.webkit.WebView%2C%2520android.webkit.SslErrorHandler%2C%2520android.net.http.SslError)&e=AT0HN6RWgLynCRtwcCSOzSVvlpMDUhi7C5saZwaY5p4unt4S4-GxIACJX_OPzTQp1Fn4oADk7Q_rwvZvRiF5XstftUzyuAWAolfkkk_WAtDpvOgW0Llcn_BXIEpgYobFNELMZ31ntKzTQXflaLkeRA) with a call to `handler.proceed() with insufficient validations. This may cause the WebView to ignore SSL certificate validation errors, making the application vulnerable to man-in-the-middle attacks.
Recommendation
Modify the vulnerable Java class to enable the Android default SSL certificate validation mechanism. This can be achieved by:
using cancel to surface the SSL certification validation errors, or- remove any onReceivedSslError() method override defined in the vulnerable classes

More information about SSL validation in WebViews can be found in [the Android documentation](https://www.oculus.com/lynx/?u=https%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fwebkit%2FWebViewClient.html%23onReceivedSslError(android.webkit.WebView%2C%2520android.webkit.SslErrorHandler%2C%2520android.net.http.SslError)&e=AT0HN6RWgLynCRtwcCSOzSVvlpMDUhi7C5saZwaY5p4unt4S4-GxIACJX_OPzTQp1Fn4oADk7Q_rwvZvRiF5XstftUzyuAWAolfkkk_WAtDpvOgW0Llcn_BXIEpgYobFNELMZ31ntKzTQXflaLkeRA)
Secure Code
Here is an example to use cancel to surface the SSL certificate validation error:
void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
   handler.cancel();
}

It would be really great if you could take a look at this.

Priyanshu-CODERX avatar Jan 15 '25 16:01 Priyanshu-CODERX

Hi @Priyanshu-CODERX, Thank you for your feedback and suggestions ! I am releasing App-Store compatible version. Please uninstall current version and switch to the branch of the package with the following URL.

add package from git URL ...

https://github.com/TLabAltoh/TLabWebView.git#appstore-compatible-upm

This version just replaced native plugin (libTLabWebView.aar) and please note that this version will not be able to load insecure websites (URL starting with http://).

TLabAltoh avatar Jan 15 '25 17:01 TLabAltoh

Thanks mate, that fixed it

Priyanshu-CODERX avatar Jan 18 '25 16:01 Priyanshu-CODERX