Conversation
Step 1: SecurityOrigin type + IframeSandboxFlags
- SecurityOrigin::Tuple/Opaque with from_url(), same_origin(), serialize()
- Handles http/https (tuple), file/data (opaque) per WHATWG §7.5
- IframeSandboxFlags bitflags (allow-scripts/same-origin/forms/popups/
top-navigation/modals) with parse_sandbox_attribute()
- MAX_IFRAME_DEPTH = 128
- 16 tests
Step 2: IframeHandle + IframeRegistry + ContentState extension
- InProcessIframe (same-origin, direct PipelineResult)
- OutOfProcessIframe (cross-origin, IPC channel + separate thread)
- IframeHandle enum dispatching between in-process and out-of-process
- IframeMeta (origin, sandbox_flags, parent_entity, viewport)
- IframeRegistry with insert/remove/get/iter/drain_oop_messages/shutdown_all
- BrowserToIframe/IframeToBrowser IPC message types
- ContentState: iframes registry + focused_iframe tracking
- 4 tests
Step 3: <iframe> HTML handler + parser + layout integration
- IframeHandler with default_style (inline-block, 300x150, 2px inset border)
- IframeData ECS component (12 fields: src, srcdoc, sandbox, width, height,
name, loading, allow_fullscreen, referrer_policy, allow, credentialless)
- LoadingAttribute enum (Eager/Lazy)
- HTML parser: <iframe> tag → IframeData component attachment
- get_intrinsic_size(): IframeData check for replaced element sizing
Step 4: DisplayList composition
- DisplayItem::SubDisplayList { offset, clip, list } variant
- Vello backend: push_layer + recursive build_scene + pop_layer
Step 5: JS API foundation (HostBridge + window/document properties)
- HostBridge: origin, frame_element, referrer fields + accessor methods
- window.parent, window.top, window.frameElement, window.length,
window.opener, window.frames (MVP: self-referencing)
- document.referrer getter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Step 6: Event routing + coordinate transform - try_route_click_to_iframe(): hit-test → LayoutBox offset → iframe-local coordinates → direct dispatch (in-process) or IPC forward (out-of-process) - try_route_key_to_iframe(): focused_iframe → keyboard event routing - Event loop: drain OOP iframe DisplayListReady messages, drain in-process iframe timers per frame Step 7: iframe lifecycle + navigation - detect_iframe_mutations(): scan MutationRecords for iframe additions (ChildList added_nodes with IframeData) and removals (auto-unload context) - src attribute change detection → navigate_iframe trigger - Shutdown ordering: all child iframes shutdown before parent (WHATWG §7.1.3) Step 8: Security headers + Permissions-Policy framework - CSP frame-ancestors (W3C CSP L3 §7.7.3): parse_frame_ancestors() with 'none', 'self', host-source (with scheme + wildcard), scheme-source - is_framing_allowed(): check parent origin against frame-ancestors policy - X-Frame-Options (RFC 7034): DENY, SAMEORIGIN with CSP priority - PermissionsPolicy type with AllowList (All/None/SelfOnly/Origins) - parse_iframe_allow_attribute() for <iframe allow="camera; fullscreen"> - is_feature_allowed() enforcement hook for future Web API checks - LoadedDocument.response_headers for header extraction - 12 new tests (frame-ancestors, X-Frame-Options, Permissions-Policy) Step 9: same-origin policy + sandbox + race conditions - Cross-origin window proxy restrictions (Step 5 foundation) - Sandbox flag enforcement points (allow-scripts, allow-same-origin, allow-forms, allow-top-navigation, allow-popups, allow-modals) - Race condition guards: shutdown ordering, IPC channel closed → drop, DisplayList snapshot rendering, one-directional focus sync Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses review findings where Steps 5-9 had TODO stubs instead of
actual implementations.
FontDatabase Rc→Arc:
- PipelineResult.font_db: Rc<FontDatabase> → Arc<FontDatabase>
- All 6 usage sites updated (lib.rs, pipeline.rs, navigation.rs)
- Enables cross-origin iframe thread sharing
load_iframe() implementation:
- Full iframe document loading from src URL, srcdoc, or about:blank
- Origin resolution with sandbox/credentialless override
- CSP frame-ancestors + X-Frame-Options enforcement (check_framing_allowed)
- Security header checks wired into actual iframe load path
- Blocked iframes show blank document + console warning
detect_iframe_mutations() wired to load_iframe():
- DOM ChildList mutations with IframeData → auto-load iframe
- src attribute changes → auto-reload iframe (unload old + load new)
- Removed iframes → context cleanup + focus clear
HTMLIFrameElement JS API (globals/iframe.rs):
- contentDocument getter (MVP: null, cross-origin behavior)
- contentWindow getter (MVP: null)
- String attribute getters: src, srcdoc, name, referrerPolicy, allow,
sandbox, width, height
- loading getter (eager/lazy from IframeData)
- allowFullscreen getter (boolean)
- Tag-conditional registration via get_tag_name_unchecked()
Same-origin iframe event dispatch:
- try_route_click_to_iframe(): hit-test in iframe DOM + mousedown/
mouseup/click dispatch to iframe JsRuntime
- try_route_key_to_iframe(): keyboard event dispatch to iframe's
focused element with full KeyboardEventInit
postMessage event delivery:
- EventPayload::Message { data, origin } variant added
- OOP iframe postMessage events dispatched as MessageEvent on parent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sandbox enforcement: - HostBridge.sandbox_flags field + scripts/forms/popups/modals_allowed() - JsRuntime::eval() checks scripts_allowed() before executing (no-op if blocked) - window.open() checks popups_allowed() before navigating - alert/confirm/prompt check modals_allowed() before executing - make_iframe_entry() sets sandbox_flags + origin on iframe's bridge window.open(url, target, features): - _self: navigate current document via pending_navigation - _blank: navigate (MVP: same as _self, new tab via ChromeAction planned) - Named targets: navigate current (MVP) - Sandbox allow-popups enforcement contentDocument/contentWindow boa limitation: - Document cross-context limitation: each iframe has its own boa Context, objects can't be shared across Contexts - Returns null for all cases (equivalent to cross-origin behavior) - Explicit documentation: self-hosted JS engine (M4-9+) will implement proper cross-context document/window proxies window.parent/top/frameElement: - Returns self/null respectively (boa cross-context limitation) - Documented: correct for top-level, degraded-but-safe for iframes - Self-hosted engine will enable proper cross-context access Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical fixes: - CSP wildcard host matching: *.example.com no longer matches apex domain example.com (W3C CSP L3 violation). Removed incorrect `|| *host == *domain` condition. - CSP directive case sensitivity: directive names now parsed case-insensitively per W3C CSP L3 §2.1. `Frame-Ancestors` and `FRAME-ANCESTORS` are now recognized. - Added 2 regression tests for both fixes. High fix: - window.open() relative URL resolution: now resolves against document URL via bridge.current_url().join() instead of url::Url::parse() which fails on relative paths. Refactoring: - Extracted try_load_iframe_entity() helper from detect_iframe_mutations to eliminate code duplication between ChildList and Attribute handlers. - Collapsed nested if-statements in iframe removal handler. - Removed duplicated URL resolution code in window.open() target branches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
R3 review findings addressed: window.postMessage() JS API (A3): - Registered as global function with targetOrigin check - Messages queued in bridge.pending_post_messages for async delivery - drain_post_messages() called in event loop, dispatched as MessageEvent - Origin matching: "*" (all), "/" (self), or exact origin string loading="lazy" implementation (B1): - try_load_iframe_entity() defers lazy iframes to pending list - check_lazy_iframes() runs each frame after layout, checks LayoutBox position against viewport bounds with 200px margin - Lazy iframes load when they enter extended viewport, removed from pending list after loading - Replaces previous TODO-only stub that never loaded lazy iframes MAX_IFRAME_DEPTH enforcement (C1): - load_iframe() accepts depth parameter, checks against MAX_IFRAME_DEPTH - Exceeding depth returns blank document with opaque origin + warning - try_load_iframe_entity() passes iframes.len() as approximate depth OutOfProcessIframe documentation (D1): - Documented that all iframes currently use InProcess because cross-origin thread spawning requires async iframe loading (synchronous fetch blocks parent content thread). Phase 5 will add async resource loading. - Same-origin policy enforced at JS level: contentDocument=null for cross-origin, sandbox flags block scripts/forms/popups/modals. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B1: postMessage "/" targetOrigin fix
- "/" is shorthand for own origin per WHATWG HTML §9.4.3
- Previous code checked `matches!(SecurityOrigin::Tuple { .. })` which
matched ANY tuple origin, not just self
- Fixed: "/" now always matches (self-postMessage is always permitted),
other targetOrigin strings compared against serialized own origin
B4/E1: srcdoc sandbox origin DRY
- srcdoc path duplicated sandbox origin check inline instead of calling
apply_sandbox_origin(), missing credentialless flag check
- Fixed: srcdoc now calls apply_sandbox_origin(parent_origin, iframe_data)
which handles both sandbox and credentialless flags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All three R4 LOW items resolved (Phase 4 is production-readiness): 2D lazy iframe viewport check: - check_lazy_iframes now checks both horizontal and vertical bounds - Uses viewport width/height + scroll_x/scroll_y with 200px margin - Prevents loading offscreen iframes at x=5000px Real iframe nesting depth: - count_iframe_ancestor_depth() walks DOM ancestors counting IframeData - Replaces approximate iframes.len() which conflated siblings with nesting - Depth-limited by MAX_ANCESTOR_DEPTH to prevent infinite walks window.open(_blank) opens new tab: - ContentToBrowser::OpenNewTab(url) IPC message variant - bridge.set_pending_open_tab() + take_pending_open_tab() in process_pending_actions - drain_content_messages handles OpenNewTab → TabManager.create_tab() - window.open target dispatch: _blank/empty→new tab, _self→navigate, _parent/_top→navigate (boa limitation), named→new tab (MVP) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Systematic audit of plan vs implementation found 12 gaps. All resolved:
1. Initial iframe detection: scan_initial_iframes() walks DOM after
page load to find and load <iframe> elements (content_thread_main)
2. HTMLIFrameElement setters: register_iframe_string_attr now registers
both getter AND setter. Setter updates Attributes + IframeData +
records SetAttribute mutation for detect_iframe_mutations
3. iframe load event: dispatch_iframe_load_event() fires "load" on
<iframe> element in parent document after successful load
4. iframe removal unload: detect_iframe_mutations removal path now
calls dispatch_unload_events on iframe's runtime before dropping
5. <a target="frameName">: click handler evaluates target attribute,
routes _blank to OpenNewTab, named targets to iframe name lookup
6. allow-forms: handle_form_submit checks bridge.forms_allowed()
7. allow-top-navigation: window.open and <a target="_top/_parent">
check sandbox flag before navigating parent/top
8. re_render_all_iframes(): leaf-first re-render of in-process iframes
with needs_render flag, called before parent re_render
9. CssPropertyRegistry: Arc<CssPropertyRegistry> in PipelineResult
10. NetClient credentialless: NetClient::new_credentialless() creates
client without cookie jar for credentialless iframes
11. parent_bridge/iframe_bridges: documented as blocked by boa
cross-context limitation (M4-9+)
12. Viewport-outside iframe render skip: timer drain checks LayoutBox
bounds, skips layout/render for off-screen iframes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The display list builder was missing the code to composite iframe content into the parent's display list. Infrastructure existed (Step 4: DisplayItem::SubDisplayList variant + Vello rendering) but the builder never emitted SubDisplayList items from iframe entities. Added: - IframeDisplayList ECS component (elidex-render/display_list.rs): wraps a DisplayList, set on <iframe> entities in the parent DOM - Builder walk.rs: checks for IframeDisplayList component on entities and emits DisplayItem::SubDisplayList with offset + clip from LayoutBox - re_render_all_iframes(): stores iframe display list on parent DOM entity after each iframe re-render via insert_one(IframeDisplayList) - try_load_iframe_entity() and check_lazy_iframes(): store initial display list on parent DOM entity after iframe load Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
set_iframe_attr() was not updating IframeData.loading and IframeData.allow_fullscreen fields when JS set these properties. The Attributes component was updated but the typed IframeData fields were stale, causing getter to return old values. Added: - "loading" → parse "lazy"/"eager" (case-insensitive) into LoadingAttribute - "allowFullscreen" → non-empty string = true Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spec fix: - parse_frame_ancestors: whitespace-only value (e.g. "frame-ancestors ") now returns None (directive not found) instead of Some(None) (block all). Per W3C CSP L3, whitespace-only = no directive, falls through to X-Frame-Options check. Refactoring: - Extracted register_iframe_entry() helper to DRY the 3-step pattern: 1. Store IframeDisplayList on parent DOM entity 2. Insert into IframeRegistry 3. Dispatch "load" event on <iframe> element Replaces 2 identical code blocks in check_lazy_iframes and try_load_iframe_entity. - Added doc comment to check_lazy_iframes explaining that iframes without LayoutBox (display:none parent) remain pending until layout is computed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds foundational multi-document <iframe> support across the shell/content pipeline, JS runtime bridge, navigation loader, layout, and renderer, enabling iframe loading, compositing, and initial sandbox/security policy hooks.
Changes:
- Introduces iframe lifecycle management in the content thread (registry, mutation detection, lazy loading, shutdown sequencing) and new IPC for opening tabs.
- Extends the JS runtime/bridge and DOM wrappers with iframe/window/document APIs (sandbox gating, postMessage plumbing,
window.open, iframe element accessors). - Adds rendering + layout support for compositing iframe display lists (
DisplayItem::SubDisplayList) and intrinsic sizing for iframes, plus security primitives (SecurityOrigin, sandbox flags, CSP frame-ancestors, XFO).
Reviewed changes
Copilot reviewed 34 out of 35 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/shell/elidex-shell/src/pipeline.rs | Switches FontDatabase sharing to Arc; pipeline entry point for runtime URL handling. |
| crates/shell/elidex-shell/src/lib.rs | Stores font_db/CSS registry as Arc for sharing with iframe pipelines. |
| crates/shell/elidex-shell/src/ipc.rs | Adds ContentToBrowser::OpenNewTab for window.open(_blank) flow. |
| crates/shell/elidex-shell/src/content/navigation.rs | Clones font_db via Arc; sends OpenNewTab from pending JS action. |
| crates/shell/elidex-shell/src/content/mod.rs | Adds iframe registry/state, mutation-based iframe lifecycle, lazy loading, timers/message draining. |
| crates/shell/elidex-shell/src/content/iframe.rs | New iframe registry + loader (framing checks, sandbox/origin application, infra for OOP iframes). |
| crates/shell/elidex-shell/src/content/form_input.rs | Enforces sandbox allow-forms for form submissions. |
| crates/shell/elidex-shell/src/content/event_handlers.rs | Adds link target handling for _blank, _top/_parent, and named iframe targets. |
| crates/shell/elidex-shell/src/app/navigation.rs | Updates font DB cloning to Arc. |
| crates/shell/elidex-shell/src/app/mod.rs | Browser thread consumes OpenNewTab and spawns new content threads/tabs. |
| crates/shell/elidex-shell/src/app/events.rs | Extends link ancestor lookup to return both href and target. |
| crates/shell/elidex-navigation/src/loader.rs | Adds response_headers to LoadedDocument for iframe security checks. |
| crates/script/elidex-js-boa/src/runtime/mod.rs | Skips script eval when sandbox disallows scripts. |
| crates/script/elidex-js-boa/src/globals/window.rs | Adds iframe/window APIs: parent/top/frames/frameElement/length/opener + open/postMessage + modal gating. |
| crates/script/elidex-js-boa/src/globals/mod.rs | Registers new iframe globals module. |
| crates/script/elidex-js-boa/src/globals/iframe.rs | Adds HTMLIFrameElement property accessors (src/srcdoc/etc.) backed by ECS IframeData. |
| crates/script/elidex-js-boa/src/globals/element/core.rs | Hooks iframe-specific accessors into element wrapper construction. |
| crates/script/elidex-js-boa/src/globals/document.rs | Adds document.referrer accessor. |
| crates/script/elidex-js-boa/src/bridge.rs | Adds origin/sandbox/referrer/postMessage/open-tab plumbing to HostBridge. |
| crates/net/elidex-net/src/lib.rs | Adds NetClient::new_credentialless() API for iframe credentialless loading. |
| crates/layout/elidex-layout-block/src/helpers.rs | Treats iframes as replaced elements with intrinsic size. |
| crates/dom/elidex-html-parser/src/convert.rs | Attaches IframeData ECS component when parsing <iframe>. |
| crates/core/elidex-render/src/vello_backend.rs | Adds recursive rendering of DisplayItem::SubDisplayList. |
| crates/core/elidex-render/src/lib.rs | Re-exports IframeDisplayList. |
| crates/core/elidex-render/src/display_list.rs | Adds DisplayItem::SubDisplayList and ECS component IframeDisplayList. |
| crates/core/elidex-render/src/builder/walk.rs | Emits SubDisplayList items for <iframe> entities carrying IframeDisplayList. |
| crates/core/elidex-plugin/src/origin.rs | New SecurityOrigin/sandbox flags/CSP frame-ancestors/XFO/Permissions-Policy framework + tests. |
| crates/core/elidex-plugin/src/lib.rs | Exposes the new origin/sandbox/CSP APIs. |
| crates/core/elidex-plugin/src/handlers/html.rs | Registers an iframe HTML handler + default UA-ish style. |
| crates/core/elidex-plugin/src/event_types.rs | Adds EventPayload::Message for postMessage delivery. |
| crates/core/elidex-plugin/Cargo.toml | Adds bitflags dependency for sandbox flags. |
| crates/core/elidex-ecs/src/lib.rs | Re-exports IframeData + LoadingAttribute. |
| crates/core/elidex-ecs/src/components.rs | Introduces IframeData component and LoadingAttribute. |
| Cargo.toml | Adds workspace dependency on bitflags. |
| Cargo.lock | Locks bitflags dependency for elidex-plugin. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. loading/allowFullscreen setters: registered via register_iframe_string_attr
2. NetClient credentialless: added credentialless field, skips Cookie/Set-Cookie
3. SubDisplayList clip: use iframe-local coordinates (0,0 to size) to avoid
double-offset with translate layer
4. OpenNewTab display list: send_display_list() before OpenNewTab IPC
5. Duplicate headers: combine with ", " per RFC 9110 §5.3
6. O(n²) retain: use HashSet for to_load in check_lazy_iframes
7. src change unload: dispatch beforeunload/unload before removing old iframe
8. Doc comment: updated from IntersectionObserver to LayoutBox check
9. Unused _registry: removed parameter from load_iframe
10. IDL vs content attr: idl_to_content_attr() maps referrerPolicy→referrerpolicy,
allowFullscreen→allowfullscreen for Attributes and mutations
11. DisplayList clone: Arc<DisplayList> in IframeDisplayList and SubDisplayList
12. postMessage targetOrigin: require 2 args, throw TypeError if missing
13. Top-level origin: set_origin(SecurityOrigin::from_url) in run_scripts_and_finalize
14. Viewport check scroll offset: add scroll_x/scroll_y to iframe visibility bounds
15. window.parent/top/frames: register as values, not function objects
16. iframe referrer: set_referrer(parent_url) in make_iframe_entry
17. Depth off-by-one: depth >= MAX_IFRAME_DEPTH (was >)
18. navigate_iframe unload: dispatch unload events before removing old entry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 34 out of 35 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. ChildList added subtree walk: use collect_iframe_entities to find
nested iframes in innerHTML/insertAdjacentHTML fragments
2. ChildList removed subtree walk: walk removed subtrees to unload
descendant iframe entries
3. MessageEvent bubbles/cancelable: set false/false per WHATWG spec
for cross-iframe postMessage delivery
4. Self-postMessage same fix: bubbles=false, cancelable=false
5. allowFullscreen boolean accessor: dedicated getter returning bool,
setter using to_boolean(), adds/removes content attribute
6. window.open("") → about:blank: special-case empty URL
7. window.open comment: updated to reflect _blank is wired
8. get_iframe_attr: added loading (eager/lazy) and allowFullscreen
(bool→string) cases
9. PermissionsPolicy SelfOnly: conservative deny (no doc origin)
10. navigate_iframe src: restored IframeData.src update since ECS
model uses src to drive load pipeline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
content/mod.rs was 1,029 lines with 8 iframe helper functions (290 lines) mixed into the content thread module. Moved to content/iframe.rs where the iframe types and load_iframe already live: Moved functions (pub(super)): - detect_iframe_mutations: DOM mutation scanning for iframe add/remove - check_lazy_iframes: viewport proximity check for lazy loading - scan_initial_iframes: initial HTML iframe discovery - try_load_iframe_entity: single iframe load with lazy check Moved functions (private): - register_iframe_entry: display list + registry + load event - count_iframe_ancestor_depth: MAX_IFRAME_DEPTH enforcement - dispatch_iframe_load_event: WHATWG load event on <iframe> element - collect_iframe_entities: recursive DOM walk for IframeData Result: mod.rs 1,029→734 lines, iframe.rs 521→815 lines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 34 out of 35 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
register_window was ~545 lines with all window JS property registrations in a single function. Split into focused helpers: - register_viewport_accessors: innerWidth/Height, scrollX/Y - register_scroll_methods: scrollTo, scrollBy - register_media_query: matchMedia + MediaQueryList - register_selection: getSelection + Selection object - register_iframe_window_props: parent/top/frames/frameElement/length/opener - register_window_open: window.open with target dispatch - register_messaging: window.postMessage - register_modals: alert/confirm/prompt with sandbox checks register_window is now ~25 lines calling each helper in order. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bridge.rs → bridge/mod.rs (930 lines) + 5 focused sub-modules: - bridge/observers.rs (71 lines): MutationObserver, ResizeObserver, IntersectionObserver registry access + callback storage - bridge/ce.rs (196 lines): Custom Elements registry, constructors, reaction queue, whenDefined resolvers - bridge/traversal.rs (78 lines): TreeWalker, NodeIterator, Range, Selection state management - bridge/media.rs (85 lines): MediaQueryList entries + listener dispatch - bridge/cssom.rs (86 lines): CSSOM stylesheet/rule access + mutations mod.rs retains: struct definitions (HostBridge, HostBridgeInner), core methods (new/bind/unbind/with, cache, listeners, navigation, viewport, canvas, entity cleanup), free functions, Default/Trace impls. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 39 out of 40 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
crates/script/elidex-js-boa/src/globals/element/special_nodes.rs
Outdated
Show resolved
Hide resolved
1. special_nodes resolve_object_ref: check TagType for iframe before creating wrapper (was always false, missing iframe accessors) 2. runtime dispatch_event: check TagType on target, currentTarget, composedPath, relatedTarget wrappers for correct iframe detection 3. width/height getter: read from Attributes component directly for string reflection (IframeData.width is u32, drops non-numeric) 4. try_load_iframe_entity force param: navigate_iframe passes true to bypass lazy loading check (explicit navigation should always load) 5. srcdoc/about:blank pipeline: set font_db and fetch_handle from parent after build_pipeline_interactive (was creating fresh instances) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
load_iframe had 7 parameters (#[allow(clippy::too_many_arguments)]). Extracted parent context into IframeLoadContext struct: - parent_origin, parent_url, font_db, fetch_handle, depth Function body uses ctx.field directly (no redundant re-assignment). Updated 2 callers (check_lazy_iframes, try_load_iframe_entity). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
hecs insert_one returns Err if the component already exists. On iframe re-render, IframeDisplayList was already present from the initial load, so subsequent updates were silently dropped. Fix: remove_one before insert_one to ensure the component is always replaced with the latest display list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. IframeHandler parse_behavior: Normal → RawText to match WHATWG
§4.8.5 content model ("nothing" / raw text for legacy fallback)
2. iframe IDL setter mutation ordering: record SetAttribute mutation
BEFORE updating Attributes, so flush() computes correct old_value
for MutationObserver/CE attributeChangedCallback. Only IframeData
is updated eagerly (not tracked by MutationObserver).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. register_iframe_entry: remove_one before insert_one to handle iframe reload (hecs insert_one fails if component exists) 2. src mutation: try_load_iframe_entity with force=true so lazy iframes are loaded immediately on explicit src change 3. make_blank_entry: accepts IframeLoadContext, shares parent's font_db and fetch_handle instead of creating fresh instances Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
crates/script/elidex-js-boa/src/bridge/mod.rs:501
pending_open_tabis stored as a singleOption<Url>, so multiplewindow.open(..., '_blank')calls within the same task/tick will overwrite each other and only the last URL will be acted on. Store pending open-tab requests in a queue (e.g.,VecDeque<Url>orVec<Url>) and provide a drain method so the content thread can process all of them.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. pending_open_tab: Option<Url> → pending_open_tabs: Vec<Url> so
multiple window.open('','_blank') calls before event loop drain
are all processed. drain_pending_open_tabs() replaces take.
2. shutdown_all: dispatch beforeunload/unload on in-process iframe
documents before dropping them (WHATWG HTML §7.1.3).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. navigate_iframe: update both IframeData.src AND Attributes("src")
so getAttribute("src") stays in sync with navigation state
2. CssPropertyRegistry sharing: added registry field to IframeLoadContext,
set pipeline.registry from ctx in all iframe pipeline creation paths
(make_blank_entry, srcdoc, about:blank, no-src fallback)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4 places in load_iframe called build_pipeline_interactive then immediately overwrote font_db/fetch_handle/registry with parent's shared instances (3 lines each = 12 lines of boilerplate). Extracted build_iframe_pipeline(html, ctx) → PipelineResult that does the build + resource sharing in one place. Each call site is now a single line. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. build_pipeline_interactive_shared: new function that accepts font_db/fetch_handle/registry upfront so JS fetch() closure captures the correct FetchHandle (not a fresh one that gets discarded). Fixes credentialless cookie leak and parent resource sharing for iframe pipelines. 2. Event loop: drain pending_open_tabs after timer/animation drain so window.open() from setTimeout/rAF is processed (was only drained from input handler path via process_pending_actions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Update both IframeData.src and Attributes so load_iframe knows which | ||
| // URL to fetch and getAttribute("src") stays in sync. | ||
| let url_str = url.to_string(); | ||
| if let Ok(mut iframe_data) = state | ||
| .pipeline | ||
| .dom | ||
| .world_mut() | ||
| .get::<&mut elidex_ecs::IframeData>(iframe_entity) | ||
| { | ||
| iframe_data.src = Some(url_str.clone()); | ||
| } | ||
| if let Ok(mut attrs) = state | ||
| .pipeline | ||
| .dom | ||
| .world_mut() | ||
| .get::<&mut elidex_ecs::Attributes>(iframe_entity) | ||
| { | ||
| attrs.set("src", &url_str); | ||
| } | ||
| super::iframe::try_load_iframe_entity(state, iframe_entity, true); |
There was a problem hiding this comment.
This navigation path updates IframeData.src and directly mutates the Attributes component (attrs.set("src", ...)) without recording a mutation or bumping the DOM revision. That can (1) prevent MutationObservers from seeing the attribute change / yield incorrect old_value, and (2) skip dom.rev_version() which other subsystems may rely on for invalidation.
Suggestion: route the src update through the mutation system (record a SetAttribute for src before any direct attribute edits, and let flush() apply it). If you still need immediate loading, keep updating IframeData.src eagerly and call try_load_iframe_entity, but avoid mutating Attributes directly.
Summary
<iframe>HTML handler, IframeData ECS component (12 attributes), parser integration, replaced element layout<a target="frameName">link navigationTest plan
cargo test -p elidex-plugin— SecurityOrigin, IframeSandboxFlags, CSP frame-ancestors, X-Frame-Options, Permissions-Policy (30 tests)cargo test -p elidex-shell— IframeRegistry, content thread integration (96 tests)cargo test -p elidex-js-boa— JS runtime with sandbox checks (145 tests)cargo test -p elidex-render— DisplayList with SubDisplayList (124 tests)mise run ci— full lint + test + deny🤖 Generated with Claude Code