Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Problem/Bug]: A window hierarchy problem introduced by WebView2 #4787

Closed
zprettytext opened this issue Sep 4, 2024 · 2 comments
Closed
Assignees
Labels
bug Something isn't working

Comments

@zprettytext
Copy link

zprettytext commented Sep 4, 2024

What happened?

background

The background of the problem is this: we have two processes A and B that reuse the same WebView2 Run Environment. The process model is as follows:
image

Then we found the following problems:

  1. Call SetWindowPos putting a pop-up window on top of A will cause B's main window switch to the foreground.
  2. Call SetWindowPos putting a pop-up window on top of B will also cause A's main window switch to the foreground.

Cause analysis:

This problem only occurs when WebView is loaded in both A and B.
We speculate that it is probably because when WebView is loaded in both A and B, A and B contain child windows (WebView window handles) belonging to the same process (Microsoft Edge WebView2), which leads to a problem that the Z order of A and B windows is mixed together.

conclusion

We need A and B to share the same WebView permission environment so that WebView can share cache data. However, we don't want to cause window Z order problems due to sharing the same Microsoft Edge WebView2 process. We want a perfect solution.

Importance

Important. My app's user experience is significantly compromised.

Runtime Channel

Stable release (WebView2 Runtime)

Runtime Version

122.0.2365.80

SDK Version

1.0221

Framework

Win32

Operating System

Windows 10, Windows 11

OS Version

No response

Repro steps

Please refer to the above description.

Repros in Edge Browser

No, issue does not reproduce in the corresponding Edge version

Regression

No, this never worked

Last working version (if regression)

No response

@zprettytext zprettytext added the bug Something isn't working label Sep 4, 2024
@zprettytext
Copy link
Author

To prove what I said, I implemented a unit test. The steps are as follows:
step 1. Create a unit test window and load a WebView.
step 2. Open the Dev Tool window.
step 3. Call the timer in the Dev Tool console, delay 10s to execute chrome.webview.postMessage('[CreatePopWindow]') to send a message to the WebView.
setTimeout(function() {
chrome.webview.postMessage('[pop]')
}, 10000);

step 4. Switch the unit test window to the background.
step 5. After the unit test WebView receives the Web message, the window pops up and calls SetWindowPos to set the window.
step 6. There are two situations:
6.1. If the Dev Tool window is in the foreground, the unit test window will be switched to the foreground, which is not the expected behavior.
6.2. If the Dev Tool window is not in the foreground, the unit test window will not be switched to the foreground, which is in line with the expected behavior.
Now the key question is: why does the Dev Tool window in the foreground cause the unit test window to be picked to the foreground?

After unit test window webview receives the message, the window pop-up window related code is as follows

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        switch (uMsg) {
        case WM_DESTROY:
            return 0;
        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
    }

    void RegisterWindowClass() {
        WNDCLASSEX wc = { 0 };
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WindowProc;
        wc.hInstance = nullptr;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszClassName = TEXT("MyPopupWindowClass");

        RegisterClassEx(&wc);
    }

    void CreatePopupWindow(HWND ancestorWindow) {
        RegisterWindowClass();
        HWND hWnd = CreateWindowEx(
            0,
            TEXT("MyPopupWindowClass"),     
            TEXT("My Popup Window"),          
            WS_POPUP | WS_VISIBLE | WS_OVERLAPPEDWINDOW,           
            CW_USEDEFAULT, CW_USEDEFAULT,    
            300, 200,                        
            ancestorWindow,                 
            NULL,                            
            NULL,                            
            NULL
        );

        if (hWnd) {
            SetWindowPos(hWnd, HWND_TOP, 0, 0, 300, 200, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
            ShowWindow(hWnd, SW_SHOW);
        }
    }

 EXPECT_CALL(sinkWin, OnRecvWebMessage(testing::_, testing::_, testing::_))
        .Times(::testing::AnyNumber())
        .WillRepeatedly(::testing::Invoke([webViewControl](IUnifyWebViewInst* inst, const Cmm::CString& message, bool& handled) {

            if (L"[pop]" == message)
            {
                HWND ancestorWindow = ::GetAncestor(webViewControl->GetWebViewWindow(), GA_ROOT);
                CreatePopupWindow(ancestorWindow);
            }
         }));

The screen recording information is as follows:

  1. The first time, the unit test window jumped to the foreground because the Dev Tool window was in the foreground (not the expected behavior).
  2. The second time, the Dev Tool window was not in the foreground.
    The conclusion is that whether the unit test window is in the foreground affects the behavior of the unit test window.
    gifrec_20240905-18_32_02

@bradp0721
Copy link
Member

@zprettytext What you're describing makes sense. When both process A and process B share the same WebView2 Environment settings, they end up sharing the same msedgewebview2.exe process. Child HWNDs across a thread or process boundary results in attached input queues. So having process A and process B share a WebView2 process, results in all 3 processes now having attached input queues. This gives all processes foreground permission to change the active window.

I believe that you can resolve the issue above by adding SWP_NOOWNERZORDER .-,SWP_NOOWNERZORDER,-0x0200) to your SetWindowPos flags. This should prevent the owner window from changing z-order when you change the owned popup window to be HWND_TOP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants