clientIO / clientIO/joint

[Bug]: toJPEG method is broken on joint/plus 4.2.3 on Safari

Open
#3,242 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
JavaScript
Stars
5.4k
Forks
893
Avg merge
3d 3h
Merged PRs (30d)
26

Description

Current versus expected behaviour

After upgrading to joint/plus v4.2.3, we are encountering a blocking error only on Safari (v26.3.1).
The joint.format.toJPEG() method fails, causing the application to crash with a critical error. We caught it via an exception, and it appears to be: TypeError: Attempted to assign to readonly property.

After some investigation, the error seems to originate from this portion of code in joint/plus:
(apologies for the minified code)

function copyExternalStyles(t, e) {
  var i = Array.from(t.querySelectorAll("*")), e = Array.from(e.querySelectorAll("*"));
  let n = t.ownerDocument;
  var o = n.styleSheets.length, s = [];
  for (let t2 = o - 1; 0 <= t2; t2--) s[t2] = n.styleSheets[t2], n.styleSheets[t2].disabled = true;
  let r = {};
  i.forEach((t2, e2) => {
    let i2 = window.getComputedStyle(t2, null), n2 = {};
    import_core.util.forIn(i2, (t3) => {
      n2[t3] = i2.getPropertyValue(t3);
    }), r[e2] = n2;
  }), o != n.styleSheets.length && s.forEach((t2, e2) => {
    n.styleSheets[e2] = t2;
  });
  for (let t2 = 0; t2 < o; t2++) n.styleSheets[t2].disabled = false;
  let a = {};
  i.forEach((t2, e2) => {
    let i2 = window.getComputedStyle(t2, null), n2 = r[e2], o2 = {};
    import_core.util.forIn(i2, (t3) => {
      isNaN(t3) && i2.getPropertyValue(t3) !== n2[t3] && (o2[t3] = i2.getPropertyValue(t3));
    }), a[e2] = o2;
  }), e.forEach((t2, e2) => {
    $$1(t2).css(a[

at n.styleSheets[e2] = t2; that is probably a forbidden operation for Safari.

Steps to reproduce

Below I will provide a minimum editor in order to replicate the error; after 2 seconds it automatically call .toJPEG() method. It is missing the jointjs/plus imports.

<!doctype html>
<html lang="it">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>JointJS toJPEG Safari Test</title>
		<style>
			.test-container {
				padding: 2rem;
			}

			.status {
				margin: 1rem 0;
				padding: 1rem;
				background: #f0f0f0;
				border-radius: 4px;
				font-weight: bold;
			}

			.canvas {
				border: 2px solid #ccc;
				margin-top: 1rem;
			}
		</style>
	</head>
	<body>
		<div class="test-container">
			<h1>JointJS toJPEG Safari Test</h1>
			<p class="status">Status: <span id="result">Waiting...</span></p>
			<div class="canvas" id="container"></div>
		</div>

		<script type="module">
			let container = document.getElementById('container');
			let resultElement = document.getElementById('result');
			let joint;
			let paper;
			let graph;

			Promise.all([
				// here import Jointjs/plus 
			]).then(([_joint]) => {
				joint = _joint;

				if (!container) {
					return;
				}

				// Create a simple graph
				graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });

				// Create a simple paper
				paper = new joint.dia.Paper({
					model: graph,
					width: 800,
					height: 600,
					el: container
				});

				// Add a simple rectangle
				const rect = new joint.shapes.standard.Rectangle({
					position: { x: 100, y: 100 },
					size: { width: 200, height: 100 },
					attrs: {
						body: {
							fill: '#ffcccc',
							stroke: '#000000'
						},
						label: {
							text: 'Test Rectangle',
							fill: '#000000'
						}
					}
				});

				graph.addCell(rect);

				// Test toJPEG after 2 seconds
				setTimeout(() => {
					resultElement.textContent = 'Generating JPEG...';
					console.log('Starting toJPEG test...');

					try {
						joint.format.toJPEG(
							paper,
							(dataURL) => {
								resultElement.textContent = 'Success! JPEG generated';
								console.log('toJPEG succeeded:', dataURL.substring(0, 50) + '...');
							},
							{
								quality: 0.7,
								padding: 100
							}
						);
					} catch (error) {
						resultElement.textContent = `Error: ${error}`;
						console.error('toJPEG failed:', error);
					}
				}, 2000);
			});
		</script>
	</body>
</html>
Version

4.2.3

What browsers are you seeing the problem on?

Safari

What operating system are you seeing the problem on?

Mac

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the joint.format.toJPEG() entry point and inspect copyExternalStyles, especially the n.styleSheets[e2] = t2 operation shown in the report. Reproduce the failure with the provided HTML editor on Safari 26.3.1. Done means toJPEG no longer throws the readonly-property error and generates the JPEG successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.