airsdk / Adobe-Runtime-Support

Report, track and discuss issues in Adobe AIR. Monitored by Adobe - and HARMAN - and maintained by the AIR community.
206 stars 11 forks source link

StageWebView2: Loading URLs sometimes causes Error "3228" (Load error: 9.) #1641

Open 2jfw opened 2 years ago

2jfw commented 2 years ago

With StageWebView2 loading URLs sometimes leads to Error 3228 (Load error: 9.).

URL: https://airsdk.dev/

[trace] 13:48:07.218 [ERROR] components.webview.WebViewWrapper Error loading URL: [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] (ID 3228) - Text: Load error: 9.

However, the website is loaded when the error occurs.

Before the actual load operation loadURL() it does not make a difference if the stop() function is called or not. It seems that quick (re)loads loadURL() cause this issue (maybe non-complete loading operation followed by load operation?).

Using AIR 33.1.1.758

2jfw commented 2 years ago

@ajwfrost Any news on this one here? We are now getting reports, that loading URLs occasionally results in this error.

e.g.: [trace] 12:36:09.148 [DEBUG] components.webview.WebViewWrapper loadURL https://duckduckgo.com/ [trace] 12:36:09.149 [DEBUG] components.webview.WebViewWrapper onCompleteLoadURL https://duckduckgo.com/ [trace] 12:36:09.180 [ERROR] components.webview.WebViewWrapper Error loading URL: [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] (ID 3228) - Text: Load error: 9. [trace] Error #2044: Unhandled error:. text=Load error: 9.

2jfw commented 2 years ago

It is strange, that sometimes our UncaughtErrorHandler is actually reacting on the error, since the ErrorEvent is/was caught properly before by.

It seems that loading an URL when it was not completed yet, causes this error. See the following traces: [trace] 13:02:00.213 [DEBUG] components.webview.WebViewWrapper loadURL https://airsdk.dev/ [trace] 13:02:00.544 [DEBUG] components.webview.WebViewWrapper loadURL https://www.dallmeier.com/ [trace] 13:02:00.546 [DEBUG] components.webview.WebViewWrapper onCompleteLoadURL https://www.dallmeier.com/ [trace] 13:02:00.576 [ERROR] components.webview.WebViewWrapper Error loading URL: [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] (ID 3228) - Text: Load error: 9. [trace] Error: Load error: 9. [trace] 13:02:00.590 [FATAL] com.dallmeier.semsyNext.controller.UncaughtErrorController Caught unhandled Exception -- errorID: 3228, [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] [trace] Error #2044: Unhandled error:. text=Load error: 9.

2jfw commented 2 years ago

@ajwfrost Any news on this small issue?

ajwfrost commented 2 years ago

Hi I'm not 100% sure on this, it looks like there's a component being used components.webview.WebViewWrapper so there may be some limits to what we can do.. but to check the behaviour:

It seems that quick (re)loads loadURL() cause this issue (maybe non-complete loading operation followed by load operation?).

So if you kick off a load, and then before it's complete you kick off another load, that's when you see an error? From the documentation, it looks like it's somewhat undefined what should happen with this double-load approach - but I would suggest calling stop() before the second loadURL() method.

Looking at the code, the older webview on Windows has no special handling around this -> it just requests a Navigate on the underlying WebBrowser control. Perhaps that one is automatically handling it? whereas from your output it looks like calling Navigate on the Edge WebView2 component if it's currently loading something else is resulting in a load error with status code '9' which is COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED. So the error event you're seeing is probably associated with the initial loadURL call (which would explain why you see the website actually working...)

The fact of hitting the uncaught error handler is odd. In the code, if there is an event listener, then this will be called, and the dispatch to the uncaught error handler is just in an 'else' block from this. Although it may be that an error event handler which itself throws an exception will also end up in an uncaught exception handler (although at that point, the error should be different ...) - I've just been trying this, have been able to get the load error '9' a lot, but no uncaught errors happening...?

thanks

ajwfrost commented 2 years ago

Hmmm.... although it appears that the stop() method actually also causes the same CONNECTION_ABORTED error code 9 event to be dispatched.

This is just coming from the OS component, so it's a little tricky to know whether we could just swallow those messages, or whether we need to continue passing them through in order to highlight other errors (e.g. if you had started a load and then the network dropped or similar, although potentially that would be a different code...)

So .. would you be okay with leaving this as-is and handling it appropriately in your application (or component) code? I'm not sure what else really makes sense...

thanks

2jfw commented 2 years ago

Right, calling stop() method does not help - the error occurs anyways.

I did some more testing and the Uncaught Error does only occur when you actually change the URL - quickly "reloading" with the same URL does only cause the Load Error 9.

Behavior seems a bit strange - scenario is like the following:


Code snippets:

WebViewWrapper: _webView is of type StageWebView _webView.addEventListener(ErrorEvent.ERROR, onError);

    private function onError(event : ErrorEvent) : void
    {
        _log.error("Error loading URL: " + event.toString() + " (ID " + event.errorID + ") - Text: " + event.text);

        dispatchEvent(event.clone());
    }

UncaughtErrorController: _application is of type WindowedApplication _application.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);

    private function uncaughtErrorHandler(event : UncaughtErrorEvent) : void
    {
        if (event.error is Error)
        {
            var error : Error = event.error as Error;

            _logger.fatal("Caught unhandled Exception '" + error.name + "' -- message: " + error.message + ", errorID: " + error.errorID + "\n" + error.getStackTrace());
        }
        else if (event.error is ErrorEvent)
        {
            var errorEvent : ErrorEvent = event.error as ErrorEvent;

            _logger.fatal("Caught unhandled Exception -- errorID: " + errorEvent.errorID + ", " + errorEvent.toString());
        }
    }

Conclusion:

Scenario 1 with quickly reloading the same URL: no UncaughtError, only onError

[trace] 09:10:12.460 [ERROR] components.webview.WebViewWrapper Error loading URL: [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] (ID 3228) - Text: Load error: 9.

(then nothing else)


Scenario 2 with quickly changing the URL: triggering onError and then causing UncaughtErrorHandler to trigger:

[trace] 09:28:47.607 [ERROR] components.webview.WebViewWrapper Error loading URL: [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] (ID 3228) - Text: Load error: 9.

(then causing Uncaught Error Handler)

[trace] 09:28:47.627 [FATAL] com.dallmeier.semsyNext.controller.UncaughtErrorController Caught unhandled Exception -- errorID: 3228, [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Load error: 9." errorID=3228] [trace] Error #2044: Unhandled error:. text=Load error: 9. [trace] 09:28:47.977 [DEBUG] components.webview.WebViewWrapper onCompleteLoadURL https://duckduckgo.com/


I cannot understand why the allegedly same Error in scenario 1 does not cause the UncaughtErrorHandler to trigger, whilst in scenario 2 it does. Hard to find a suitable work-around for this - any suggestion?

ajwfrost commented 2 years ago

Thanks for the details; I'm wondering what your client app is doing in terms of the listeners etc? The first snippet you've got there is from the WebViewWrapper class, but how are you using that? Presumably you just have one instance of it, add all the listeners at the start, and don't remove them?

I'm wondering whether the uncaught error is the one that's being dispatched within that onError handler i.e. the dispatchEvent(event.clone()); line. Possibly worth changing the event description after cloning it (or just sending a new errorevent), so that you can see if that's the case?

If you're able to package this up into a test SWF, we can try running it here and breaking within the uncaught error handler to see where/why this is being raised...

2jfw commented 2 years ago

The WebViewWrapper class is a component and is instanced (it is inheriting from spark:Group). It has a proper disopse() function, where all event listeners are removed etc, but in both scenarios the view containing the WebViewWrapper is only opened so the dispose function is not called.

I'm wondering whether the uncaught error is the one that's being dispatched within that onError handler i.e. the dispatchEvent(event.clone()); line.

In my tests I did also comment out dispatchEvent(event.clone()); and the UncaughtErrorHandler then does not trigger!

I will try to extract the code and put it in a sample app.

2jfw commented 2 years ago

While creating the test app for this issue in pure ActionScript, and while copying code from the main app, I realized that the module, where the Uncaught Error occurs does actually NOT have an ErrorEvent.ERROR listener attached to the WebViewWrapper so when the WebViewWrapper does re-dispatch the cloned ErrorEvent in the onError function, it is actually working as intended that the UncaughtErrorHandler triggers. Sorry for the inconvenience here!

Not quite sure how to deal with the error though - we could simply ignore it code-wise and do not react on it at all, but then there is no feedback for the user when something wents wrong. I am not sure if aborting the load operation by peforming a new load operation should result in an actual Error 3228 (Load error: 9.). If it was something like more of a warning and/or different error code a differentiation would be possible. Or is this specific Error only dispatched in this specific scenario?

However, we could live with the current implementation and simply display nothing at all when ErrorEvent.ERROR appears...

ajwfrost commented 2 years ago

Okay great, thanks (phew!)

I'm wondering if we can internally track when a load is in progress and then when there's a stop/refresh/reload event causing it to abort, versus when a load is in progress and something else (i.e. an actual error) causes it to abort. If so, we can swallow the event in the first case, and only actually dispatch it when an unexpected error has occurred...

Let me check on that and see what info the WebView2 interfaces can provide...

2jfw commented 2 years ago

Thank you, much appreciated, Andrew!

ajwfrost commented 2 years ago

Okay so it seems there's a navigation ID that we can track on the webview, and we can reset that whenever a user-requested operation has taken place such as stop/reload/loadUrl etc. If the navigation ID is still in place at the point where we get the 'complete' handlers, we know that any error is as a result of the actual navigation failure rather than caused by the deliberate calls into the APIs.

So this seems to have removed those error events whilst leaving the normal events in place during successful operation...