fix: Implement robust cross-platform window visibility fallback for issue #2861

Addresses the issue where application windows fail to show on Windows 10 Pro
due to efficiency mode preventing WebView2 NavigationCompleted events.

## Changes Made

### Windows (webview_window_windows.go)
- **Decouple window container from WebView state**: Window now shows immediately
- **Add timeout fallback**: 3-second timeout to show WebView if navigation is delayed
- **Prevent efficiency mode**: Set WebView2 IsVisible=true per Microsoft recommendation
- **Enhanced state tracking**: Added showRequested, visibilityTimeout, windowShown fields
- **Robust navigation completion**: Improved handler to work with new visibility logic

### macOS (webview_window_darwin.go)
- **Documentation**: Added comment noting macOS already follows best practices
- **No functional changes**: macOS implementation already robust

### Linux (webview_window_linux.go, linux_cgo.go, linux_purego.go)
- **Add missing methods**: Implemented show()/hide() methods in main Linux file
- **CGO implementation**: Added windowShow()/windowHide() delegation methods
- **Purego implementation**: Added windowShow()/windowHide() methods for purego builds
- **Consistent behavior**: Matches CGO implementation with position saving

## Implementation Pattern

Adopts the following pattern:
1. **Separate concerns**: Window container vs WebView content readiness
2. **Immediate visibility**: Show window container immediately
3. **Progressive enhancement**: Show WebView content when ready
4. **Robust fallbacks**: Timeout and multiple strategies for edge cases

## Testing Considerations

- Windows 10 Pro efficiency mode scenarios
- WebView2 navigation delays or failures
- Cross-platform consistency
- Performance impact of timeout mechanisms

Fixes #2861

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2025-06-12 23:03:34 +10:00
commit d03a63e1b7
15 changed files with 1370 additions and 5 deletions

View file

@ -82,6 +82,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-  Add File Association support for mac by [@wimaha](https://github.com/wimaha) in [#4177](https://github.com/wailsapp/wails/pull/4177)
- Add `wails3 tool version` for semantic version bumping by [@leaanthony](https://github.com/leaanthony)
- Add badging support for macOS and Windows by [@popaprozac](https://github.com/popaprozac) in [#](https://github.com/wailsapp/wails/pull/4234)
### Fixed
- Fixed Windows+Linux Edit Menu issues by [@leaanthony](https://github.com/leaanthony) in [#3f78a3a](https://github.com/wailsapp/wails/commit/3f78a3a8ce7837e8b32242c8edbbed431c68c062)
@ -127,6 +128,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed system trays not showing after taskbar restarts by [@leaanthony](https://github.com/leaanthony) based on work by @kron.
- Fixed fallbackResponseWriter not implementing Flush() in [#4245](https://github.com/wailsapp/wails/pull/4245)
- Fixed fallbackResponseWriter not implementing Flush() by [@superDingda] in [#4236](https://github.com/wailsapp/wails/issues/4236)
- Fixed Windows Efficiency mode startup race condition by [@leaanthony](https://github.com/leaanthony)
### Changed

View file

@ -18,7 +18,7 @@ require (
github.com/google/uuid v1.6.0
github.com/goreleaser/nfpm/v2 v2.41.3
github.com/jackmordaunt/icns/v2 v2.2.7
github.com/jaypipes/ghw v0.13.0
github.com/jaypipes/ghw v0.17.0
github.com/leaanthony/clir v1.7.0
github.com/leaanthony/go-ansi-parser v1.6.1
github.com/leaanthony/gosod v1.0.4

View file

@ -174,8 +174,8 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/jackmordaunt/icns/v2 v2.2.7 h1:K/RbfvuzjmjVY5y4g+XENRs8ZZatwz4YnLHypa2KwQg=
github.com/jackmordaunt/icns/v2 v2.2.7/go.mod h1:ovoTxGguSuoUGKMk5Nn3R7L7BgMQkylsO+bblBuI22A=
github.com/jaypipes/ghw v0.13.0 h1:log8MXuB8hzTNnSktqpXMHc0c/2k/WgjOMSUtnI1RV4=
github.com/jaypipes/ghw v0.13.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
github.com/jaypipes/ghw v0.17.0 h1:EVLJeNcy5z6GK/Lqby0EhBpynZo+ayl8iJWY0kbEUJA=
github.com/jaypipes/ghw v0.17.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic=
github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGFXMCeE4=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=

View file

@ -1009,6 +1009,10 @@ func (w *linuxWebviewWindow) hide() {
C.gtk_widget_hide(w.gtkWidget())
}
func (w *linuxWebviewWindow) windowHide() {
C.gtk_widget_hide(w.gtkWidget())
}
func (w *linuxWebviewWindow) isFullscreen() bool {
gdkWindow := C.gtk_widget_get_window(w.gtkWidget())
state := C.gdk_window_get_state(gdkWindow)
@ -1123,6 +1127,13 @@ func (w *linuxWebviewWindow) show() {
//w.setPosition(w.lastX, w.lastY)
}
func (w *linuxWebviewWindow) windowShow() {
if w.gtkWidget() == nil {
return
}
C.gtk_widget_show_all(w.gtkWidget())
}
func windowIgnoreMouseEvents(window pointer, webview pointer, ignore bool) {
var enable C.int
if ignore {

View file

@ -1206,3 +1206,18 @@ func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) {
func isOnMainThread() bool {
return mainThreadId == gThreadSelf()
}
// linuxWebviewWindow show/hide methods for purego implementation
func (w *linuxWebviewWindow) windowShow() {
if w.window == 0 {
return
}
windowShow(w.window)
}
func (w *linuxWebviewWindow) windowHide() {
if w.window == 0 {
return
}
windowHide(w.window)
}

View file

@ -903,6 +903,8 @@ func (w *macosWebviewWindow) getScreen() (*Screen, error) {
}
func (w *macosWebviewWindow) show() {
// macOS implementation is already robust - window container shows immediately
// This is the preferred pattern that Windows should follow
C.windowShow(w.nsWindow)
}

View file

@ -412,6 +412,18 @@ func (w *linuxWebviewWindow) setIgnoreMouseEvents(ignore bool) {
w.ignoreMouse(w.ignoreMouseEvents)
}
func (w *linuxWebviewWindow) show() {
// Linux implementation is robust - window shows immediately
// This is the preferred pattern that Windows should follow
w.windowShow()
}
func (w *linuxWebviewWindow) hide() {
// Save position before hiding (consistent with CGO implementation)
w.lastX, w.lastY = w.position()
w.windowHide()
}
func (w *linuxWebviewWindow) showMenuBar() {}
func (w *linuxWebviewWindow) hideMenuBar() {}
func (w *linuxWebviewWindow) toggleMenuBar() {}

View file

@ -57,6 +57,11 @@ type windowsWebviewWindow struct {
chromium *edge.Chromium
webviewNavigationCompleted bool
// Window visibility management - robust fallback for issue #2861
showRequested bool // Track if show() was called before navigation completed
visibilityTimeout *time.Timer // Timeout to show window if navigation is delayed
windowShown bool // Track if window container has been shown
// resizeBorder* is the width/height of the resize border in pixels.
resizeBorderWidth int32
resizeBorderHeight int32
@ -960,14 +965,45 @@ func (w *windowsWebviewWindow) printStyle() {
}
func (w *windowsWebviewWindow) show() {
// Always show the window container immediately (decouple from WebView state)
// This fixes issue #2861 where efficiency mode prevents window visibility
w32.ShowWindow(w.hwnd, w32.SW_SHOW)
w.windowShown = true
w.showRequested = true
// Show WebView if navigation has completed
if w.webviewNavigationCompleted {
w.chromium.Show()
w32.ShowWindow(w.hwnd, w32.SW_SHOW)
// Cancel timeout since we can show immediately
if w.visibilityTimeout != nil {
w.visibilityTimeout.Stop()
w.visibilityTimeout = nil
}
} else {
// Start timeout to show WebView if navigation is delayed (fallback for efficiency mode)
if w.visibilityTimeout == nil {
w.visibilityTimeout = time.AfterFunc(3*time.Second, func() {
// Show WebView even if navigation hasn't completed
// This prevents permanent invisibility in efficiency mode
if !w.webviewNavigationCompleted && w.chromium != nil {
w.chromium.Show()
}
w.visibilityTimeout = nil
})
}
}
}
func (w *windowsWebviewWindow) hide() {
w32.ShowWindow(w.hwnd, w32.SW_HIDE)
w.windowShown = false
w.showRequested = false
// Cancel any pending visibility timeout
if w.visibilityTimeout != nil {
w.visibilityTimeout.Stop()
w.visibilityTimeout = nil
}
}
// Get the screen for the current window
@ -990,6 +1026,10 @@ func newWindowImpl(parent *WebviewWindow) *windowsWebviewWindow {
parent: parent,
resizeBorderWidth: int32(w32.GetSystemMetrics(w32.SM_CXSIZEFRAME)),
resizeBorderHeight: int32(w32.GetSystemMetrics(w32.SM_CYSIZEFRAME)),
// Initialize visibility tracking fields
showRequested: false,
visibilityTimeout: nil,
windowShown: false,
}
return result
@ -1640,6 +1680,14 @@ func (w *windowsWebviewWindow) setupChromium() {
chromium.Embed(w.hwnd)
// Prevent efficiency mode by keeping WebView2 visible (fixes issue #2861)
// Microsoft recommendation: keep IsVisible = true to avoid efficiency mode
// See: https://github.com/MicrosoftEdge/WebView2Feedback/discussions/4021
err := chromium.PutIsVisible(true)
if err != nil {
globalApplication.error("Failed to set WebView2 visibility for efficiency mode prevention: %v", err)
}
if chromium.HasCapability(edge.SwipeNavigation) {
err := chromium.PutIsSwipeNavigationEnabled(opts.EnableSwipeGestures)
if err != nil {
@ -1837,6 +1885,12 @@ func (w *windowsWebviewWindow) navigationCompleted(sender *edge.ICoreWebView2, a
}
w.webviewNavigationCompleted = true
// Cancel any pending visibility timeout since navigation completed
if w.visibilityTimeout != nil {
w.visibilityTimeout.Stop()
w.visibilityTimeout = nil
}
wasFocused := w.isFocused()
// Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026
err := w.chromium.Hide()
@ -1850,8 +1904,13 @@ func (w *windowsWebviewWindow) navigationCompleted(sender *edge.ICoreWebView2, a
if wasFocused {
w.focus()
}
// Only call parent.Show() if not hidden and show was requested but window wasn't shown yet
// The new robust show() method handles window visibility independently
if !w.parent.options.Hidden {
w.parent.Show()
if w.showRequested && !w.windowShown {
w.parent.Show()
}
w.update()
}
}

View file

@ -0,0 +1,19 @@
# Compiled binary
window-visibility-test
window-visibility-test.exe
# Build artifacts
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binaries
*.test
# Output of the go coverage tool
*.out
# Go workspace file
go.work

View file

@ -0,0 +1,146 @@
# Window Visibility Test - Issue #2861
This example demonstrates and tests the fixes implemented for [Wails v3 Issue #2861](https://github.com/wailsapp/wails/issues/2861) regarding application windows not showing on Windows 10 Pro due to efficiency mode.
## Problem Background
On Windows systems, the "efficiency mode" feature could prevent Wails applications from displaying windows properly. This occurred because:
1. **WebView2 NavigationCompleted events** could be delayed or missed in efficiency mode
2. **Window visibility was gated** behind WebView2 navigation completion
3. **No fallback mechanisms** existed for delayed or failed navigation events
## Solution Implemented
The fix implements a **robust cross-platform window visibility pattern**:
### Windows Improvements
- ✅ **Decouple window container from WebView state** - Windows show immediately
- ✅ **3-second timeout fallback** - Shows WebView if navigation is delayed
- ✅ **Efficiency mode prevention** - Sets WebView2 `IsVisible=true` per Microsoft guidance
- ✅ **Enhanced state tracking** - Proper visibility state management
### Cross-Platform Consistency
- ✅ **macOS** - Already robust, documented best practices
- ✅ **Linux** - Added missing show/hide methods for both CGO and purego builds
## Test Scenarios
This example provides comprehensive testing for:
### 1. **Basic Window Tests**
- **Normal Window**: Standard window creation - should appear immediately
- **Delayed Content Window**: Simulates heavy content loading (like Vue.js apps)
- **Hidden → Show Test**: Tests delayed showing after initial creation
### 2. **Stress Tests**
- **Multiple Windows**: Creates 3 windows simultaneously
- **Rapid Creation**: Creates windows in quick succession
### 3. **Critical Issue #2861 Test**
- **Efficiency Mode Test**: Specifically designed to reproduce and verify the fix
- Tests window container vs content loading timing
- Includes heavy content simulation
## How to Run
```bash
cd /path/to/wails/v3/examples/window-visibility-test
wails dev
```
## Testing Instructions
### What to Look For
1. **Immediate Window Appearance** - Windows should appear within 100ms of clicking buttons
2. **Progressive Loading** - Content may load progressively, but window container visible immediately
3. **No Efficiency Mode Issues** - Windows appear even if Task Manager shows "efficiency mode"
4. **Consistent Cross-Platform Behavior** - Similar behavior on Windows, macOS, and Linux
### How to Test
1. **Note the current time** displayed in the app
2. **Click any test button** or use menu items
3. **Immediately observe** if a window appears (should be within 100ms)
4. **Wait for content** to load and check reported timing
5. **Try multiple tests** in sequence to test robustness
6. **Test both buttons and menu items** for comprehensive coverage
### Expected Results
- ✅ Window containers appear immediately upon button click
- ✅ Content loads progressively within 2-3 seconds
- ✅ No blank or invisible windows, even under efficiency mode
- ✅ Activity log shows sub-100ms window creation times
- ✅ All test scenarios work consistently
## Manual Testing Checklist
### Windows 10 Pro (Primary Target)
- [ ] Test with efficiency mode enabled in Task Manager
- [ ] Create windows while system is under load
- [ ] Test rapid window creation scenarios
- [ ] Verify WebView2 content loads after container appears
- [ ] Check activity log for sub-100ms creation times
### Windows 11
- [ ] Verify consistent behavior with Windows 10 Pro fixes
- [ ] Test efficiency mode scenarios
- [ ] Validate timeout fallback mechanisms
### macOS
- [ ] Confirm existing robust behavior maintained
- [ ] Test all window creation scenarios
- [ ] Verify no regressions introduced
### Linux
- [ ] Test both CGO and purego builds
- [ ] Verify new show/hide methods work correctly
- [ ] Test window positioning and timing
## Technical Implementation Details
### Window Creation Flow
```
1. User clicks button → JavaScript calls Go backend
2. Go creates WebviewWindow → Sets properties
3. Go calls window.Show() → IMMEDIATE window container display
4. WebView2 starts navigation → Progressive content loading
5. Timeout fallback ensures WebView shows even if navigation delayed
```
### Key Code Changes
- **Windows**: `/v3/pkg/application/webview_window_windows.go`
- **macOS**: `/v3/pkg/application/webview_window_darwin.go`
- **Linux**: `/v3/pkg/application/webview_window_linux.go`, `linux_cgo.go`, `linux_purego.go`
## Reporting Test Results
When testing, please report:
1. **Platform & OS Version** (e.g., "Windows 10 Pro 21H2", "macOS 13.1", "Ubuntu 22.04")
2. **Window Creation Timing** (from activity log)
3. **Any Delayed or Missing Windows**
4. **Efficiency Mode Status** (Windows only - check Task Manager)
5. **Content Loading Behavior** (immediate container vs progressive content)
6. **Any Error Messages** in activity log or console
### Sample Test Report Format
```
Platform: Windows 10 Pro 21H2
Efficiency Mode: Enabled
Results:
- Normal Window: ✅ Appeared immediately (<50ms)
- Delayed Content: ✅ Container immediate, content loaded in 2.1s
- Multiple Windows: ✅ All 3 appeared simultaneously
- Critical Test: ✅ Window appeared immediately, content progressive
Notes: No issues observed, all windows visible immediately
```
## Architecture Notes
This example demonstrates the **preferred window visibility pattern** for web-based desktop applications:
1. **Separate Concerns**: Window container vs web content readiness
2. **Immediate Feedback**: Users see window immediately
3. **Progressive Enhancement**: Content loads and appears when ready
4. **Robust Fallbacks**: Multiple strategies for edge cases
5. **Cross-Platform Consistency**: Same behavior on all platforms

View file

@ -0,0 +1,159 @@
# Testing Guide - Window Visibility Issue #2861
## Quick Start
1. **Build and run the application:**
```bash
cd v3/examples/window-visibility-test
./build.sh
# OR
wails dev
```
2. **Main testing interface:**
- The app opens with a comprehensive testing dashboard
- Contains multiple test scenarios accessible via buttons
- Also provides menu-based testing (File, Tests, Help menus)
- Real-time activity logging with precise timing
## Critical Test Cases
### 🎯 **Issue #2861 Reproduction Test** (Most Important)
**Button:** "Efficiency Mode Test"
**Expected:** Window container appears immediately, content loads progressively
**Watch for:**
- Window visible within 100ms of button click
- Content loading message appears initially
- Content completes loading after 2-3 seconds
- No blank or invisible windows
### ⏳ **Delayed Content Simulation**
**Button:** "Create Delayed Content Window"
**Expected:** Tests navigation completion timing
**Watch for:**
- Window container appears immediately
- Loading spinner visible initially
- Content loads after 3-second delay
- Window remains visible throughout
### 🔄 **Hidden → Show Robustness**
**Button:** "Hidden → Show Test"
**Expected:** Tests delayed show() calls
**Watch for:**
- Initial response in activity log
- Window appears after exactly 2 seconds
- No timing issues or failures
## Platform-Specific Testing
### Windows 10 Pro (Primary Target)
**Enable Efficiency Mode Testing:**
1. Open Task Manager → Processes tab
2. Find the test application process
3. Right-click → "Efficiency mode" (if available)
4. Run all test scenarios
5. Verify windows still appear immediately
**Key Metrics:**
- Window creation: < 100ms
- Content loading: 2-3 seconds
- No invisible windows under any conditions
### Windows 11
**Similar to Windows 10 Pro but also test:**
- New Windows 11 efficiency features
- Multiple monitor scenarios
- High DPI scaling
### macOS
**Focus on consistency:**
- All scenarios should work identical to Windows
- No regressions in existing robust behavior
- Test across different macOS versions if possible
### Linux
**Test both build variants:**
```bash
# CGO build (default)
wails dev
# Purego build
CGO_ENABLED=0 wails dev
```
- Verify both variants behave identically
- Test across different Linux distributions
## Success Criteria
### ✅ **Pass Conditions**
- All windows appear within 100ms of button click
- Activity log shows consistent sub-100ms timing
- Content loads progressively without blocking window visibility
- No blank, invisible, or delayed windows under any test scenario
- Efficiency mode (Windows) does not prevent window appearance
- Menu and button testing yield identical results
### ❌ **Fail Conditions**
- Any window takes >200ms to appear
- Blank or invisible windows under any condition
- Window visibility blocked by content loading
- Efficiency mode prevents window appearance
- Inconsistent behavior between test methods
- Platform-specific failures
## Reporting Results
**Please provide this information:**
```
Platform: [Windows 10 Pro/Windows 11/macOS/Linux distro + version]
Build Type: [CGO/Purego] (Linux only)
Efficiency Mode: [Enabled/Disabled/N/A] (Windows only)
Test Results:
- Normal Window: [✅ Pass / ❌ Fail] - [timing in ms]
- Delayed Content: [✅ Pass / ❌ Fail] - [container timing / content timing]
- Hidden→Show: [✅ Pass / ❌ Fail] - [notes]
- Multiple Windows: [✅ Pass / ❌ Fail] - [notes]
- Efficiency Mode Test: [✅ Pass / ❌ Fail] - [critical timing results]
Notes:
[Any additional observations, error messages, or unexpected behavior]
```
## Advanced Testing Scenarios
### **Rapid Stress Testing**
1. Click "Rapid Creation Test" multiple times quickly
2. Use keyboard shortcuts to rapidly access menu items
3. Create multiple windows then close them rapidly
4. Test system under load (other applications running)
### **Edge Case Testing**
1. Test during system startup (high load)
2. Test with multiple monitors
3. Test with different DPI scaling settings
4. Test while other WebView2 applications are running
### **Timing Verification**
1. Use browser dev tools (F12) to check console timing
2. Compare activity log timing with system clock
3. Test on slower/older hardware if available
4. Verify timing consistency across multiple runs
## Troubleshooting
### **Common Issues**
- **Blank window**: Check activity log for error messages
- **Slow timing**: Verify system isn't under heavy load
- **Build failures**: Ensure Wails v3 CLI is latest version
- **Import errors**: Run `go mod tidy` in example directory
### **Debug Information**
The application provides extensive logging:
- Browser console (F12) shows JavaScript timing
- Activity log shows backend call timing
- Go application logs show window creation details
- Check system Task Manager for process efficiency mode status
This comprehensive testing should validate that the window visibility fixes successfully resolve issue #2861 across all supported platforms.

View file

@ -0,0 +1,431 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Window Visibility Test - Issue #2861</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
padding: 20px;
overflow-x: hidden;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 40px;
background: rgba(255, 255, 255, 0.1);
padding: 30px;
border-radius: 15px;
backdrop-filter: blur(10px);
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.2em;
opacity: 0.9;
}
.test-section {
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 25px;
margin-bottom: 25px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.test-section h2 {
font-size: 1.5em;
margin-bottom: 15px;
color: #fff;
display: flex;
align-items: center;
gap: 10px;
}
.test-section p {
margin-bottom: 15px;
line-height: 1.6;
opacity: 0.9;
}
.button-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-top: 20px;
}
.test-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 10px;
padding: 15px 20px;
color: white;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid rgba(255, 255, 255, 0.3);
position: relative;
overflow: hidden;
}
.test-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.3);
border-color: rgba(255, 255, 255, 0.5);
}
.test-button:active {
transform: translateY(0);
}
.test-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
.test-button:hover::before {
left: 100%;
}
.status-display {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 15px;
margin-top: 20px;
border-left: 4px solid #4CAF50;
}
.status-display h3 {
margin-bottom: 10px;
color: #4CAF50;
}
.log-entry {
padding: 8px 12px;
margin: 5px 0;
background: rgba(255, 255, 255, 0.1);
border-radius: 6px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.critical-test {
border-left: 4px solid #ff6b6b;
}
.critical-test h2 {
color: #ff6b6b;
}
.info-box {
background: rgba(52, 152, 219, 0.2);
border: 1px solid rgba(52, 152, 219, 0.4);
border-radius: 8px;
padding: 15px;
margin: 20px 0;
}
.info-box h4 {
color: #3498db;
margin-bottom: 8px;
}
.timing-display {
text-align: center;
font-size: 1.1em;
margin: 20px 0;
padding: 15px;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.emoji {
font-size: 1.2em;
margin-right: 8px;
}
.menu-note {
background: rgba(255, 193, 7, 0.2);
border: 1px solid rgba(255, 193, 7, 0.4);
border-radius: 8px;
padding: 15px;
margin: 20px 0;
color: #fff;
}
.menu-note strong {
color: #ffc107;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🪟 Window Visibility Test</h1>
<p>Testing fixes for Wails v3 Issue #2861 - Windows 10 Pro Efficiency Mode</p>
<div class="timing-display">
<span id="current-time">Current Time: --:--:--</span>
</div>
</div>
<div class="test-section">
<h2><span class="emoji"></span>Basic Window Tests</h2>
<p>These tests verify that windows appear immediately when requested, regardless of WebView content loading state.</p>
<div class="button-grid">
<button class="test-button" onclick="createNormalWindow()">
Create Normal Window
</button>
<button class="test-button" onclick="createDelayedContentWindow()">
Create Delayed Content Window
</button>
<button class="test-button" onclick="createHiddenThenShowWindow()">
Hidden → Show Test
</button>
</div>
</div>
<div class="test-section">
<h2><span class="emoji"></span>Stress Tests</h2>
<p>These tests push the window system to verify robustness under load and timing edge cases.</p>
<div class="button-grid">
<button class="test-button" onclick="createMultipleWindows()">
Multiple Windows (3x)
</button>
<button class="test-button" onclick="rapidWindowCreation()">
Rapid Creation Test
</button>
</div>
</div>
<div class="test-section critical-test">
<h2><span class="emoji">🔬</span>Critical Issue #2861 Test</h2>
<p>This test specifically targets the Windows efficiency mode bug where WebView2 NavigationCompleted events could be delayed or missed.</p>
<div class="info-box">
<h4>Expected Behavior:</h4>
<ul>
<li>Window container appears <strong>immediately</strong> upon button click</li>
<li>Content loads progressively (may take 2-3 seconds)</li>
<li>No blank or invisible windows, even under efficiency mode</li>
</ul>
</div>
<div class="button-grid">
<button class="test-button" onclick="createEfficiencyModeTestWindow()">
🎯 Efficiency Mode Test
</button>
</div>
</div>
<div class="test-section">
<h2><span class="emoji">📊</span>Test Results & Timing</h2>
<p>Monitor window creation timing and behavior. Each button click should result in immediate window visibility.</p>
<div class="status-display">
<h3>Activity Log</h3>
<div id="activity-log">
<div class="log-entry">Ready for testing... Click any button to begin.</div>
</div>
</div>
</div>
<div class="test-section">
<h2><span class="emoji">📝</span>Testing Instructions</h2>
<div class="info-box">
<h4>What to Look For:</h4>
<ul>
<li><strong>Immediate Window Appearance:</strong> Windows should appear within 100ms of clicking</li>
<li><strong>Progressive Loading:</strong> Content may load progressively, but window container should be visible immediately</li>
<li><strong>No Efficiency Mode Issues:</strong> On Windows, windows should still appear even if Task Manager shows "efficiency mode"</li>
<li><strong>Consistent Behavior:</strong> All platforms (Windows, macOS, Linux) should behave similarly</li>
</ul>
</div>
<div class="info-box">
<h4>How to Test:</h4>
<ol>
<li>Note the current time displayed above</li>
<li>Click any test button</li>
<li>Immediately observe if a window appears (should be within 100ms)</li>
<li>Wait for content to load and check the reported timing</li>
<li>Try multiple tests in sequence</li>
<li>Test both buttons and menu items</li>
</ol>
</div>
</div>
</div>
<script type="module">
import {WindowTestService} from "./bindings/window-visibility-test";
let logCounter = 0;
// Update current time every second
function updateCurrentTime() {
const now = new Date();
document.getElementById('current-time').textContent =
'Current Time: ' + now.toLocaleTimeString() + '.' + String(now.getMilliseconds()).padStart(3, '0');
}
setInterval(updateCurrentTime, 100);
updateCurrentTime();
// Logging function
function logActivity(message, isError = false) {
const log = document.getElementById('activity-log');
const entry = document.createElement('div');
entry.className = 'log-entry';
if (isError) entry.style.borderLeft = '4px solid #ff6b6b';
const timestamp = new Date().toLocaleTimeString() + '.' + String(new Date().getMilliseconds()).padStart(3, '0');
entry.innerHTML = `<strong>[${timestamp}]</strong> ${message}`;
log.appendChild(entry);
log.scrollTop = log.scrollHeight;
// Keep only last 10 entries
while (log.children.length > 10) {
log.removeChild(log.firstChild);
}
console.log(`[Window Test] ${timestamp}: ${message}`);
}
// Window creation functions that call the Go backend
async function createNormalWindow() {
const startTime = performance.now();
logActivity('🚀 Creating normal window...');
try {
const result = await WindowTestService.CreateNormalWindow();
const endTime = performance.now();
logActivity(`✅ ${result} (${Math.round(endTime - startTime)}ms)`);
} catch (error) {
logActivity(`❌ Error creating normal window: ${error.message}`, true);
}
}
async function createDelayedContentWindow() {
const startTime = performance.now();
logActivity('⏳ Creating delayed content window...');
try {
const result = await WindowTestService.CreateDelayedContentWindow();
const endTime = performance.now();
logActivity(`✅ ${result} (${Math.round(endTime - startTime)}ms)`);
} catch (error) {
logActivity(`❌ Error creating delayed content window: ${error.message}`, true);
}
}
async function createHiddenThenShowWindow() {
const startTime = performance.now();
logActivity('🔄 Creating hidden then show window...');
try {
const result = await WindowTestService.CreateHiddenThenShowWindow();
const endTime = performance.now();
logActivity(`✅ ${result} (${Math.round(endTime - startTime)}ms)`);
logActivity('⏰ Watch for window to appear in 2 seconds...');
} catch (error) {
logActivity(`❌ Error creating hidden then show window: ${error.message}`, true);
}
}
async function createMultipleWindows() {
const startTime = performance.now();
logActivity('🔢 Creating multiple windows...');
try {
const result = await WindowTestService.CreateMultipleWindows();
const endTime = performance.now();
logActivity(`✅ ${result} (${Math.round(endTime - startTime)}ms)`);
} catch (error) {
logActivity(`❌ Error creating multiple windows: ${error.message}`, true);
}
}
async function createEfficiencyModeTestWindow() {
const startTime = performance.now();
logActivity('🎯 Creating efficiency mode test window...');
try {
const result = await WindowTestService.CreateEfficiencyModeTestWindow();
const endTime = performance.now();
logActivity(`✅ ${result} (${Math.round(endTime - startTime)}ms)`);
logActivity('🔍 Check: Did the window appear immediately?');
} catch (error) {
logActivity(`❌ Error creating efficiency mode test window: ${error.message}`, true);
}
}
async function rapidWindowCreation() {
logActivity('🚄 Starting rapid window creation test...');
for (let i = 0; i < 3; i++) {
setTimeout(async () => {
try {
await createNormalWindow();
logActivity(`✅ Rapid window ${i + 1} created`);
} catch (error) {
logActivity(`❌ Rapid window ${i + 1} failed: ${error.message}`, true);
}
}, i * 200); // 200ms intervals
}
}
// Make functions globally available for onclick handlers
window.createNormalWindow = createNormalWindow;
window.createDelayedContentWindow = createDelayedContentWindow;
window.createHiddenThenShowWindow = createHiddenThenShowWindow;
window.createMultipleWindows = createMultipleWindows;
window.createEfficiencyModeTestWindow = createEfficiencyModeTestWindow;
window.rapidWindowCreation = rapidWindowCreation;
// Log when the page loads
window.addEventListener('load', function() {
logActivity('📱 Main window loaded and ready for testing');
});
</script>
</body>
</html>

View file

@ -0,0 +1,55 @@
module window-visibility-test
go 1.24.0
toolchain go1.24.4
replace github.com/wailsapp/wails/v3 => ../../
require github.com/wailsapp/wails/v3 v3.0.0-alpha.0
require (
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/adrg/xdg v0.5.3 // indirect
github.com/bep/debounce v1.2.1 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.13.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/u v1.1.1 // indirect
github.com/lmittmann/tint v1.0.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.49.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/wailsapp/go-webview2 v1.0.21 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
// Add any other dependencies that might be needed
// These will be resolved when the user runs go mod tidy

View file

@ -0,0 +1,146 @@
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA=
github.com/wailsapp/go-webview2 v1.0.21/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,308 @@
package main
import (
"embed"
"fmt"
"log"
"time"
"github.com/wailsapp/wails/v3/pkg/application"
)
// WindowTestService provides methods for testing window visibility scenarios
type WindowTestService struct {
app *application.App
}
// NewWindowTestService creates a new window test service
func NewWindowTestService() *WindowTestService {
return &WindowTestService{}
}
// SetApp sets the application reference (internal method, not exposed to frontend)
func (w *WindowTestService) setApp(app *application.App) {
w.app = app
}
// CreateNormalWindow creates a standard window - should show immediately
func (w *WindowTestService) CreateNormalWindow() string {
log.Println("Creating normal window...")
w.app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Normal Window - Should Show Immediately",
Width: 600,
Height: 400,
X: 100,
Y: 100,
HTML: "<html><head><title>Normal Window</title><style>body{font-family:Arial,sans-serif;padding:20px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:white;}</style></head><body><h1>✅ Normal Window</h1><p>This window should have appeared immediately after clicking the button.</p><p>Timestamp: " + time.Now().Format("15:04:05") + "</p></body></html>",
})
return "Normal window created"
}
// CreateDelayedContentWindow creates a window with delayed content to test navigation timing
func (w *WindowTestService) CreateDelayedContentWindow() string {
log.Println("Creating delayed content window...")
// Use HTML that will take time to load (simulates heavy Vue app)
delayedHTML := `
<html>
<head>
<title>Delayed Content</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; }
.spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 40px; height: 40px; animation: spin 2s linear infinite; margin: 20px auto; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body>
<h1> Delayed Content Window</h1>
<p>This window tests navigation completion timing.</p>
<div class="spinner"></div>
<p>Loading... (simulates heavy content)</p>
<script>
// Simulate slow loading content
setTimeout(function() {
document.querySelector('.spinner').style.display = 'none';
document.body.innerHTML += '<h2> Content Loaded!</h2><p>Navigation completed at: ' + new Date().toLocaleTimeString() + '</p>';
}, 3000);
</script>
<p>Window container should be visible immediately, even during load.</p>
</body>
</html>`
w.app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Delayed Content Window - Test Navigation Timing",
Width: 600,
Height: 400,
X: 150,
Y: 150,
HTML: delayedHTML,
})
return "Delayed content window created"
}
// CreateHiddenThenShowWindow creates a hidden window then shows it after delay
func (w *WindowTestService) CreateHiddenThenShowWindow() string {
log.Println("Creating hidden then show window...")
window := w.app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Hidden Then Show Window - Test Show() Robustness",
Width: 600,
Height: 400,
X: 200,
Y: 200,
HTML: "<html><head><title>Hidden Then Show</title><style>body{font-family:Arial,sans-serif;padding:20px;background:linear-gradient(135deg,#a8edea 0%,#fed6e3 100%);color:#333;}</style></head><body><h1>🔄 Hidden Then Show Window</h1><p>This window was created hidden and then shown after 2 seconds.</p><p>Should test the robustness of the show() method.</p><p>Created at: " + time.Now().Format("15:04:05") + "</p></body></html>",
Hidden: true, // Start hidden
})
// Show after 2 seconds to test delayed showing
go func() {
time.Sleep(2 * time.Second)
log.Println("Showing previously hidden window...")
window.Show()
}()
return "Hidden window created, will show in 2 seconds"
}
// CreateMultipleWindows creates multiple windows simultaneously to test performance
func (w *WindowTestService) CreateMultipleWindows() string {
log.Println("Creating multiple windows...")
for i := 0; i < 3; i++ {
bgColors := []string{"#ff9a9e,#fecfef", "#a18cd1,#fbc2eb", "#fad0c4,#ffd1ff"}
content := fmt.Sprintf(`
<html>
<head>
<title>Batch Window %d</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; background: linear-gradient(135deg, %s); color: #333; text-align: center; }
</style>
</head>
<body>
<h1>🔢 Batch Window %d</h1>
<p>Part of multiple windows stress test</p>
<p>All windows should appear quickly and simultaneously</p>
<p>Created at: %s</p>
</body>
</html>`, i+1, bgColors[i], i+1, time.Now().Format("15:04:05"))
w.app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: fmt.Sprintf("Batch Window %d - Stress Test", i+1),
Width: 400,
Height: 300,
X: 250 + (i * 50),
Y: 250 + (i * 50),
HTML: content,
})
}
return "Created 3 windows simultaneously"
}
// CreateEfficiencyModeTestWindow creates a window designed to trigger efficiency mode issues
func (w *WindowTestService) CreateEfficiencyModeTestWindow() string {
log.Println("Creating efficiency mode test window...")
// Create content that might trigger efficiency mode or WebView2 delays
heavyHTML := `
<html>
<head>
<title>Efficiency Mode Test</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
.test-section { margin: 20px 0; padding: 15px; background: rgba(255,255,255,0.1); border-radius: 8px; }
.heavy-content { height: 200px; overflow-y: scroll; background: rgba(0,0,0,0.2); padding: 10px; border-radius: 4px; }
</style>
</head>
<body>
<h1> Efficiency Mode Test Window</h1>
<p>This window tests the fix for Windows 10 Pro efficiency mode issue #2861</p>
<div class="test-section">
<h3>Window Container Status</h3>
<p id="container-status"> Window container is visible (this text proves it)</p>
</div>
<div class="test-section">
<h3>WebView2 Status</h3>
<p id="webview-status"> WebView2 navigation in progress...</p>
<p id="navigation-time"></p>
</div>
<div class="test-section">
<h3>Heavy Content (simulates Vue.js app)</h3>
<div class="heavy-content" id="heavy-content">
Loading heavy content...
</div>
</div>
<script>
// Track navigation completion
var startTime = performance.now();
document.getElementById('navigation-time').textContent = 'Navigation started at: ' + new Date().toLocaleTimeString();
// Simulate heavy JavaScript processing
function heavyProcessing() {
var content = '';
for (var i = 0; i < 1000; i++) {
content += 'Line ' + i + ': Simulated heavy content processing...<br>';
}
document.getElementById('heavy-content').innerHTML = content;
}
// Simulate WebView2 navigation completion with delay
setTimeout(function() {
document.getElementById('webview-status').innerHTML = ' WebView2 navigation completed successfully';
var endTime = performance.now();
document.getElementById('navigation-time').innerHTML += '<br>Navigation completed at: ' + new Date().toLocaleTimeString() +
'<br>Total time: ' + Math.round(endTime - startTime) + 'ms';
heavyProcessing();
}, 2000);
console.log('Window created at:', new Date().toLocaleTimeString());
</script>
</body>
</html>`
w.app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Efficiency Mode Test - Issue #2861 Reproduction",
Width: 700,
Height: 500,
X: 300,
Y: 300,
HTML: heavyHTML,
})
return "Efficiency mode test window created"
}
// GetWindowCount returns the current number of windows
func (w *WindowTestService) GetWindowCount() int {
// This would need to be implemented based on the app's window tracking
// For now, return a placeholder
return 1 // Main window
}
//go:embed assets/*
var assets embed.FS
func main() {
// Create the service
service := NewWindowTestService()
// Create application with menu
app := application.New(application.Options{
Name: "Window Visibility Test",
Description: "Test application for window visibility robustness (Issue #2861)",
Services: []application.Service{
application.NewService(service),
},
Assets: application.AssetOptions{
Handler: application.BundledAssetFileServer(assets),
},
})
// Set app reference in service
service.setApp(app)
// Create application menu
menu := app.NewMenu()
// File menu
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New Normal Window").OnClick(func(ctx *application.Context) {
service.CreateNormalWindow()
})
fileMenu.Add("New Delayed Content Window").OnClick(func(ctx *application.Context) {
service.CreateDelayedContentWindow()
})
fileMenu.AddSeparator()
fileMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
// Test menu
testMenu := menu.AddSubmenu("Tests")
testMenu.Add("Hidden Then Show Window").OnClick(func(ctx *application.Context) {
service.CreateHiddenThenShowWindow()
})
testMenu.Add("Multiple Windows Stress Test").OnClick(func(ctx *application.Context) {
service.CreateMultipleWindows()
})
testMenu.Add("Efficiency Mode Test").OnClick(func(ctx *application.Context) {
service.CreateEfficiencyModeTestWindow()
})
// Help menu
helpMenu := menu.AddSubmenu("Help")
helpMenu.Add("About").OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "About Window Visibility Test",
Width: 500,
Height: 400,
X: 400,
Y: 300,
HTML: "<html><head><title>About</title><style>body{font-family:Arial,sans-serif;padding:20px;background:#f0f0f0;color:#333;text-align:center;}</style></head><body><h1>Window Visibility Test</h1><p>This application tests the fixes for Wails v3 issue #2861</p><p><strong>Windows 10 Pro Efficiency Mode Fix</strong></p><p>Tests window container vs WebView content visibility</p><hr><p><em>Created for testing robust window visibility patterns</em></p></body></html>",
})
})
app.SetMenu(menu)
// Create main window
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Window Visibility Test - Issue #2861",
Width: 800,
Height: 600,
X: 50,
Y: 50,
URL: "/index.html",
})
err := app.Run()
if err != nil {
log.Fatal(err)
}
}