Commit Graph
100 Commits
Author SHA1 Message Date
ZacharyZhang-NY 540b901fd6 fix(sync): secure encrypted snapshot lifecycle 2026-07-10 06:24:53 -04:00
ZacharyZhang-NY 556c5ff624 fix(sync): enforce private profile boundaries 2026-07-10 03:02:10 -04:00
ZacharyZhang-NY cb0dc7f23f fix(auth): prevent device registration replay 2026-07-10 02:22:52 -04:00
ZacharyZhang-NY f6ee99c3c2 fix(auth): make session revocation authoritative 2026-07-10 02:06:09 -04:00
ZacharyZhang-NY b7500b4f91 fix(servo): refresh upstream safety fixes 2026-07-10 01:56:30 -04:00
ZacharyZhang-NY 15ae783c05 ci(security): enforce Rust advisory policy 2026-07-10 01:23:14 -04:00
ZacharyZhang-NY e085174b89 fix(servo): gate vulnerable RSA private operations 2026-07-10 01:06:48 -04:00
ZacharyZhang-NY 670f7abe60 fix(deps): backport secure Wayland XML parser 2026-07-10 00:37:49 -04:00
ZacharyZhang-NY fcc8443c45 fix(deps): upgrade maintained stack safety macros 2026-07-10 00:18:12 -04:00
ZacharyZhang-NY 54adc0eab8 fix(deps): upgrade git2 to 0.21.0 2026-07-10 00:14:57 -04:00
ZacharyZhang-NY 1252c50fdc fix(deps): update quinn memory bound patch 2026-07-10 00:12:38 -04:00
ZacharyZhang-NY 144ba89ba3 fix(deps): update memory map safety patch 2026-07-10 00:10:30 -04:00
ZacharyZhang-NY a3dcc80952 fix(deps): update crossbeam pointer safety patch 2026-07-10 00:09:30 -04:00
ZacharyZhang-NY 70e595ddd4 fix(deps): update anyhow soundness patch 2026-07-10 00:08:30 -04:00
ZacharyZhang-NY e68b591a14 fix(deps): remove vulnerable grid release 2026-07-09 23:59:42 -04:00
ZacharyZhang-NY 27899bbc22 fix(deps): update vulnerable tar parser 2026-07-09 23:51:24 -04:00
ZacharyZhang-NY 58e9bd7d38 fix(servo): bound live request frames 2026-07-09 23:44:27 -04:00
ZacharyZhang-NY 83badaea81 fix(servo): bound live protocol metadata 2026-07-09 23:32:17 -04:00
ZacharyZhang-NY a6e3aeaf46 fix(navigation): classify HTTP schemes case-insensitively 2026-07-09 23:08:15 -04:00
ZacharyZhang-NY 755a6aabd8 fix(servo): lease persistent profile data 2026-07-09 23:05:10 -04:00
ZacharyZhang-NY 62ee35a164 fix(cloudflare): upgrade audited worker toolchain 2026-07-09 22:56:58 -04:00
ZacharyZhang-NY 3b528ab5c9 fix(cloudflare): upgrade vulnerable auth dependency 2026-07-09 22:55:37 -04:00
ZacharyZhang-NY 7d70db6da9 fix(sync): preserve private profile boundaries 2026-07-09 22:48:44 -04:00
ZacharyZhang-NY 31fc1acf17 fix(servo): retire sessions on internal navigation 2026-07-09 22:37:17 -04:00
ZacharyZhang-NY b422ac1631 fix(permissions): make profile snapshots authoritative 2026-07-09 21:56:36 -04:00
ZacharyZhang-NY c28ec2bee8 feat(servo): isolate profiles with hardware sidecars 2026-07-09 20:27:22 -04:00
ZacharyZhang-NY 78dc86b18e feat(servo): upgrade to latest secure upstream snapshot 2026-07-09 16:00:50 -04:00
ZacharyZhang-NYandClaude Opus 4.8 12565eabc7 fix(gpui): restore Windows/Linux build — sync_native_surface signature
Commit 3678db6 added a `corner_radii: Corners<Pixels>` parameter to the
`PlatformWindow::sync_native_surface` trait method (and to the macOS impl
and the `native_surface` element call site) to clip the overlay to the
panel's rounded corners, but left the Windows, X11, and Wayland impls at
the old 2-argument signature. A 2-arg method in an `impl PlatformWindow`
block against a 3-arg trait method is an E0050 compile error, so the
workspace no longer built on Windows, X11, or Wayland — only macOS, which
is the sole CI runner, so it went unnoticed. This breaks the project's
explicit macOS/Windows/Linux requirement.

Align all three impls to the trait by accepting `_corner_radii`. Bodies
are unchanged: those platforms position/size the child surface exactly as
before and do not clip its corners (the pre-3678db6 behaviour on every
platform — not a regression; per-platform corner clipping can land later).
Verified: signatures now match the trait (`crate::Corners<Pixels>`, the
same path the trait uses), rustfmt parses all three files, and the macOS
build is unaffected (cargo check -p ely_app clean). Windows/Linux cannot
be compile-checked on this macOS host (their C deps need the platform SDK),
but the fix is a type-level signature alignment to a known trait.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:23:15 -04:00
ZacharyZhang-NYandClaude Opus 4.8 d150da5bd0 fix(servo): enable variable fonts so weight/width axes render
Like CSS Grid, Servo's `Preferences::default()` ships
`layout_variable_fonts_enabled: false`. `Servo::new` forwards it to Stylo
(`layout.variable_fonts.enabled`), and with the gate off Stylo ignores
`font-variation-settings` and variable weight/width axes: a variable font
renders only its default instance, so every requested weight looks
identical. Modern sites lean on variable fonts (Inter, Roboto Flex,
system New York/SF), so text rendered at the wrong weight versus Chrome.

servo-fonts already drives variations through HarfBuzz, so enabling the
pref is the real fix. Verified with a `@font-face` page using a variable
font at `font-variation-settings: "wght" 200` vs `"wght" 900`: identical
weight before, distinctly light vs black after.

runtime.rs 473 lines (<500). fmt/audit/clippy clean; software_host real-
Servo test passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:08:41 -04:00
ZacharyZhang-NYandClaude Opus 4.8 585cb25fe3 fix(domain): drop unwrap/panic from command tests for clippy gate
The workspace lints deny `clippy::unwrap_used` and `clippy::panic`, but
`command.rs`'s two unit tests used `.unwrap()` and `panic!`, so
`cargo clippy --workspace --all-targets -- -D warnings` (a CI gate)
failed on them. Convert both to the crate's Result-returning test
convention: `CommandIntent::parse(...)?` instead of `.unwrap()`, and a
`return Err(...)` in the let-else instead of `panic!`. Same assertions;
`DomainError` is `thiserror::Error`, so `?` flows into `Box<dyn Error>`.

Workspace clippy --all-targets now reports 0 errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:56:35 -04:00
ZacharyZhang-NYandClaude Opus 4.8 207eeaf54c style: drop stray blank lines left by the test-file splits
The `sed`-based extractions in the preceding three split commits each
left one extra blank line at the cut boundary, which `cargo fmt --all
--check` (a CI gate) rejects. Whitespace only — no code change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:53:41 -04:00
ZacharyZhang-NYandClaude Opus 4.8 7fc73e6ac8 refactor(shell): split web_surface_live_site_tests under 500-line audit
`web_surface_live_site_tests.rs` (648, behind the `live-site-smoke`
feature) was the last file over the audit ceiling. Its helpers split
cleanly one-directionally: drivers/waiters call the leaf
validators/builders, never the reverse.

Keep imports, consts, the 6 tests, `run_isolated_live_site_test`, the
`assert_*` drivers, `render_web_surface_frame`, and the `wait_for_*`
helpers in the parent; move the leaf validators + pure fixtures
(`ExpectedCssViewport`, `validate_prd_frame*`, `log_prd_frame`,
`require*`, `*_bounds`, `live_scroll_point`, `web_tab`, `normalized_url`)
into `web_surface_live_site_support.rs` as `pub(super)` items, opened
with `use super::*;`. Parent pulls them back via `use ...support::*`.

470 / 184 lines. `cargo build/clippy -p ely_app --tests --features
live-site-smoke -- -D warnings` clean. scripts/audit_source_lines.sh now
exits 0 (every source file <= 500).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:51:42 -04:00
ZacharyZhang-NYandClaude Opus 4.8 de413f8f46 refactor(shell): split 1062-line gpui_harness_tests god-component
`gpui_harness_tests.rs` was the repo's largest file (1062 lines) and the
worst violator of the 500-line / no-god-component audit. Its 13
`#[gpui::test]`s each build their own local fixtures and share only a
pair of type aliases, an `impl super::ElyShell` test helper, and two free
fns (`active_tab_overlay_state`, `example_url`).

Keep those shared items plus tests 1-4 in the root module; move tests 5-8
to `gpui_harness_tests_b.rs` and tests 9-13 to `gpui_harness_tests_c.rs`,
each opening with `use super::*;` so they inherit the parent's imports
and shared items with no per-item churn. Declared via `#[path]` mod, the
established sibling-test pattern in this crate.

475 / 319 / 276 lines. No tests added, removed, or renamed (paths gain a
`gpui_harness_tests_{b,c}::` segment). clippy --all-targets -D warnings
clean; `cargo test -p ely_app` 167 passed / 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:47:51 -04:00
ZacharyZhang-NYandClaude Opus 4.8 66e5b266e9 refactor(core): split oversized integration-test files under 500-line audit
`scripts/audit_source_lines.sh` (a CI gate) flagged `tests/commands.rs`
(527) and `tests/splits.rs` (524) over the 500-line ceiling. Both are
flat lists of independent `#[test]` fns with no shared helpers, so each
splits cleanly into a sibling integration-test binary (the crate already
uses a topical file-per-concern layout under `tests/`).

- commands.rs (29 tests) -> commands.rs (20: tab/space/profile/search)
  + command_pages.rs (9: internal-page-opening commands). 527 -> 389.
- splits.rs (25 tests) -> splits.rs (13: layout/axis/detach mechanics)
  + saved_split_lifecycle.rs (12: close/archive/restore/group). 524 -> 266.

No tests added or removed; each new file carries only the imports it
uses. `cargo clippy -p ely_browser_core --tests -- -D warnings` clean;
all ely_browser_core tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:34:56 -04:00
ZacharyZhang-NYandClaude Opus 4.8 b1fd8ab3f8 refactor(servo): extract paint coordination into runtime_paint
`runtime.rs` had grown past the 500-line ceiling enforced by
`scripts/audit_source_lines.sh` once the grid-pref comment landed (522
lines). Peel the repaint/present pair — `paint_without_readback`,
`paint_without_readback_with_completion`, the private `paint_webview`,
and `paint_with_readback` — into a sibling `runtime_paint.rs`, exactly
the `paint.rs` boundary the embedding architecture doc prescribes.

`runtime_paint` is a child module of `runtime` (declared via `#[path]`,
mirroring `runtime_context`), so it keeps access to the private
`SoftwareServoHost` fields and the `webview()` / `wait_for_paint_completion`
/ `read_rendered_frame` helpers without widening any visibility. No
behaviour change: public API and call sites are identical.

runtime.rs 522 -> 469 lines. Build + clippy clean; full workspace test
suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:17:00 -04:00
ZacharyZhang-NYandClaude Opus 4.8 3d36cb25bf fix(servo): enable CSS Grid so grid layouts stop collapsing
`Preferences::default()` is Servo's conservative library default and
ships `layout_grid_enabled: false`. `Servo::new` forwards prefs to Stylo
(`prefs::set` -> `stylo_static_prefs::set_pref!("layout.grid.enabled")`),
so with the gate off Stylo blockifies `display: grid`: every grid
container collapses to `display: block` and grid-based page layouts
stack into a single column — the "broken" rendering reported on modern
sites.

`ely_servo_preferences()` only flipped `dom_intersection_observer_enabled`
and inherited the grid default, so ELY rendered grid pages collapsed
while Servo's own servoshell (which enables the pref) renders them
correctly. The layout path is implemented — servo-layout drives
`DisplayInside::Grid` through Taffy — so enabling the pref is the real
fix, not a workaround.

Verified with a deterministic `display: grid; grid-template-columns:
1fr 1fr 1fr` page: 9 stacked full-width bars before, a 3x3 grid after.
Wikipedia/HN/GitHub/google.com re-checked unchanged; full workspace
test suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:07:50 -04:00
ZacharyZhang-NY aa8182bec4 update 2026-05-21 15:44:26 -04:00
ZacharyZhang-NY 7fc2967793 fix(servo): skip WebView::load when Servo already at requested URL
Servo's \`WebView::set_history\` fires \`notify_url_changed\` on **every**
history mutation — full navigations, redirects, in-page link clicks,
and JS-driven \`history.pushState\` / \`history.replaceState\`.

Our pipeline fans that delegate signal back through
\`WebSurfaceUrlChange\` into \`tab.url\`. The next \`ensure_surface\`
observes the new tab URL, hashes a fresh ensure key, and calls
\`ServoLiveClient::ensure\` → \`apply_navigation\`. Until this commit
\`apply_navigation\` compared the request against the local
\`session.requested_url\` cache and, on mismatch, sent Servo a
\`webview.load(url)\` — even when Servo was the one who *just*
told us about that URL.

\`WebView::load\` is a hard navigation: it tells the constellation
to abort the current document, clear the surface, and refetch.
google.com's homepage \`replaceState\`s a fresh \`?zx=<timestamp>\`
roughly once a second to bust caches; with this round-trip we
were turning each of those into a full load-clear-refetch and
producing one visible white flash per second.

Use \`host.snapshot(webview_id).url()\` (which Servo keeps in lock-step
with \`set_history\`) as the source of truth. If Servo's WebView is
already at the requested URL, just sync our \`session.requested_url\`
bookkeeping and return — no \`load\` message, no surface clear, no
flash. Genuine embedder-initiated navigations (chrome URL bar typed,
in-app link click) still take the \`should_navigate\` path because
Servo's URL hasn't caught up to the requested target yet.
2026-05-18 15:46:18 -04:00
ZacharyZhang-NY 0a97c4af13 fix(servo): stop forcing paint+present on every ensure
Servo's reference embedder (\`examples/winit_minimal.rs\`) is reactive:

  - \`user_event\` only calls \`servo.spin_event_loop()\` — never paints
  - \`notify_new_frame_ready\` is what flips the embedder into the
    redraw path (calls \`window.request_redraw()\`)
  - \`RedrawRequested\` is the **single** site that runs
    \`webview.paint() + rendering_context.present()\`

Painting is therefore at-most-once-per-real-frame: a paint happens
only when Servo has actually composited new content.

Our \`ServoLiveClient::ensure\` was forcing
\`paint_without_readback_with_completion\` after every setup pass.
\`ensure\` runs on every \`ensure_surface\` invocation — viewport
debounce settle, URL redirect (google.com → / → /?zx=…), permission
update, native-surface re-attach, page-zoom change. Each invocation
called Servo's painter while it had no new composited frame ready:
WebRender's \`clear_background\` wiped the surface to the configured
shell background colour, the empty scene rendered, \`present\` swapped
that blank surface to the NSView's CALayer — and the user saw a
white flash. A chain of redirects produced a chain of flashes.

Drop the unconditional \`paint\` from \`ensure\`. Keep the
\`host.tick()\` (Servo's spin) so the navigate / viewport / input
messages reach the constellation in this turn, but defer painting to
\`poll\`, which already gates \`paint_without_readback_with_completion\`
on \`snapshot.has_pending_frame()\` — exactly mirroring the
reference embedder's reactive model. \`ServoLiveFrame\` returned from
\`ensure\` only carries snapshot metadata, so callers that depend on
the frame value (worker queue, UI state) are unaffected.
2026-05-18 15:39:14 -04:00
ZacharyZhang-NY d1e811b724 fix(servo): resize through WebView only, never the rendering context
Servo's reference embedder (\`examples/winit_minimal.rs\`) handles
resize with a single call: \`webview.resize(new_size)\`. The Servo
paint pipeline behind that call:

  1. early-returns if \`rendering_context.size() == new_size\`,
  2. otherwise calls \`rendering_context.resize\` itself,
  3. updates \`webview_renderer.rect\` so the compositor relays out
     the page at the new device viewport,
  4. sends \`transaction.set_document_view(...)\` so WebRender's
     document viewport matches the surface,
  5. flags the painter \`needs_repaint(RepaintReason::Resize)\`.

Our \`runtime.rs::resize\` called \`webview.rendering_context.resize\`
*before* \`webview.webview.resize\`. surfman accepted the new size, so
the painter saw \`rendering_context.size() == new_size\` and took the
early-return path — steps 3, 4, and 5 never ran. The compositor
kept the original (creation-time) viewport rect while we presented
a much larger surface. The page ended up laid out for a tiny
viewport and either rendered into the top-left of a sea of cleared
background (StableLance) or never painted any pipeline at all
(google.com appeared totally blank).

Drop the direct \`rendering_context.resize\` call and route through
\`WebView::resize\` exactly as the reference embedder does. The
debounce in \`record_viewport_size\` still collapses a sidebar /
window-edge animation into a single trailing-edge resize, so we
also avoid hammering Servo with per-frame surface mutations.
2026-05-18 15:30:55 -04:00
ZacharyZhang-NY 3678db60b9 fix(shell): clip native_surface overlay to its panel's corner radius
The web canvas is a GPUI `native_surface` element, which on macOS
attaches an `NSView` (with a Metal-backed `CALayer`) as an AppKit
overlay on top of the GPUI render layer. GPUI's `overflow: hidden`
clips its own children but cannot reach into AppKit, so the canvas
painted square corners on top of the main panel's rounded edge —
the white surface visibly overshot the panel's rounded bottom.

Thread a per-corner radius through the GPUI patch:

- `Window::sync_native_surface` and the `PlatformWindow` trait carry
  a `Corners<Pixels>`.
- The macOS implementation sets `CALayer.cornerRadius` to the maximum
  requested radius and `maskedCorners` to the bitmask of corners that
  actually requested rounding (GPUI is Y-down while NSView is Y-up,
  so a GPUI "top" maps to a CALayer "MaxY" corner — comment locks
  the mapping). `setMasksToBounds: YES` lets Core Animation actually
  honor the corner radius for layered content.
- The `NativeSurface` element resolves all four corner radii from its
  own `StyleRefinement` (so callers attach `.rounded_bl(...)` /
  `.rounded_br(...)` exactly as on any other GPUI element).

Application side wires the right radius per context:

- The main canvas asks for `RADIUS_CARD` (18 px) on its bottom corners
  so it follows the surrounding panel's rounded bottom edge while
  the top stays flush against the toolbar.
- A split pane's canvas asks for the pane's own `SPLIT_PANE_RADIUS`
  (10 px) so the canvas hugs the rounded frame the pane already paints.

`render_failed_web_surface` doesn't need rounding wiring — the error
page is a GPUI div and is clipped normally by the surrounding
`overflow: hidden`.
2026-05-18 15:07:53 -04:00
ZacharyZhang-NY 8aa9ddaeb2 fix(shell): debounce viewport resize so animations stop flashing
Sidebar / window-edge animations emit a new viewport bounds on every
GPUI paint. The previous flow fired `runtime.ensure_tab` synchronously
on every change, which reached down into Servo's surfman backend and
destroyed + reallocated the rendering framebuffer 50+ times per second
— each cycle leaving the NSView with an empty surface until Servo's
next paint completed. The user saw the page strobe blank-then-content
on every gesture, plus a one-time Metal driver warning about an
unloadable texture (the previous framebuffer caught mid-recreate).

Track `viewport_size_changed_at` on `PerTabSurface` and propagate the
debounce on two seams:

- `record_viewport_size` returns `Buffered` for a transition that
  arrives inside the 80 ms window of the previous one, so the
  synchronous `flush_external_web_surface_tick` from the GPUI paint
  callback skips the resize altogether. The very first measurement
  and the first transition after a quiet period still return `Applied`
  so a single drag step or page-load is not delayed.
- `ensure_surface` skips while the viewport is settling, but only
  *after* the initial ensure has installed `last_ensure_key`. The
  first ensure must fire even mid-gesture or the page never loads.
- `next_tick_delay` clamps the poll cadence to `ACTIVE_POLL_INTERVAL`
  while any visible tab is settling, so the trailing-edge resize
  fires within a frame of the gesture stopping instead of waiting
  for an 80 ms idle tick.

The synchronous `record + ensure` pattern in
`failed_surface_ensure_waits_for_a_new_key_before_retrying` can't
advance an `Instant`, so it now calls a `#[cfg(test)]`
`clear_viewport_resize_debounce_for_test` to simulate the
trailing edge and exercise the retry-on-new-key business rule in
isolation. Adds `rapid_viewport_changes_buffer_until_gesture_settles`
to lock the new behaviour.
2026-05-18 14:53:56 -04:00
ZacharyZhang-NY fc1c0ed8d3 refactor(shell): peel session + wire helpers off WebSurfaceRuntime
web_surface_runtime.rs hit 628 lines because it carried four unrelated
concerns:

- session domain types (`WebSurfaceRuntimeScope`, `WebSurfaceSession`,
  `WebSurfaceUrlChange`, …) and their config-dir / `session_for_scope`
  helpers — these are the runtime's input vocabulary, not its control
  flow.
- wire-side glue (scroll-input field marshalling, `pending_input_kind`
  classifier, the latency tracing call, and the `From<&Site...>` impl
  that lowers permissions to the Servo client) — these are the runtime's
  output vocabulary.
- the runtime itself, plus its tests harness.

Split into:
- `web_surface_runtime.rs` (441 lines): WebSurfaceRuntime + Drop.
- `web_surface_runtime_session.rs`: the session types + helpers, with
  `pub(super)`-exposed methods so the runtime can drive them.
- `web_surface_runtime_wire.rs`: the wire helpers and the From impl.

`web_surface_runtime` keeps re-exporting the URL-change / frame /
ensure-result types so existing call sites
(`web_surface_state.rs`, `web_surface.rs`, `web_surface_controller.rs`)
need no rewiring.
2026-05-18 14:28:57 -04:00
ZacharyZhang-NY dabb965afd refactor(shell): peel input recorders off WebSurfaceStore
`web_surface.rs` had crept to 548 lines; six unrelated input-recorder
methods (scroll delta, viewport size, native surface, hover point,
click point, typed text) dominated the file. Move them into a sibling
`web_surface_input.rs` so the store file (now 338 lines) is the
ensure / tick / lifecycle facade and the input file is a flat set of
`record_*` methods on the same struct.

`surfaces`, `keyboard_focus`, and `surface_mut` become `pub(super)` so
the sibling can reach them; everything else stays private.
2026-05-18 14:24:52 -04:00
ZacharyZhang-NY db7cd2f9bc refactor(core): split tabs.rs by tab concern
tabs.rs had grown to 538 lines — past the 500-line ceiling — and was
mixing four unrelated concerns:

- `tab_navigation` (URL changes + history back/forward)
- `tab_metadata` (title, favicon, zoom, favorite/pin, sort, sync flag)
- `tab_archive_restore` (un-archive entry points + query matching)
- core CRUD (open / close / move-to-space + private helpers)

Each split file lives below 250 lines and only pulls the `ely_domain`
types and crate helpers it actually uses. `active_tab_mut` and the
`TabUrlUpdate` enum get `pub(super)` so siblings can reach them; no
behaviour change.
2026-05-18 14:20:58 -04:00
ZacharyZhang-NY 4cbd1c8870 test(servo): pin DPR-on-first-apply contract with viewport diff
Lift `apply_viewport`'s three-way diff (resize / set_hidpi / set_zoom)
into a `ViewportChange` value so the decision is testable without a
running `SoftwareServoHost`. Four unit cases pin the contract:

- Retina creation (DPR=2.0): only the hidpi push fires (the bug above).
- Standard-DPI creation (DPR=1.0): nothing to push, session already
  matches Servo's defaults.
- Mid-session zoom change: only `set_page_zoom` fires.
- Cross-monitor DPR change: only the hidpi push fires.

This is a regression guard for the previous commit — any future change
that re-initializes a session from request values instead of Servo's
post-build defaults will fail `fresh_retina_session_pushes_hidpi_only`.
2026-05-18 14:14:04 -04:00
ZacharyZhang-NY a067d62ad3 fix(servo): push DPR on first viewport apply
The newly-created session was stamped with the request's hidpi factor,
but `WebViewBuilder` defaults `hidpi_scale_factor` to 1.0 and we never
override it. `apply_viewport`'s diff check then saw
`session.dpr == request.dpr` and skipped `set_hidpi_scale`, leaving Servo
at hidpi=1.0 forever. On Retina that collapses CSS pixels onto device
pixels — the page lays out for a 2× viewport and renders at half size.
Same shape applies to page zoom.

Initialize the session with Servo's actual post-build defaults so the
viewport diff is the source of truth for whether `set_hidpi_scale` and
`set_page_zoom` need to run.
2026-05-18 14:12:18 -04:00
ZacharyZhang-NY 68a4507dbe update 2026-05-18 13:58:36 -04:00
ZacharyZhang-NY d076dad356 Fix shell regressions and reset Servo embedding direction 2026-05-16 11:23:41 -04:00
ZacharyZhang-NY 7823ecd0a3 feat(sync): include plugin settings in snapshots 2026-05-16 07:12:52 -04:00
ZacharyZhang-NY 80729083eb feat(sync): include history in snapshots 2026-05-16 07:03:59 -04:00
ZacharyZhang-NY b550360a47 refactor(core): split browser settings state 2026-05-16 06:48:45 -04:00
ZacharyZhang-NY 00d48a9286 feat(sync): include site permissions in snapshots 2026-05-16 06:43:04 -04:00
ZacharyZhang-NY d5e25345c5 refactor(sync): split snapshot apply coordinator 2026-05-16 06:35:31 -04:00
ZacharyZhang-NY 5409c02394 feat(sync): include profiles in snapshots 2026-05-16 06:31:52 -04:00
ZacharyZhang-NY 323271fc1f feat(sync): include reading list in snapshots 2026-05-16 06:17:38 -04:00
ZacharyZhang-NY b2b3d5deca feat(sync): include notes in snapshots 2026-05-16 06:07:44 -04:00
ZacharyZhang-NY 7d5e936ca4 refactor(sync): split snapshot records 2026-05-16 05:52:39 -04:00
ZacharyZhang-NY bfdc97d7fd feat(sync): preserve spaces in snapshots 2026-05-16 05:42:17 -04:00
ZacharyZhang-NY 973fd0a582 feat(app): animate sync toggle motion 2026-05-16 05:27:24 -04:00
ZacharyZhang-NY 0c9bfc2673 perf(sync): avoid snapshot clone when scheduling uploads 2026-05-16 05:19:54 -04:00
ZacharyZhang-NY 51f836f34c feat(sync): auto upload changed browser state 2026-05-16 05:13:19 -04:00
ZacharyZhang-NY 0d755af005 perf(app): suppress repeated web metadata updates 2026-05-16 05:03:43 -04:00
ZacharyZhang-NY 76a56decff perf(sidecar): avoid second hardware warmup readback 2026-05-16 04:55:47 -04:00
ZacharyZhang-NY df092f285c perf(app): defer web poll after ensure 2026-05-16 04:40:34 -04:00
ZacharyZhang-NY 654f661237 perf(app): skip unchanged software web frames 2026-05-16 04:36:49 -04:00
ZacharyZhang-NY 91df575cf8 perf(app): batch scroll surface flushes 2026-05-16 04:23:49 -04:00
ZacharyZhang-NY 0c733fa006 perf(app): settle web cadence on visible load frames 2026-05-16 04:16:12 -04:00
ZacharyZhang-NY 168c60bf2a perf(app): settle web poll cadence after load 2026-05-16 04:13:20 -04:00
ZacharyZhang-NY 99d54a7089 perf(app): idle repeated terminal web states 2026-05-16 04:06:41 -04:00
ZacharyZhang-NY 372c29f2c3 perf(app): stop extending loading poll bursts 2026-05-16 04:01:43 -04:00
ZacharyZhang-NY 47eaea28a2 fix(sync): honor paused object policies 2026-05-16 03:53:42 -04:00
ZacharyZhang-NY 8c2dffddc7 fix(sync): run initial upload for signed-in profiles 2026-05-16 03:50:16 -04:00
ZacharyZhang-NY d05d31ed22 feat(sync): include open tabs in snapshots 2026-05-16 03:44:07 -04:00
ZacharyZhang-NY d078ca5247 fix(sidecar): flush metadata changes to live clients 2026-05-16 03:32:29 -04:00
ZacharyZhang-NY 93c2b3636c perf(app): skip iosurface scans on ui thread 2026-05-16 03:25:35 -04:00
ZacharyZhang-NY 54fcce29f3 perf(app): gate payloadless surfaces on app imports 2026-05-16 03:17:45 -04:00
ZacharyZhang-NY dbfcbdb377 perf(sidecar): use payloadless hardware frames after warmup 2026-05-16 02:50:11 -04:00
ZacharyZhang-NY bd59e74ad2 perf(shell): trim render snapshot work 2026-05-16 01:52:19 -04:00
ZacharyZhang-NY cfef7479db perf(shell): render from one browser snapshot 2026-05-16 01:45:38 -04:00
ZacharyZhang-NY e442e6def4 perf(web-surface): avoid snapshot clones on live ticks 2026-05-16 01:41:42 -04:00
ZacharyZhang-NY 42dc9af66c perf(sidecar): skip redundant paint barriers 2026-05-16 01:25:37 -04:00
ZacharyZhang-NY 8ad85ce7f7 perf(sidecar): report exact live frame percentiles 2026-05-16 01:16:22 -04:00
ZacharyZhang-NY c6de666d1c test(sidecar): align live perf bench 2026-05-16 01:12:10 -04:00
ZacharyZhang-NY 5f49786c6e refactor(sidecar): split live session state 2026-05-16 01:07:50 -04:00
ZacharyZhang-NY 234bf4c8a5 fix(web-surface): bound live input paints 2026-05-16 01:02:38 -04:00
ZacharyZhang-NY 8841d74ac5 test(web-surface): lock active cadence to 120hz 2026-05-16 00:22:10 -04:00
ZacharyZhang-NY 319b32b2de feat(sync-ui): animate sync controls 2026-05-16 00:19:59 -04:00
ZacharyZhang-NY 42740842b6 feat(chrome): animate high-frequency actions 2026-05-16 00:15:33 -04:00
ZacharyZhang-NY 40ab6b4874 feat(sync): round trip cloud snapshots 2026-05-16 00:01:28 -04:00
ZacharyZhang-NY 06e947f43e perf(web-surface): adapt shell tick cadence 2026-05-15 23:42:48 -04:00
ZacharyZhang-NY 225a998642 fix(tabs): refresh metadata on navigation 2026-05-15 23:31:12 -04:00
ZacharyZhang-NY ca8f118785 perf(web-surface): throttle idle Servo polling 2026-05-15 23:25:09 -04:00
ZacharyZhang-NY 736ff08e90 Fix dead traffic-light hit area under FullSizeContentViewWindowMask
`move_traffic_light` was framing the close / minimize / zoom NSButtons
at `titlebar_height - traffic_light_position.y - button_h`. Once the
window opts into `NSFullSizeContentViewWindowMask`, the content layout
rectangle covers the whole frame and `titlebar_height()` returns 0 —
collapsing the origin to a large negative Y. macOS still painted the
buttons through its own caching layer at the title bar's natural
position, but the buttons' hit-test rectangle followed the frame off
screen, so clicking close / minimize / zoom did nothing.

Anchor the math against the close button's actual superview frame
height (the themeFrame, which equals the window height in Y-up
coordinates) so the visual position and the hit area stay in lockstep
no matter what mask combination the window opens with. `titlebar_height`
stays available for callers that want the legacy non-full-size
interpretation, with `#[allow(dead_code)]` and a docstring pointing at
this fix.
2026-05-15 23:12:43 -04:00
ZacharyZhang-NY a6620fa265 Fold the workspace picker into one pill and widen the traffic-light reserve
Drops the separate dashboard tile and the top-level "+" button so the
sidebar's first row holds a single workspace identity. "+ New
workspace" lives inside the disclosure popover next to the existing
spaces list, matching that row's geometry instead of inventing a new
square tile.

The previous 76 px traffic-light reserve only cleared the buttons
when the row also carried the tile and "+" — with the pill alone
plus its drop-shadow, it slid back behind the green button on the
default sidebar width. Widen `TRAFFIC_LIGHT_RESERVE` to 100 px and
document the derivation: pill outer left = `29 + RESERVE`, traffic-
light group right edge ≈ 112 px, so 100 leaves 17 px of breathing
room. Anchor solver and tests track the new geometry.
2026-05-15 23:02:53 -04:00
ZacharyZhang-NY 1c72622db8 Resolve favicons through Google's s2 service
Pointing the renderer at the site's own `/favicon.ico` was producing
GPUI image-cache errors on every other tab — notion.com redirected
across origins, sites shipped multi-image `image/x-icon` blobs the
PNG/WebP decoder couldn't read, hosts 404'd. Each one logged a noisy
`ERROR gpui::asset_cache: Failed to load asset` line.

Switch `UrlText::favicon_url` to
`https://www.google.com/s2/favicons?domain=<host>&sz=64`. Google's
endpoint normalises every response to PNG, follows redirects on its
side, and serves a `_/` globe glyph for sites without a favicon at
all — same URL shape every browser dev-tools panel already shows
for "favicon" so the fetch is uniformly succeeding.
2026-05-15 21:59:30 -04:00
ZacharyZhang-NY 1e480a9f87 Update OTP test to match the new browser@elydora.com sender
The wrangler.toml + better_auth.ts move from auth@ to browser@elydora.com
left this assertion behind, so `npm test` failed pre-deploy. Realign
the expected `from` field and the worker's test suite is green again.
2026-05-15 21:45:29 -04:00
ZacharyZhang-NY 74b3de54ed Add email + OTP sign-in flow for cloud sync
Replace the "drop a session token in a file" workflow with a real
Chrome-style email login. The Cloudflare worker already had Better
Auth's `email-otp` plugin wired into `SEND_EMAIL`; this commit
builds the renderer-side counterpart.

Worker side:
- Move the OTP sender from `auth@elydora.com` to `browser@elydora.com`
  (wrangler.toml `allowed_sender_addresses` + better_auth.ts
  `EMAIL_OTP_FROM_ADDRESS`). Worker must be redeployed to pick this up.

Client side (`ely_sync_client::email_otp`):
- `send_email_otp(config, email)` POSTs `/api/auth/email-otp/send-verification-otp`
  with `{ email, type: "sign-in" }`.
- `verify_email_otp(config, email, otp)` POSTs `/api/auth/sign-in/email-otp`,
  reads the Better Auth session token from the JSON body's `token` field
  with the `Set-Cookie: better-auth.session_token=…` header as the
  documented fallback channel, and returns it as a `BearerToken`.

Shell side (`shell/auth.rs` + `shell/internal_pages/sync.rs`):
- New `AuthFlowPhase` (Idle / SendingCode / AwaitingOtp / Verifying /
  Error) tracks the in-flight form. Two off-thread workers run the
  HTTP exchanges so the GPUI render loop never blocks.
- Successful verify saves the bearer via `SyncEngine::install_bearer`
  and triggers an immediate snapshot upload, so the user is signed in
  + initial-synced in one click.
- Sync settings page replaces the bare "Sync now" button row with an
  account card: when SignedOut → email field + Send code → OTP field
  + Verify / Resend; when signed in → an account chip + Sign out.
- `trigger_cloud_sync_upload` no longer takes a `Context` param so
  the post-auth path can fire it from the inbox-drain pass without
  needing a window context.
2026-05-15 21:32:50 -04:00
ZacharyZhang-NY 80cff6dad3 Stop the web surface from strobing on transient errors
Two paths in `WebSurfaceStore::tick` were downgrading a perfectly good
Ready frame back to Failed / Loading on every transient hiccup:

- `WebSurfaceRuntimeFrame::Failed` overwrote the surface state
  unconditionally. Combined with the 125 Hz tick (which submits a Poll
  for every visible tab and any transient ensure / poll error becomes
  a Failed response), even one parse glitch wiped the rendered page.
- The `initial_display_gate_message` and `should_hold_initial_frame`
  checks for incoming Ready frames hardcoded `has_previous_frame =
  false`, so a stray empty paint pass after the page had already
  rendered would knock the surface back to Loading or Failed.

Detect whether the surface already has a `Ready(_)` state and:
- ignore Failed responses (logging through `tracing` for diagnostics)
  while a real frame is on screen;
- pass that "had ready" signal into the gate / hold checks so they only
  apply to the very first paint, not later refreshes.

The page now stays put even when Servo emits a momentary error; only
the first-paint failure path can mark the surface Failed.
2026-05-15 21:21:14 -04:00
ZacharyZhang-NY 838f80006a Remove broken sidebar Layout cards from appearance settings
The three layout preset cards (Single column / Compact / Hidden on
hover) painted as bright white boxes regardless of theme, never
implemented the layouts they previewed, and duplicated the live
sidebar resize handle which already covers the same intent. Drop
the section, the supporting `appearance_layout_cards` module, and
the chrome `mod` declaration that re-exported it.
2026-05-15 21:18:10 -04:00