Unlike many "browser in browser" solutions that use server-side rendering or proxies, Vortex runs entirely client-side using standard web technologies pushed to their limits via specific Extension APIs.
- Container: A standard React Single Page App (SPA) acting as the "Operating System".
- Window Manager:
React Flowhandles the spatial coordinate system (x, y, zoom), occlusion culling (virtualization), and event delegation. - Sandboxing: Each "Node" is a standard HTML
<iframe>. Use of iframes provides process isolation (handled by the browser's site isolation policy). - Bridge: The
content-script.jsacts as the IPC (Inter-Process Communication) bridge between the "Guest" (iframe) and "Host" (Vortex App).
When a user clicks a link in Node A (
-
Event Capture:
content-script.jsusesclickevent capturing (useCapture: true) to intercept the event before the DOM processes it. -
Message Passing: A Chrome Runtime Message is sent:
{ "type": "LINK_CLICK", "url": "https://example.com", "sourceNodeId": "node-123" } -
Position Calculation: The Host receives this. It finds
$N_a$ 's position$(x_a, y_a)$ .-
Naive Algorithm (Current):
$P_{new} = (x_a + \text{offset}, y_a)$ . - Implication: Creates a horizontal chain.
- Optimization: A force-directed layout or tree-packing algorithm would be safer to avoid overlap, but current naive approach ensures predictable "reading order".
-
Naive Algorithm (Current):
This is the most critical and technically "aggressive" part of the stack.
Sites send X-Frame-Options: DENY or SAMEORIGIN. Browsers respect this by refusing to render the body of the response in an iframe.
We use the declarativeNetRequest API (Manifest V3) to strip headers at the network layer, before the renderer process sees them.
{
"id": 1,
"action": {
"type": "modifyHeaders",
"responseHeaders": [
{ "header": "x-frame-options", "operation": "remove" },
{ "header": "content-security-policy", "operation": "remove" }
]
},
"condition": { "resourceTypes": ["sub_frame"] }
}- Result: The browser renderer sees a response with NO security restrictions regarding embedding.
- Risk: This technically disables Clickjacking protection for these sites within the context of the extension.
- Issue: Many sites (e.g., Google, GitHub) set cookies with
SameSite=LaxorStrict. - Effect: When embedded in an iframe (even if we force it to load), the browser treats it as a "third-party" context. The cookies are NOT sent.
- Result: Users may see "Log In" screens even if they are logged in on their main browser tab.
- Fix: Very hard to fix in MV3.
- Partial Solution: The extension would need
host_permissionsto modifyCookieheaders to removeSameSiteattributes. (Security Risk: High).
- Partial Solution: The extension would need
- Issue: Each iframe is a full browser context (DOM, JS Heap, Layout Engine). Opening 50 nodes = opening 50 tabs.
- Scale: Chrome processes are heavy. 50 active iframes can easily consume 4GB+ RAM.
- Solution: Virtualization / Suspension.
- Current Imp: We have a "Suspend" button.
- Ideal Auto-Suspend: Check viewport intersection. If a node is far off-screen, replace the
<iframe>with a static screenshot (image). Destroy the iframe to free RAM. Re-hydrate on hover/view. - Note: React Flow has
onlyRenderVisibleElements, but that just removes the DOM node. Removing the DOM node destroys the iframe state (scroll position, form data). Restoring it reloads the page.
- Issue: We are embedding arbitrary 3rd party code.
- Defense:
- Sandbox Attribute: We use
<iframe sandbox="...">. - Values:
allow-scripts,allow-same-origin,allow-forms. - Missing:
allow-top-navigation. This prevents the iframe from "breaking out" and redirecting the entire Vortex app to a malicious site. - Content Script Isolation: Chrome Extensions run content scripts in an "Isolated World". The page JS cannot touch the content script JS variables, preventing the page from hijacking the extension capabilities.
- Sandbox Attribute: We use
Instead of keeping 50 live iframes:
- Capture: When a node is inactive, use
chrome.tabs.captureVisibleTab(trickier with iframes) or standard HTML2Canvas to take a snapshot. - Freeze: Unmount the iframe, render
<img>. - Thaw: On click, remount iframe.
- Trade-off: You lose scroll position and dynamic state (e.g. video playing).
Chrome is moving towards "Partitioned" cookies (CHIPS).
- Eventually, iframes will have their own "cookie jar" separate from the top-level site. This fixes privacy but doesn't solve "I want to be logged in".
- Workaround: To truly support seamless auth, Vortex would need to function as a "Tab Manager" that purely visualizes real tabs, rather than embedding them. (i.e. click node -> switch to real tab). But that ruins the "Spatial" experience.