Skip to content

Commit

Permalink
WIP - no more base_index Frames abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollmer committed Sep 23, 2024
1 parent 9071da8 commit 13770f8
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 239 deletions.
5 changes: 3 additions & 2 deletions pkg/shell/active-pages-modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const _ = cockpit.gettext;
export const ActivePagesDialog = ({ dialogResult, state }) => {
function get_pages() {
const result = [];
for (const f of state.frames) {
for (const n in state.frames) {
const f = state.frames[n];
if (f) {
const active = state.last_fullpath_for_host(f.host) == f.fullpath;
result.push({
Expand Down Expand Up @@ -62,7 +63,7 @@ export const ActivePagesDialog = ({ dialogResult, state }) => {
function onRemove() {
pages.forEach(element => {
if (element.selected) {
state.remove_frame(element.address, element.component);
state.remove_frame(element.name);
}
});
dialogResult.resolve();
Expand Down
187 changes: 8 additions & 179 deletions pkg/shell/base_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,175 +22,6 @@ import cockpit from "cockpit";
const shell_embedded = window.location.pathname.indexOf(".html") !== -1;
// const _ = cockpit.gettext;

function component_checksum(machine, component) {
const parts = component.split("/");
const pkg = parts[0];
if (machine.manifests && machine.manifests[pkg] && machine.manifests[pkg][".checksum"])
return "$" + machine.manifests[pkg][".checksum"];
}

function Frames(index) {
const self = this;
let language = document.cookie.replace(/(?:(?:^|.*;\s*)CockpitLang\s*=\s*([^;]*).*$)|^.*$/, "$1");
if (!language)
language = navigator.language.toLowerCase(); // Default to Accept-Language header

/* Lists of frames, by host */
self.iframes = { };

/* Array with stable order, so that React will always render
iframes in the same order.
*/
self.frame_array = [];

function remove_frame(frame) {
/* XXX - We can never actually remove entries from the
frame_array. Otherwise React _might_ do the wrong thing:
Instead of removing the corresponding iframe element, it
might just keep it and change its attributes. (Maybe.) This
might confuse the Browser and at least trigger spurious
reloads. (Maybe.)
At least, removing entries here for real will crash
Chromium. Even if we only remove at the end. Firefox is
fine. Maybe React does everything correctly and Chromium
just has a bug related to how React removes iframes from
the DOM.
So we just punch holes into the array. Each hole results in
a useless, empty <iframe> element in the DOM. But creating
new frames will first fill those holes again before making
the array longer. Thus, the number of iframes does not grow
without limit in long running sessions.
*/
const idx = self.frame_array.findIndex(f => f === frame);
if (idx >= 0)
self.frame_array[idx] = null;
}

self.remove = function remove(machine, component) {
let address;
if (typeof machine == "string")
address = machine;
else if (machine)
address = machine.address;
if (!address)
address = "localhost";
const list = self.iframes[address] || { };
if (!component)
delete self.iframes[address];
Object.keys(list).forEach(function(key) {
if (!component || component == key) {
remove_frame(list[key]);
delete list[component];
}
});
};

self.lookup = function lookup(machine, fullpath, hash, title) {
let host;
let address;
let new_frame = false;

if (typeof machine == "string") {
address = host = machine;
} else if (machine) {
host = machine.connection_string;
address = machine.address;
}

if (!host)
host = "localhost";
if (!address)
address = host;

let list = self.iframes[address];
if (!list)
self.iframes[address] = list = { };

const name = "cockpit1:" + host + "/" + fullpath;
let frame = list[fullpath];
if (frame && frame.name != name) {
remove_frame(frame);
frame = null;
}

/* Need to create a new frame */
if (!frame) {
/* Never create new frames for machines that are not
connected yet. That would open a channel to them (for
loading the URL), which woould trigger the bridge to
attempt a log in. We want all logins to happen in a
single place (in hosts.jsx) so that we can get the
options right, and show a warning dialog.
*/
if (host != "localhost" && machine.state !== "connected")
return null;

new_frame = true;
frame = {
name,
host: machine.address,
fullpath: fullpath,
title,
ready: false,
};

let base, checksum;
if (machine) {
if (machine.manifests && machine.manifests[".checksum"])
checksum = "$" + machine.manifests[".checksum"];
else
checksum = machine.checksum;
}

if (checksum && checksum == component_checksum(machine, fullpath)) {
if (host === "localhost")
base = "..";
else
base = "../../" + checksum;
} else {
/* If we don't have any checksums, or if the component specifies a different
checksum than the machine, load it via a non-caching @<host> path. This
makes sure that we get the right files, and also that we don't poisen the
cache with wrong files.
We can't use a $<component-checksum> path since cockpit-ws only knows how to
route the machine checksum.
TODO - make it possible to use $<component-checksum>.
*/
base = "../../@" + host;
}

frame.url = base + "/" + fullpath;
if (fullpath.indexOf("/") === -1)
frame.url += "/index";
frame.url += ".html";
}

if (!hash)
hash = "/";
const src = frame.url + "#" + hash;
frame.src = src;

/* Store frame only when fully setup */
if (new_frame) {
list[fullpath] = frame;

/* Try to find an empty slot for the frame. See
* frame_remove for why we have these.
*/
const idx = self.frame_array.findIndex(f => f == null);
if (idx >= 0)
self.frame_array[idx] = frame;
else
self.frame_array.push(frame);
}
return frame;
};
}

function Router(index) {
const self = this;

Expand Down Expand Up @@ -503,7 +334,6 @@ function Index() {

self.has_oops = false;

self.frames = new Frames(self);
self.router = new Router(self);

/* Watchdog for disconnect */
Expand Down Expand Up @@ -595,19 +425,14 @@ function Index() {
return state;
};

const last_hash_for_host_fullpath = { };

function lookup_component_hash(address, component) {
if (!address)
address = "localhost";

const list = self.frames.iframes[address];
const iframe = list ? list[component] : undefined;

if (iframe) {
const src = iframe.src;
if (src)
return src.split("#")[1];
}

if (last_hash_for_host_fullpath[address])
return last_hash_for_host_fullpath[address][component] || null;
return null;
}

Expand Down Expand Up @@ -641,6 +466,10 @@ function Index() {
if (frame_change && !state.hash)
state.hash = lookup_component_hash(state.host, state.component);

if (!last_hash_for_host_fullpath[state.host])
last_hash_for_host_fullpath[state.host] = { };
last_hash_for_host_fullpath[state.host][state.component] = state.hash;

const target = shell_embedded ? window.location : encode(state, null, true);

if (replace) {
Expand Down
28 changes: 13 additions & 15 deletions pkg/shell/frames.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ function poll_frame_ready(state, frame, elt, count, setupFrameWindow) {
if (elt.contentWindow && setupFrameWindow)
setupFrameWindow(elt.contentWindow);

// if (elt.contentDocument && elt.contentDocument.documentElement) {
// elt.contentDocument.documentElement.lang = language;
// if (cockpit.language_direction)
// elt.contentDocument.documentElement.dir = cockpit.language_direction;
// }
if (elt.contentDocument && elt.contentDocument.documentElement) {
elt.contentDocument.documentElement.lang = state.language;
if (state.language_direction)
elt.contentDocument.documentElement.dir = state.language_direction;
}
} else {
elt.removeAttribute("data-ready");
window.setTimeout(function() {
Expand All @@ -94,7 +94,7 @@ function poll_frame_ready(state, frame, elt, count, setupFrameWindow) {
}
}

export const Frames = ({ state, current_frame, setupFrameWindow }) => {
export const Frames = ({ state, idle_state, current_frame }) => {
const content_ref = useRef(null);
const { frames } = state;

Expand Down Expand Up @@ -135,11 +135,8 @@ export const Frames = ({ state, current_frame, setupFrameWindow }) => {
return elt;
}

const frames_by_name = {};
const iframes_by_name = {};

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

for (const c of content.children) {
if (c.nodeName == "IFRAME") {
if (c.getAttribute('name'))
Expand All @@ -151,13 +148,13 @@ export const Frames = ({ state, current_frame, setupFrameWindow }) => {

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

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

Expand Down Expand Up @@ -195,10 +192,11 @@ export const Frames = ({ state, current_frame, setupFrameWindow }) => {
iframe.style.display = (frame == current_frame) ? "block" : "none";

if (need_poll)
poll_frame_ready(state, frame, iframe, 0, setupFrameWindow);
poll_frame_ready(state, frame, iframe, 0, win => idle_state.setupIdleResetEventListeners(win));

// XXX - doesn't work (or is not enough), document has
// zero height. Make content-area dark?
// XXX - doesn't work (or is not enough), "about:blank"
// document has zero height on Firefox (but full height on
// Chrome). Make content-area dark as well?
if (!iframes_by_name[name] && iframe.contentDocument.documentElement) {
const style = localStorage.getItem('shell:style') || 'auto';
if ((window.matchMedia &&
Expand Down
Loading

0 comments on commit 13770f8

Please sign in to comment.