Skip to content

Commit

Permalink
WIP - manage iframes explicitly
Browse files Browse the repository at this point in the history
We really need to be super careful with setting their 'src' attribute
and React just doesn't seem to be.
  • Loading branch information
mvollmer committed Sep 19, 2024
1 parent 7c94e11 commit 566ddbc
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 53 deletions.
2 changes: 2 additions & 0 deletions pkg/lib/cockpit/_internal/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ class Transport extends EventEmitter<{ ready(): void }> {

/* See if we should communicate via parent */
if (window.parent !== window && window.name.indexOf("cockpit1:") === 0) {
console.log("PARENT SOCKET");
this.#ws = new ParentWebSocket(window.parent);
} else {
console.log("REAL SOCKET");
const ws_loc = calculate_url();
transport_debug("connecting to " + ws_loc);
this.#ws = new WebSocket(ws_loc, "cockpit1");
Expand Down
20 changes: 17 additions & 3 deletions pkg/shell/base_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,12 @@ function Router(index) {
if (control.channel !== undefined) {
for (const seed in source_by_seed) {
const source = source_by_seed[seed];
if (!source.window.closed)
if (!source.window.closed) {
// console.log("POST", source.window.name, JSON.stringify(message));
source.window.postMessage(message, origin);
}
}
// console.log("done");
} else if (control.command == "hint") {
/* This is where we handle hint messages directed at
* the shell. Right now, there aren't any.
Expand Down Expand Up @@ -299,7 +302,7 @@ function Router(index) {

function unregister(source) {
const child = source.window;
console.log("UNREGISTER", child);
console.log("UNREGISTER", child.name);
cockpit.kill(null, child.name);
const frame = child.frameElement;
if (frame)
Expand All @@ -311,10 +314,11 @@ function Router(index) {
}
delete source_by_seed[source.channel_seed];
delete source_by_name[source.name];
console.log("SOURCES", source_by_seed);
}

function register(child) {
console.log("REGISTER", child);
console.log("REGISTER", child.name);
let host, page;
const name = child.name || "";
if (name.indexOf("cockpit1:") === 0) {
Expand Down Expand Up @@ -354,6 +358,8 @@ function Router(index) {

perform_track(child);

console.log("SOURCES", source_by_seed, source_by_name);

index.navigate();
return source;
}
Expand Down Expand Up @@ -480,6 +486,14 @@ function Router(index) {
source.window.postMessage(message, origin);
}
};

self.unregister_name = (name) => {
const source = source_by_name[name];
if (source)
unregister(source);
else
console.warn("UNNOWN", name, source_by_name);
}
}

/*
Expand Down
163 changes: 113 additions & 50 deletions pkg/shell/shell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import cockpit from "cockpit";

import React from 'react';
import React, { useEffect } from 'react';
import { createRoot } from "react-dom/client";

import { WithDialogs } from "dialogs.jsx";
Expand Down Expand Up @@ -55,42 +55,13 @@ const _ = cockpit.gettext;
/* Get rid of base_index.current_frame
*/

/* Maintain frame[data-loaded] in React
*/

/* Do something about jump calling navigate calling jump.
*/

/* Use Dialogs.run for HostsDialog, get rid of trigger_connection_flow
*/

/* Have to be careful when changing "src" of an iframe, apparently.
if (frame.getAttribute('src') != src) {
if (frame.contentWindow) {
// This prevents the browser from creating a new
// history entry. It would do that whenever the "src"
// of a frame is changed and the window location is
// not consistent with the new "src" value.
//
// This matters when a "jump" command changes both the
// the current frame and the hash of the new frame.
frame.contentWindow.location.replace(src);
}
}
*/

/* Give new iframes initial darkness
// The new iframe is shown before any HTML/CSS is ready and loaded,
// explicitly set a dark background so we don't see any white flashes
if (dark_mode && frame.contentDocument && frame.contentDocument.documentElement) {
// --pf-v5-global--BackgroundColor--dark-300
const dark_mode_background = '#1b1d21';
frame.contentDocument.documentElement.style.background = dark_mode_background;
} else {
frame.contentDocument.documentElement.style.background = 'white';
}
*/

/* frame_ready(..)
Expand Down Expand Up @@ -389,6 +360,8 @@ const MachineTroubleshoot = ({ machine, onClick }) => {
);
};

const dead_iframes = [];

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused variable dead_iframes.

const Shell = ({ state, machines, loader }) => {
useEvent(state, "update");
const current_user = useLoggedInUser()?.name || "";
Expand All @@ -412,6 +385,107 @@ const Shell = ({ state, machines, loader }) => {
config
} = state;

useEffect(() => {
const content = document.getElementById("content");
if (content) {
const frames_by_name = {};
const iframes_by_name = {};

frames.forEach(f => { if (f) frames_by_name[f.name] = f; });

const free_iframes = [];

for (const c of content.children) {
if (c.nodeName == "IFRAME") {
if (c.getAttribute('name'))
iframes_by_name[c.getAttribute('name')] = c;
else
free_iframes.push(c);
}
}

function iframe_remove(elt) {
// XXX - chromium crashes somewhere down the line when
// removing iframes here.
console.log("REMOVE", elt.getAttribute('name'))
state.index.router.unregister_name(elt.getAttribute('name'));
elt.removeAttribute('name');
elt.removeAttribute('src');
elt.removeAttribute('data-host');
elt.removeAttribute('class');
elt.style.display = "none";
free_iframes.push(elt);
}

function iframe_new(name) {
let elt = free_iframes.shift();
if (!elt) {
elt = document.createElement("iframe");
elt.setAttribute("name", name);
elt.style.display = "none";
content.appendChild(elt);
} else {
elt.setAttribute("name", name);
elt.contentWindow.name = name;
}
return elt;
}

// Remove obsolete iframes
for (const name in iframes_by_name) {
if (!frames_by_name[name])
iframe_remove(iframes_by_name[name]);
}

// Add new and update current iframes
for (const name in frames_by_name) {
const frame = frames_by_name[name];
let iframe = iframes_by_name[name];

if (!iframe) {
iframe = iframe_new(name);
console.log("NEW", name);
iframe.setAttribute("class", "container-frame");
iframe.setAttribute("data-host", frame.host);
}

if (iframe.getAttribute('src') != frame.src) {
console.log("SRC", name, iframe.getAttribute('src'), "->", frame.src);
if (iframe.contentWindow) {
// This prevents the browser from creating a new
// history entry. It would do that whenever the "src"
// of a frame is changed and the window location is
// not consistent with the new "src" value.
//
// This matters when a "jump" command changes both the
// the current frame and the hash of the new frame.
iframe.contentWindow.location.replace(frame.src);
}
iframe.setAttribute('src', frame.src);
}

iframe.style.display = (frame == current_frame) ? "block" : "none";

// XXX - doesn't work (or not enough), document
// has zero hight. Make content-area dark?
if (iframe.contentDocument.documentElement) {
const style = localStorage.getItem('shell:style') || 'auto';
if ((window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
&& style === "auto")
|| style === "dark")
{
// --pf-v5-global--BackgroundColor--dark-300
iframe.contentDocument.documentElement.style.background = '#1b1d21';
} else {
iframe.contentDocument.documentElement.style.background = 'white';
}
} else
console.log("No doc?");
}
}
});

if (problem && !ready) {
const ca_cert_url = window.sessionStorage.getItem("CACertUrl");
return (
Expand All @@ -428,7 +502,8 @@ const Shell = ({ state, machines, loader }) => {
return null;
}

console.log("SHELL nav", machine.address, component, fullpath);
console.log("SHELL nav", machine.address, component, fullpath, state.hash);
console.log("FRAMES", JSON.stringify(frames.map(f => f && f.name)));

const title_parts = [];
const item = compiled.items[component];
Expand Down Expand Up @@ -457,23 +532,6 @@ const Shell = ({ state, machines, loader }) => {

const current_frame = frames.find(f => f && !failure && f.host == machine.address && f.fullpath == fullpath);

function make_iframe(f, idx) {
if (f) {
return (
<iframe key={idx}
className="container-frame"
name={f.name}
title={f.title}
src={f.src}
style={{ display: (f == current_frame) ? undefined : "none" }} />
);
} else {
return <iframe key={idx} style={{ display: "none" }} />;
}
}

console.log("FRAMES", JSON.stringify(frames.map(f => f && f.name)));

return (
<div id="main" className="page"
style={{ '--ct-color-host-accent': machine.address == "localhost" ? undefined : machine.color }}>
Expand Down Expand Up @@ -517,9 +575,14 @@ const Shell = ({ state, machines, loader }) => {
<div id="nav-hosts" className="area-ct-subnav nav-hosts-menu sidebar" />

<div id="content" className="area-ct-content" role="main" tabIndex="-1">
{ frames.map(make_iframe) }
{ failure }
</div>

{ failure &&
<div id="failure-content" className="area-ct-content" role="main" tabIndex="-1">
{ failure }
</div>
}

</div>);
};

Expand Down

0 comments on commit 566ddbc

Please sign in to comment.