MicrosoftEdge / WebView2Feedback

Feedback and discussions about Microsoft Edge WebView2
https://aka.ms/webview2
451 stars 55 forks source link

How to inject custom javascript to new windows requested by window.open? #2491

Closed ztanczos closed 7 months ago

ztanczos commented 2 years ago

Hi,

I'm trying to ensure that custom JavaScript is always injected to new windows which are requested by e.g.: window.open. For this I'm subscribing to NewWindowRequested event handler the following way:

        public async Task InitializeAsync()
        {
            await webView.EnsureCoreWebView2Async();
            webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
        }

        private async void CoreWebView2_NewWindowRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NewWindowRequestedEventArgs e)
        {
            var deferral = e.GetDeferral();
            Window window = new Window();
            var newWebView = new WebView2();
            window.Content = newWebView;
            window.Show();
            await newWebView.EnsureCoreWebView2Async();
            await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("alert('hello')");

            e.NewWindow = newWebView.CoreWebView2;
            e.Handled = true;
            deferral.Complete();
        }

What I'm experiencing is that the new window pops up fine but the custom script is never executed.

How can I ensure that custom JavaScript is always executed when a new window is requested by window.open before the window object is returned to the JavaScript caller?

Thank you, Zoltan

AB#43367417

vbryh-msft commented 2 years ago

Hi @ztanczos , in order for JS injection to take effect in a new window, it should be called after e.NewWindow = newWebView.CoreWebView2;. Here is some doc.

psmulovics commented 2 years ago

Maybe it's worth revising the doc, as it currently says:

Changes to settings should be made before put_NewWindow is called to ensure that those settings take effect for the newly setup WebView.

Which would imply that the setting for AddScriptToExecuteOnDocumentCreatedAsync should be made before you set NewWindow.

vbryh-msft commented 2 years ago

@psmulovics, sorry for confusion. We have further The methods which should affect the new web contents like AddScriptToExecuteOnDocumentCreated and add_WebResourceRequested have to be called after setting NewWindow. - we will see how to make it more clear.

psmulovics commented 2 years ago

Not related to the documentation, rather to make it more foolproof - could it give a warning in debug if you set it at the wrong time?

pontusn commented 2 years ago

Most interesting information, this enabled me to remove an ugly workaround where our integration actually had to reload ininitially loaded content for window.open to make all hooks and injected scripts work.

ztanczos commented 2 years ago

Hi @vbryh-msft, Thank you for your answer but unfortunately it still doesn't work for me, at least not reliably. This is the full source code for the NewWindowRequested event handler:

        private async void CoreWebView2_NewWindowRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NewWindowRequestedEventArgs e)
        {
            try
            {
                var deferral = e.GetDeferral();
                Window window = new Window();
                var newWebView = new WebView2();

                window.Content = newWebView;
                window.Show();

                await newWebView.EnsureCoreWebView2Async();
                e.NewWindow = newWebView.CoreWebView2;

                await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("alert('hello')");
                e.Handled = true;
                deferral.Complete();
            }
            catch (Exception ex)
            {

            }
        }

and this is the code which triggers the new window request:

<button onclick="window.open('https://www.google.com')">click me</button>

With this event handler most of the time the popup window is just blank (i.e.: not even google.com is displayed), but sometimes - mostly when I put a breakpoint at AddScriptToExecuteOnDocumentCreatedAsync I see the page properly displayed and my 'hello' alert message as well.

Also if I put a Thread.Sleep(3000) before AddScriptToExecuteOnDocumentCreatedAsync it starts to work more often.

Am I doing something wrong or there's a synchronization issue in WV2?

psmulovics commented 2 years ago

tagging @liminzhu for tracking

pontusn commented 2 years ago

For our Win32 integration I avoid extensive processing in the event handler. Instead I get the deferral and use PostMessage to create window and integrate with new WebView2 instance from the message pump.

ztanczos commented 2 years ago

Hi @pontusn, I'm not sure how would that be possible with the C# .NET interface: my understanding is that I'd need to set eventArgs.NewWindow to the new CoreWebView2 instance in the event handler and creating that instance can only be done asynchronously and it is already a relatively heavy operation.

Hi @vbryh-msft, Just wondering if you had a chance to check my last message.

pontusn commented 2 years ago

Use the deferral and avoid lengthly processing in the event handler.

vbryh-msft commented 2 years ago

Hi @ztanczos - I was able to repro the issue in .Net, but was unable to quickly solve it - created a bug for somebody to have more time to look into it.

vbryh-msft commented 2 years ago

@ztanczos - I have tried your sample one more time - do you need to have alert in AddScriptToExecuteOnDocumentCreatedAsync? Could you try console.log instead and see how it behaves for you?

ztanczos commented 2 years ago

@vbryh-msft - I don't need alert, let me try to explain what I'm looking for: we have custom Javascript libraries which provide all sorts of functionalities for the hosted web applications. These libraries should be already present when loading a page. This works fine for the 'main' window but if a web application tries to open a new window by using window.open we'd like to intercept that call and inject the same set of JS libs before the window object is available to the caller.

Consider this sample HTML:

<html>
<head>
<title>test</title>
<script type="text/javascript">
    var openedWindow;

    console.log(window['TESTDATA'])

    function openWindow() {
      openedWindow = window.open('https://www.google.com')
      console.log('window opened')
      console.log(openedWindow['TESTDATA'])
    }
</script>
</head>
<body>
<h1>hello world</h1>
<button onclick="openWindow()">click me</button>
</body>
</html>

And in .NET I'm trying to inject the following JS:

        public async Task InitializeAsync()
        {
            await webView.EnsureCoreWebView2Async();
            webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
            await webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window['TESTDATA'] = 'this works'");
        }
        private async void CoreWebView2_NewWindowRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NewWindowRequestedEventArgs e)
        {
            try
            {
                _deferral = e.GetDeferral();
                Window window = new Window();
                var newWebView = new WebView2();

                window.Content = newWebView;
                window.Show();

                await newWebView.EnsureCoreWebView2Async();
                e.NewWindow = newWebView.CoreWebView2;

                await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window['TESTDATA'] = 'this doesn't work'");
                e.Handled = true;
                _deferral.Complete();
            }
            catch (Exception ex)
            {
                Debug.Fail(ex.ToString());
            }
        }

When I click on the button undefined is logged to the console instead of this doesn't work.

Not sure if this makes sense, let me know if you have further questions. Thank you

vbryh-msft commented 2 years ago

@ztanczos There are could be two issues with your example:

  1. Apostrophe in doesn't causes Uncaught SyntaxError: Unexpected identifier - could you try without it, like just does
  2. Parent window from where you are trying to access console.log(openedWindow['TESTDATA']) matters - could you try to open google.com from google.com? Hope it makes sense. Or just check window['TESTDATA'] in DevTools(right-click -> Inspect) of the new window? Screenshot_doeswork
ztanczos commented 2 years ago

@vbryh-msft thanks, I didn't notice the apostrophe.. 🤦 I changed the test URLs so there's no cross-origin issue and now I see the same behavior, i.e.: undefined is logged when the window is opened but later I see TESTDATA populated. I'll check whether this behavior would work for us.

ztanczos commented 2 years ago

What exactly is the purpose of calling deferral.Complete()? I commented out that line so that I never call .Complete() and I don't see any change in behavior.

vbryh-msft commented 2 years ago

Hi @ztanczos - you should use GetDeferral if you want to defer event completion at a later time. [Here is an example](https://github.com/MicrosoftEdge/WebView2Samples/blob/main/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs#:~:text=CoreWebView2Deferral%20deferral%20%3D,.Complete()%3B) how it can be used - you can search for more examples in that file. We suggest to always complete deferral if it was taken in order for event to be in proper state and be able to complete successfully.

[This is](https://github.com/MicrosoftEdge/WebView2Samples/blob/main/SampleApps/WebView2APISample/AppWindow.cpp#:~:text=wil%3A%3Acom_ptr%3CICoreWebView2Deferral,deferral%2D%3EComplete()) how you would use deferral particularly in new window requested(c++ code)

Is injecting JS script works in new window as expected?

ztanczos commented 2 years ago

What I observe when using the Deferral in the NewWindowRequested event handler is that once I set .NewWindow then completing or not-completing the deferral doesn't matter: once .NewWindow is set the Javascript window.open call returns with a navigated window object.

Injecting JS works although not as expected. What we're trying to achieve is to inject custom Javascript into the new window before the window object is returned to the caller. The reason for this is that we provide a large set of custom functionalities for hosted web applications via JS bindings which should be available to child windows as well. With the current behavior there's a timing issue: JS bindings will be available at some point but we have no control over when exactly, so code which relies on JS objects being available in child windows as soon as the window object is ready would break. Originally I thought we should use the Deferral to signal when .NewWindow is ready to be used but apparently it is not the case.

plantree1995 commented 2 years ago

Hi @ztanczos, I have tried some tests based on WPF_GettingStarted and it seems fine. My code is as follows:

public async Task InitializeAsync()
{
    await webView.EnsureCoreWebView2Async();
    webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
    //await webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window['TESTDATA'] = 'this works'");
}

private async void CoreWebView2_NewWindowRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NewWindowRequestedEventArgs e)
{
    var _deferral = e.GetDeferral();
    Window window = new Window();
    var newWebView = new WebView2();

    window.Content = newWebView;
    window.Show();

    await newWebView.EnsureCoreWebView2Async();
    e.NewWindow = newWebView.CoreWebView2;

    await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window['TESTDATA'] = 'this does work'; alert('hello')");
    await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("function square(x) { return x * x; }");

    e.Handled = true;
    _deferral.Complete();
}

You will get the alert and the function works.

ztanczos commented 2 years ago

Hi @plantree1995 , Your code sample doesn't work for me: I don't see any alert, in fact the new window opens as completely blank.

psmulovics commented 2 years ago

Hi @plantree1995 , Your code sample doesn't work for me: I don't see any alert, in fact the new window opens as completely blank.

Same for me.

plantree1995 commented 2 years ago

Hi @ztanczos @psmulovics, I followed this project, and all source code is posted below:

namespace WPFSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            webView.NavigationStarting += EnsureHttps;
            InitializeAsync();

        }

        async void InitializeAsync()
        {
            await webView.EnsureCoreWebView2Async(null);
            webView.CoreWebView2.WebMessageReceived += UpdateAddressBar;
            webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;

            await webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.chrome.webview.postMessage(window.document.URL);");
            await webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.chrome.webview.addEventListener(\'message\', event => alert(event.data));");

        }

        void UpdateAddressBar(object sender, CoreWebView2WebMessageReceivedEventArgs args)
        {
            String uri = args.TryGetWebMessageAsString();
            addressBar.Text = uri;
            webView.CoreWebView2.PostWebMessageAsString(uri);
        }

        private async void CoreWebView2_NewWindowRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NewWindowRequestedEventArgs e)
        {
            var _deferral = e.GetDeferral();
            Window window = new Window();
            var newWebView = new WebView2();

            window.Content = newWebView;
            window.Show();

            await newWebView.EnsureCoreWebView2Async();
            e.NewWindow = newWebView.CoreWebView2;

            await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window['TESTDATA'] = 'this does work'; alert('hello')");
            await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("function square(x) { return x * x; }");

            e.Handled = true;
            _deferral.Complete();
        }

        void EnsureHttps(object sender, CoreWebView2NavigationStartingEventArgs args)
        {
            String uri = args.Uri;
            if (!uri.StartsWith("https://"))
            {
                webView.CoreWebView2.ExecuteScriptAsync($"alert('{uri} is not safe, try an https link')");
                args.Cancel = true;
            }
        }

        private void ButtonGo_Click(object sender, RoutedEventArgs e)
        {
            if (webView != null && webView.CoreWebView2 != null)
            {
                webView.CoreWebView2.Navigate(addressBar.Text);
            }
        }

    }

}

Compile and run it in debug mode, open any links in new window, I would get the alert and function square(). If you have any other questions, please let me know.

ztanczos commented 2 years ago

Hi @plantree1995, This sample is slightly different from what we're trying to achieve. We're trying to open a new window using Javascript's window.open() this way:

<html>
<head>
<title>test</title>
<script type="text/javascript">
    var openedWindow;

    console.log(window['TESTDATA'])

    function openWindow() {
      openedWindow = window.open('http://mytesturl/wv2test.html')
      console.log(openedWindow['TESTDATA']);
    }
</script>
</head>
<body>
<h1>hello world</h1>
<button onclick="openWindow()">click me</button>
</body>
</html>

When I open this HTML with the sample code from WPF_GettingStarted I see the main page rendered correctly but when I click on the click me button I only get a blank window and no alert pop-up + undefined is logged to the console indicating that TESTDATA is not available on openedWindow.

plantree1995 commented 2 years ago

Hi @ztanczos, I tried this way.

      private async void CoreWebView2_NewWindowRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NewWindowRequestedEventArgs e)
      {
          try
          {
              var _deferral = e.GetDeferral();
              Window window = new Window();
              var newWebView = new WebView2();

              window.Content = newWebView;
              window.Show();

              await newWebView.EnsureCoreWebView2Async();
              e.NewWindow = newWebView.CoreWebView2;

              await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("function square(x) { return x * x; }; function cube(x) { return x * x * x; }");

              e.Handled = true;
              _deferral.Complete();
          }
          catch (Exception )
          {

          }
      }

and the html just like this:

<html>

<head>
  <title>test</title>
  <script type="text/javascript">
    var openedWindow;

    console.log(window['TESTDATA'])

    function openWindow() {
      openedWindow = window.open('http://bing.com', 'hello')
    }
  </script>
</head>

<body>
  <h1>hello world</h1>
  <button onclick="openWindow()">click me</button>
</body>

</html>

When you click on the button to open a new window, you could use function square() and function cube() in the Console.

ztanczos commented 2 years ago

Hi @plantree1995, Thanks for looking into this but I know that eventually the injected Javascript code will be there but I have no control over when and I'd need to ensure that the returned Window object already has the injected JS functionality right after the window.open() call.

Our use-case: we're hosting web applications in our container which provides rich functionality for these web applications via JS + interop objects. This functionality is required in windows opened by these web applications too without timing issues. In the past we could achieve the same with CEF + CefSharp by using V8Extensions but now we're struggling with WebView2.

plantree1995 commented 2 years ago

Hi @ztanczos, Do you mean that you want to get the attribute of child window' JS functionality in the parent window?

ztanczos commented 2 years ago

The other way around. E.g.: let's say we inject something into the parent window so that window.MyCustomCode is accessible & works. Right after openedWindow = window.open(...) I'd like to ensure that openedWindow.MyCustomCode also accessible & works. In reality it is a bit more complex than that (multiple JS libraries with multiple properties, classes, etc.) but this is the gist of it.

plantree1995 commented 2 years ago

Hi @ztanczos, I almost understand what you want. It seems that you can read this docs to known more about what effects will Window.open() have.

Note that remote URLs won't load immediately. When window.open() returns, the window always contains about:blank. The actual fetching of the URL is deferred and starts after the current script block finishes executing. The window creation and the loading of the referenced resource are done asynchronously

It's not synchronous, so the output undefined is as expected.

ztanczos commented 2 years ago

Hi @plantree1995, Thanks for the link + explanation - I'll re-work my test code to better illustrate understand the problem we're facing: once the web app opens a child window then within the child window - so once it is loaded - the injected JS is not yet available.

plantree1995 commented 2 years ago

Maybe we can better illustrate this issue: the function of open returns although the resource has not yet been loaded completely. So perhaps you can use in this way:

function openWindow() {
  openedWindow = await window.open('http://127.0.0.1:8010/foo.html');
  openedWindow.onload = function () {
    console.log(openedWindow['TESTDATA']);
  };
}
plantree1995 commented 2 years ago

Hi @ztanczos, have you already solved this problem? If so, I will close this issue.

ztanczos commented 2 years ago

Hi @plantree1995, Sorry for the late response, I was on holiday. I'll still need a few days, please don't close this issue yet.

plantree1995 commented 2 years ago

Hi @ztanczos, If you come cross any problems, please let me know. Perhaps we could tackle them together.

ztanczos commented 2 years ago

Hi @plantree1995, Please bear with me, our actual problem spans across multiple applications and libraries, some of which are open-source, some are not so it is difficult to describe the problem without sharing non-public information.

With Cef + CefSharp we "injected" (or rather registered) the custom Javascript libraries by using V8Extensions (CefSettings.RegisterExtension() method). That way the custom Javascript is always there, even for newly opened windows, immediately after calling window.open (so it is never undefined).

Our framework calls window.open through desktopJS: https://github.com/morganstanley/desktopJS/blob/main/packages/desktopjs/src/Default/default.ts#L299 - we might be able to tweak that for our needs.

You wrote that injecting JS is not synchronous - what would be your suggestion to check when exactly a custom JS script is injected & ready to be consumed?

plantree1995 commented 2 years ago

Hi @ztanczos, follow this link, Window.open(), we could know that:

The window creation and the loading of the referenced resource are done asynchronously.

In a previous issue, I have this suggestion.

Maybe we can better illustrate this issue: the function of open returns although the resource has not yet been loaded completely. So perhaps you can use in this way:

function openWindow() {
  openedWindow = window.open('http://127.0.0.1:8010/foo.html'); // remove the await
  openedWindow.onload = function () {
    console.log(openedWindow['TESTDATA']);
  };
}

I don't know have you ever tried in this way. You can use your custom JS scripts freely after the load event is fired. The reference is below: https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event#syntax .

ibebbs commented 1 year ago

Hi @ztanczos / @vbryh-msft / @plantree1995 ,

I also believe there is a race condition between setting CoreWebView2NewWindowRequestedEventArgs.NewWindow and having scripts loaded (via CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync) prior to navigation / document loading.

Based on the above, I have put together a full reproduction of the issue in this repo along with steps for (and recordings of) expected and actual behavior.

I would very much appreciate if people could validate this issue and/or provide a work around.

FWIW, my hunch is that WebView2 is scheduling a navigation to the destination URL on the UI thread as soon as CoreWebView2NewWindowRequestedEventArgs.NewWindow is assigned. This causes the navigation to take place as soon as the currently executing code yields the UI thread (via a Task.Delay or - I expect - somewhere in CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync), potentially before the scripts have been added. If this is the case, a simple fix for this issue would be to wait for the deferral to be completed prior to scheduling the navigation.

ibebbs commented 1 year ago

I have pushed a workaround for this issue to this branch.

Short answer: Use CoreWebView2.NavigationStarting to cancel any navigations that occur prior to CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync completing then schedule the desired navigation manually.

Long answer: The issue was that setting CoreWebView2NewWindowRequestedEventArgs.NewWindow caused WebView2 to immediately (regardless of whether a deferral was held or not) schedule a navigation to CoreWebView2NewWindowRequestedEventArgs.Uri on the UI thread. Subsequent calls to CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync yielded the UI thread establishing a race-condition condition between the navigation and the script loading (hence why there were different outcomes based on timing / CPU load / etc).

Regarding a "fix" for WebView2 to be discussed with MS, I would suggest:

  1. Critial: Setting NewWindow should not schedule a navigation until any held deferral has been completed.
  2. Nice to have: CoreWebView2NewWindowRequested should provide a Navigate boolean which allows you to instruct WebView2 whether or not to schedule a navigation.
ztanczos commented 1 year ago

Hi @ibebbs, Our use-case is a bit special because we're in fact opening a new blank window, i.e.: about:blank, and once opened we're directly manipulating and projecting DOM elements to the new window.

Because a blank window is special we ended up using ExecuteScriptAsync instead of AddScriptToExecuteOnDocumentCreatedAsync and also we triggered an "extra" navigate on the newly opened window and then we're essentially waiting for the injected scripts / interop objects to become available on the Javascript side. We're using desktopJS and here instead of returning newWindow we return a Promise.

This works although we'd really appreciate a more straightforward way of achieving the same.

vbryh-msft commented 1 year ago

Hi @ibebbs - thank you for providing the repro repo. I have checked it - it should work if you will remove alert from the script and put console.log instead. Could you please try that? Do you really need to have alert in script or it was just for demo purpose? As mentioned before in general flow with adding script for new window should work unless you have some corner case also - then we would like to learn about what are you trying to achieve.

RendijsSmukulis commented 1 year ago

@vbryh-msft I've just tried @ibebbs repro with alerts replaced with console.logs, with the same outcome 🤔

vbryh-msft commented 1 year ago

@RendijsSmukulis could you please check this comment https://github.com/MicrosoftEdge/WebView2Feedback/issues/2491#issuecomment-1211604701 - it summarizes the issue, thanks.

vbryh-msft commented 1 year ago

@RendijsSmukulis @ibebbs also to reiterate - this is correct order of calls, which should work, do not set NewWindow after adding the script:

    window.Content = newWebView;
    window.Show();

    await newWebView.EnsureCoreWebView2Async();
    e.NewWindow = newWebView.CoreWebView2;

    await newWebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("console.log('hello')");
    e.Handled = true;
    _deferral.Complete();
ibebbs commented 1 year ago

Thanks for the looking into this @vbryh-msft.

I do not believe the use of alert is the issue here. While using console.log may have appeared to resolve the issue for you, as demonstrated by @RendijsSmukulis, it does not work reliably due to what appears to be an underlying race condition (navigation occuring as soon as the UI thread is relinquished).

To demonstrate this I have added logging to the master branch of the reproduction repository and changed the script to use console.log rather than alert per your suggestion.

If you run this app, ensure "Set NewWindow" is checked and "Set Source" is not, then click the "Open Window From Target..." link, you we see the following log:

[02:56:41.3058] Assigning NewWindow
[02:56:41.3134] Assigned NewWindow
[02:56:41.3135] Start Loading Scripts
[02:56:41.3623] NavigationStarting - 'about:blank'
[02:56:41.6923] ContentLoading - 'https://ian.bebbs.co.uk/'
[02:56:41.6984] Completed Loading Scripts

As you can see, content loading has been started prior to script loading being complete. This is the core of the issue. However, this is exacerbated by anything that might cause the UI thread to be relinquished. For example, if you clear the log and set "Delay" to "2000" then click the "Open Window From Target..." link again, you will see the following log:

[03:08:37.2107] Assigning NewWindow
[03:08:37.2179] Assigned NewWindow
[03:08:37.2816] NavigationStarting - 'about:blank'
[03:08:37.5168] ContentLoading - 'https://ian.bebbs.co.uk/'
[03:08:39.2501] Start Loading Scripts
[03:08:39.2747] Completed Loading Scripts

As you can see, content has loaded before the scripts have even started loading.

I believe this demonstrates the core of the issue and why navigation should not be scheduled prior to the deferral being completed.

Thanks again for you help here.

plantree commented 1 year ago

Hi @ibebbs, my current conclusion is similar to yours: the sequence of Navigation arising from SetNewWindow and AddScriptToExecuteOnDocumentCreatedAsync could not be guaranteed.

We have put together a workaround by using PostMessage() and a second navigation to keep the order of execution.

Your workaround is very interesting and suggestions about fix seem to work.

We've documented it internally and are working on it.

Thanks for you suggesions so much.

ibebbs commented 1 year ago

No problem @plantree, glad to be of help.

Could you provide more information about your PostMessage() workaround? The workaround we're currently employing - cancelling navigations until script loading is complete - has a serious issue (navigation via form POST no longer works correctly) and we will need to find a better solution if MS are not able to provide a fix for this issue in the short-term.

Thanks!

plantree commented 1 year ago

Hi @ibebbs,

I prepared a demo project to show the workaround, which could be found here. If you have any questions, please let me know.

Besides, your suggestions are very helpful, and we are working on solving the root cause.

Thanks for you advice.

ibebbs commented 1 year ago

Thanks for the demo project @plantree

I see what you're doing and how it might be a viable workaround for some. Unfortunately I don't think we'll be able to use this approach as:

  1. We display external web content so can't (easily) ensure clicking a link would invoke the openWindow function; and
  2. It doesn't look like it'll resolve the issue with navigation via form POST.

Do you have an ETA on when a fix for the root cause might be available?

Thanks again for all your help.

plantree commented 1 year ago

Hi @ibebbs,

We do not have ETA currently, but I'll synchronize with you when we have.

As for you question, if it is possible, could you provide a demo project that we could try to solve together?

ibebbs commented 1 year ago

Sorry for the delay in getting back to you @plantree.

Unfortunately we don't have a demo project but the repo provided above should be sufficient to address the issue.

Thanks!

plantree commented 1 year ago

Hi @ibebbs,

Thanks for the demo project you provide. I'll try to repoduce your issue locally and synchronize with you later.