Skip to content

Commit 0869c3b

Browse files
committed
πŸ› fix: fix: improve viewer startup and integrated webapp loading
- Improve integrated WebKit viewer performance settings - Add dark centered loading overlay for viewer startup - Reveal committed long-loading pages without waiting for full load finish - Delay initial URL load until the window and signal handlers are ready - Add one-time startup recovery reload for persisted sessions that reopen blank - Reduce startup recovery delay to 1s for faster YouTube reopen behavior - Update release metadata and bump version to 4.0.6
1 parent 693c136 commit 0869c3b

9 files changed

Lines changed: 368 additions & 12 deletions

File tree

β€ŽCargo.lockβ€Ž

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€ŽCargo.tomlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ members = [
88
]
99

1010
[workspace.package]
11-
version = "4.0.1"
11+
version = "4.0.6"
1212
edition = "2021"
1313
license = "GPL-3.0-or-later"
1414
repository = "https://github.com/biglinux/biglinux-webapps"

β€Žbiglinux-webapps/usr/share/metainfo/br.com.biglinux.webapps.metainfo.xmlβ€Ž

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,31 @@
462462
<control>touch</control>
463463
</supports>
464464
<releases>
465+
<release version="4.0.6" date="2026-05-16">
466+
<description>
467+
<p>Reduces the integrated viewer startup recovery delay so stalled persisted sessions reload faster.</p>
468+
</description>
469+
</release>
470+
<release version="4.0.5" date="2026-05-16">
471+
<description>
472+
<p>Adds a one-time startup recovery reload for integrated-viewer pages that reopen from a persisted session but remain blank, matching the manual reload needed by YouTube.</p>
473+
</description>
474+
</release>
475+
<release version="4.0.4" date="2026-05-16">
476+
<description>
477+
<p>Fixes the integrated viewer startup sequence so complex pages such as YouTube start loading only after the window and loading overlay are fully connected.</p>
478+
</description>
479+
</release>
480+
<release version="4.0.3" date="2026-05-16">
481+
<description>
482+
<p>Simplifies the integrated viewer loading indicator, centers it reliably, and reveals long-running pages such as YouTube as soon as content is committed.</p>
483+
</description>
484+
</release>
485+
<release version="4.0.2" date="2026-05-16">
486+
<description>
487+
<p>Optimizes the integrated WebKit viewer with tighter cache and memory pressure settings, enables hardware acceleration, and replaces the blank initial page with a dark neon loading overlay.</p>
488+
</description>
489+
</release>
465490
<release version="4.0.1" date="2026-05-16">
466491
<description>
467492
<p>Fixes Wayland desktop IDs and icon mapping for browser-backed webapps, preserves integrated-viewer profiles across ID migrations, improves favicon quality selection, and refines auto-hidden headerbar and media playback behavior.</p>

β€Žcrates/webapps-viewer/src/window/chrome.rsβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use gtk4 as gtk;
44
use libadwaita as adw;
55
use webkit6 as webkit;
66

7-
use super::settings;
7+
use super::{loading, settings};
88

99
pub(super) struct ViewerChrome {
1010
pub title_widget: adw::WindowTitle,
@@ -52,7 +52,8 @@ pub(super) fn build_chrome(name: &str, url: &str, webview: &webkit::WebView) ->
5252
let toolbar = adw::ToolbarView::new();
5353
toolbar.add_top_bar(&header);
5454
toolbar.add_top_bar(&url_bar);
55-
toolbar.set_content(Some(webview));
55+
let content = loading::build_loading_overlay(webview);
56+
toolbar.set_content(Some(&content));
5657
toolbar.set_reveal_top_bars(true);
5758

5859
ViewerChrome {
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
use std::{cell::Cell, rc::Rc, sync::Once, time::Duration};
2+
3+
use glib::clone;
4+
use gtk4 as gtk;
5+
use gtk4::gdk;
6+
use gtk4::prelude::*;
7+
use webkit6 as webkit;
8+
use webkit6::prelude::*;
9+
10+
const CONTENT_REVEAL_DELAY_MS: u64 = 250;
11+
12+
const CSS: &str = r#"
13+
.viewer-loading {
14+
background-color: #03040a;
15+
}
16+
17+
.viewer-loading-spinner {
18+
color: #d4d5df;
19+
min-width: 72px;
20+
min-height: 72px;
21+
}
22+
"#;
23+
24+
static CSS_LOADED: Once = Once::new();
25+
26+
pub(super) fn build_loading_overlay(webview: &webkit::WebView) -> gtk::Overlay {
27+
load_css();
28+
29+
let overlay = gtk::Overlay::new();
30+
overlay.set_child(Some(webview));
31+
32+
let veil = gtk::Box::new(gtk::Orientation::Vertical, 0);
33+
veil.add_css_class("viewer-loading");
34+
veil.set_can_target(false);
35+
veil.set_halign(gtk::Align::Fill);
36+
veil.set_valign(gtk::Align::Fill);
37+
veil.set_hexpand(true);
38+
veil.set_vexpand(true);
39+
40+
let spinner = gtk::Spinner::new();
41+
spinner.add_css_class("viewer-loading-spinner");
42+
spinner.set_can_target(false);
43+
spinner.set_halign(gtk::Align::Center);
44+
spinner.set_valign(gtk::Align::Center);
45+
spinner.start();
46+
47+
overlay.add_overlay(&veil);
48+
overlay.add_overlay(&spinner);
49+
50+
let generation = Rc::new(Cell::new(0_u64));
51+
connect_loading_state(webview, &veil, &spinner, &generation);
52+
sync_initial_state(webview, &veil, &spinner, &generation);
53+
54+
overlay
55+
}
56+
57+
fn load_css() {
58+
CSS_LOADED.call_once(|| {
59+
let provider = gtk::CssProvider::new();
60+
provider.load_from_data(CSS);
61+
62+
if let Some(display) = gdk::Display::default() {
63+
gtk::style_context_add_provider_for_display(
64+
&display,
65+
&provider,
66+
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
67+
);
68+
}
69+
});
70+
}
71+
72+
fn connect_loading_state(
73+
webview: &webkit::WebView,
74+
veil: &gtk::Box,
75+
spinner: &gtk::Spinner,
76+
generation: &Rc<Cell<u64>>,
77+
) {
78+
webview.connect_load_changed(clone!(
79+
#[weak]
80+
veil,
81+
#[weak]
82+
spinner,
83+
#[strong]
84+
generation,
85+
move |_, event| match event {
86+
webkit::LoadEvent::Started | webkit::LoadEvent::Redirected => {
87+
show_loading(&veil, &spinner, &generation);
88+
}
89+
webkit::LoadEvent::Committed => schedule_hide(&veil, &spinner, &generation),
90+
webkit::LoadEvent::Finished => {
91+
hide_loading(&veil, &spinner);
92+
}
93+
_ => {}
94+
}
95+
));
96+
97+
webview.connect_load_failed(clone!(
98+
#[weak]
99+
veil,
100+
#[weak]
101+
spinner,
102+
#[strong]
103+
generation,
104+
#[upgrade_or]
105+
false,
106+
move |_, _, _, _| {
107+
generation.set(generation.get().wrapping_add(1));
108+
hide_loading(&veil, &spinner);
109+
false
110+
}
111+
));
112+
113+
webview.connect_estimated_load_progress_notify(clone!(
114+
#[weak]
115+
veil,
116+
#[weak]
117+
spinner,
118+
#[strong]
119+
generation,
120+
move |wv| {
121+
if wv.estimated_load_progress() >= 0.35 {
122+
schedule_hide(&veil, &spinner, &generation);
123+
}
124+
}
125+
));
126+
}
127+
128+
fn sync_initial_state(
129+
webview: &webkit::WebView,
130+
veil: &gtk::Box,
131+
spinner: &gtk::Spinner,
132+
generation: &Rc<Cell<u64>>,
133+
) {
134+
if webview.is_loading() || webview.estimated_load_progress() < 1.0 {
135+
show_loading(veil, spinner, generation);
136+
} else {
137+
hide_loading(veil, spinner);
138+
}
139+
}
140+
141+
fn show_loading(veil: &gtk::Box, spinner: &gtk::Spinner, generation: &Rc<Cell<u64>>) {
142+
generation.set(generation.get().wrapping_add(1));
143+
veil.set_visible(true);
144+
spinner.set_visible(true);
145+
spinner.start();
146+
}
147+
148+
fn schedule_hide(veil: &gtk::Box, spinner: &gtk::Spinner, generation: &Rc<Cell<u64>>) {
149+
let expected_generation = generation.get();
150+
glib::timeout_add_local_once(
151+
Duration::from_millis(CONTENT_REVEAL_DELAY_MS),
152+
clone!(
153+
#[weak]
154+
veil,
155+
#[weak]
156+
spinner,
157+
#[strong]
158+
generation,
159+
move || {
160+
if generation.get() == expected_generation {
161+
hide_loading(&veil, &spinner);
162+
}
163+
}
164+
),
165+
);
166+
}
167+
168+
fn hide_loading(veil: &gtk::Box, spinner: &gtk::Spinner) {
169+
spinner.stop();
170+
spinner.set_visible(false);
171+
veil.set_visible(false);
172+
}

β€Žcrates/webapps-viewer/src/window/mod.rsβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ mod chrome;
1010
mod context_menu;
1111
mod downloads;
1212
mod geometry;
13+
mod loading;
1314
mod navigation;
1415
mod permissions;
1516
mod session;
1617
mod settings;
1718
mod shortcuts;
1819
mod shortcuts_window;
20+
mod startup;
1921

2022
#[allow(unused_imports)]
2123
use adw::prelude::*;
@@ -34,7 +36,7 @@ pub fn build(
3436
app_id: &str,
3537
auto_hide_headerbar: bool,
3638
) -> adw::ApplicationWindow {
37-
let viewer_session = session::build_viewer_session(app_id, url);
39+
let viewer_session = session::build_viewer_session(app_id);
3840
let chrome = chrome::build_chrome(name, url, &viewer_session.webview);
3941

4042
let window = adw::ApplicationWindow::builder()
@@ -99,5 +101,7 @@ pub fn build(
99101
chrome.toolbar.set_reveal_top_bars(false);
100102
}
101103

104+
startup::connect_initial_load(&window, &viewer_session.webview, url);
105+
102106
window
103107
}

β€Žcrates/webapps-viewer/src/window/session.rsβ€Ž

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
use std::path::PathBuf;
22

3+
use gdk4 as gdk;
34
use webkit6 as webkit;
45
use webkit6::prelude::*;
56

67
use webapps_core::config;
78

89
use super::settings;
910

11+
const WEB_PROCESS_MEMORY_LIMIT_MB: u32 = 1024;
12+
const NETWORK_PROCESS_MEMORY_LIMIT_MB: u32 = 512;
13+
const MEMORY_PRESSURE_CONSERVATIVE_THRESHOLD: f64 = 0.50;
14+
const MEMORY_PRESSURE_STRICT_THRESHOLD: f64 = 0.75;
15+
const MEMORY_PRESSURE_POLL_INTERVAL_SECONDS: f64 = 30.0;
16+
1017
pub(super) struct ViewerSession {
1118
pub session: webkit::NetworkSession,
1219
pub webview: webkit::WebView,
1320
pub data_dir: PathBuf,
1421
}
1522

16-
pub(super) fn build_viewer_session(app_id: &str, url: &str) -> ViewerSession {
23+
pub(super) fn build_viewer_session(app_id: &str) -> ViewerSession {
1724
let data_dir = config::data_dir().join(app_id);
1825
let cache_dir = config::cache_dir().join(app_id);
1926
std::fs::create_dir_all(&data_dir).ok();
2027
std::fs::create_dir_all(&cache_dir).ok();
2128

29+
let mut network_pressure = memory_pressure_settings(NETWORK_PROCESS_MEMORY_LIMIT_MB);
30+
webkit::NetworkSession::set_memory_pressure_settings(&mut network_pressure);
31+
32+
let web_pressure = memory_pressure_settings(WEB_PROCESS_MEMORY_LIMIT_MB);
33+
let web_context = webkit::WebContext::builder()
34+
.memory_pressure_settings(&web_pressure)
35+
.build();
36+
web_context.set_cache_model(webkit::CacheModel::DocumentBrowser);
37+
2238
let session = webkit::NetworkSession::new(
2339
Some(data_dir.to_str().unwrap_or_default()),
2440
Some(cache_dir.to_str().unwrap_or_default()),
@@ -34,10 +50,14 @@ pub(super) fn build_viewer_session(app_id: &str, url: &str) -> ViewerSession {
3450
cookie_manager.set_accept_policy(webkit::CookieAcceptPolicy::Always);
3551
}
3652

37-
let webview = webkit::WebView::builder().network_session(&session).build();
53+
let webview = webkit::WebView::builder()
54+
.web_context(&web_context)
55+
.network_session(&session)
56+
.build();
57+
let background = gdk::RGBA::new(0.012, 0.014, 0.030, 1.0);
58+
webview.set_background_color(&background);
3859
settings::configure_settings(&webview);
3960
settings::inject_resize_block(&webview);
40-
webview.load_uri(url);
4161
webview.set_vexpand(true);
4262
webview.set_hexpand(true);
4363

@@ -47,3 +67,13 @@ pub(super) fn build_viewer_session(app_id: &str, url: &str) -> ViewerSession {
4767
data_dir,
4868
}
4969
}
70+
71+
fn memory_pressure_settings(limit_mb: u32) -> webkit::MemoryPressureSettings {
72+
let mut pressure = webkit::MemoryPressureSettings::new();
73+
pressure.set_memory_limit(limit_mb);
74+
pressure.set_conservative_threshold(MEMORY_PRESSURE_CONSERVATIVE_THRESHOLD);
75+
pressure.set_strict_threshold(MEMORY_PRESSURE_STRICT_THRESHOLD);
76+
pressure.set_kill_threshold(0.0);
77+
pressure.set_poll_interval(MEMORY_PRESSURE_POLL_INTERVAL_SECONDS);
78+
pressure
79+
}

β€Žcrates/webapps-viewer/src/window/settings.rsβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ pub(super) fn configure_settings(webview: &webkit::WebView) {
3434
s.set_enable_encrypted_media(true);
3535
s.set_enable_site_specific_quirks(true);
3636
s.set_enable_html5_local_storage(true);
37-
s.set_enable_page_cache(true);
37+
s.set_enable_page_cache(false);
3838
s.set_enable_smooth_scrolling(true);
3939
s.set_enable_back_forward_navigation_gestures(true);
40+
s.set_hardware_acceleration_policy(webkit::HardwareAccelerationPolicy::Always);
4041
// spoof Chrome UA β†’ sites like Spotify/Teams reject unknown browsers
4142
s.set_user_agent(Some(SPOOFED_UA));
4243
}

0 commit comments

Comments
Β (0)