CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/jupyter/blobs/iframe.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Efficient backend processing of iframe srcdoc and general text/html messages.
8
9
MOTIVATION: Sage 3d graphics.
10
*/
11
12
import { decode } from "he";
13
//import { getLogger } from "@cocalc/backend/logger";
14
//const logger = getLogger("jupyter:blobs:iframe");
15
16
// see https://github.com/sagemathinc/cocalc/issues/4322
17
const MAX_HTML_SIZE = 10 ** 6;
18
19
// We use iframes to render html in a number of cases:
20
// - if it starts with iframe
21
// - if it has a whole page doctype
22
// - if it has a <script> tag anywhere -- since those are ignored by safe HTML
23
// rendering; using an iframe is the only way. This e.g., makes mpld3 work uses -- <script>! https://github.com/sagemathinc/cocalc/issues/1934
24
// and altair -- https://github.com/sagemathinc/cocalc/issues/4468 -- uses <script type="text/javascript"/>
25
// - do NOT just render all html in an iframe, e.g., this would break bokeh, since one output creates the target elt,
26
// and a different output uses javascript to render it, and this doesn't work with an iframe, of course.
27
export function is_likely_iframe(content: string): boolean {
28
if (!content) {
29
return false;
30
}
31
content = content.toLowerCase();
32
if (
33
content.includes("https://bokeh.org") &&
34
content.includes("bk-notebook-logo")
35
) {
36
// Do NOT use an iframe for bokeh no matter what, since this won't work properly.
37
// Hopefully the above heuristic is sufficiently robust to detect but not overdetect.
38
return false;
39
}
40
if (content.includes("<!doctype html>") || content.includes("<html>")) {
41
// plotly wraps its output in <html>, which strongly suggests it wants to
42
// be in an iframe. It's not valid to put <html> as a child of a div, so really
43
// the only valid way to render an <html> string is as an iframe.
44
return true;
45
}
46
if (content.length >= MAX_HTML_SIZE) {
47
// it'll just break anyways if we don't use an iframe -- if we do, there is hope.
48
return true;
49
}
50
return content.startsWith("<iframe");
51
}
52
53
export function process(
54
content: string,
55
saveToBlobStore: (data: string, type: string, ipynb?: string) => string,
56
): string {
57
const decodedContent = decode(content);
58
const contentLower = decodedContent.toLowerCase();
59
const i = contentLower.indexOf("<html>");
60
const j = contentLower.lastIndexOf("</html>");
61
// trim content to the part inside the html tags – keep it otherwise
62
// this is necessary for wrapping inline html code like for
63
// https://github.com/sagemathinc/cocalc/issues/4468
64
let src = "";
65
if (i != -1 && j != -1) {
66
src = decodedContent.slice(i, j + "</html>".length);
67
} else {
68
src = `<html>${decodedContent}</html>`;
69
}
70
// logger.debug("process", { content, src });
71
return saveToBlobStore(src, "text/html", content);
72
}
73
74