Skip to content

Sandcastle only postMessage for communication with the viewer#13154

Open
jjspace wants to merge 19 commits intomainfrom
sandcastle-postmessage
Open

Sandcastle only postMessage for communication with the viewer#13154
jjspace wants to merge 19 commits intomainfrom
sandcastle-postmessage

Conversation

@jjspace
Copy link
Contributor

@jjspace jjspace commented Jan 22, 2026

Description

As part of ongoing discussions and work to add a login system for sandcastle we've been looking at locking the viewer down more. One of the best ways to do that is to separate it out to it's own origin entirely. This would not be possible with the current way we push code into the iframe to "run" a sandcastle.

This PR aims to replace all communication between the Sandcastle app and Viewer "bucket" with postMessage calls between the windows. This will enable the viewer/iframe to be anywhere and still work. Locally for now this may still be the same origin/domain for each and postMessage will still work.

Note that when an iframe is on the same origin and has allow-same-origin is able to directly access the parent window in JS. Chrome warns about this in DevTools: An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. This is functionally the same as it is right now but will not be the case if we split the origins.

After this change we conceptually have 3 distinct "applications". The Main Sandcastle, the Standalone page and the Viewer bucket which can be hosted independently of the others. We also have a dedicated library in Sandcastle.ts for the helpers available in Sandcastle code.

The communication between windows now looks something like this:

sequenceDiagram
    App->>Viewer: page load
    Viewer->>App: postMessage(bucketReady)
    App->>Viewer: postMessage(runCode)
    Viewer->Viewer: Apply code
    opt Console messages
        Viewer->>App: postMessage(consoleLog, etc.)
    end
    opt Sandcastle helpers
        Viewer->>App: postMessage(highlight)
    end
    App->>Viewer: postMessage(reload)
    Viewer->Viewer: reload page (return to top)
Loading

Implementation

  • Implementation focuses around the new IframeBridge class which aims to encapsulate the checks for the origin messages come from and are sent to as recommended by MDN and other sources
    • The TS types for the messages helps to align structure on the "protocol" we've created on both sides of the bridge
    • The "inner" and "outer" origins are now defined a build time and embedded in the JS by vite. This could alternatively be determined at runtime but I wasn't sure we wanted to list them all in the source? Open to discussion
      • Need to finish config here for all environments
  • Added sandbox attribute with allow-scripts and allow-same-origin to the bucket iframe now that we don't need direct JS access to run the code
  • Broke apart the Sancastle-client.js file into more dedicated ConsoleWrapper class/system and narrow bucket-client focused on the page and code loading
  • Switched the local dev server to host at 2 separate ports to create the split origin separation locally (port is part of origin. The same files are served from both
    • Open decision on whether we can reuse the same app to get the auto-rebuilding of the library

Issue number and link

Precursor for the work in #13079

Testing plan

  • run npm run build-sandcastle
  • run npm start and make sure sandcastle works. This is actually already split into 2 origins locally with a second dev server
  • switch to the sandcastle package and run npm run dev and make sure that still works too. this is not split origin unless I can figure out a way for the vite dev server to spin up a second server.

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

@github-actions
Copy link

Thank you for the pull request, @jjspace!

✅ We can confirm we have a CLA on file for you.

Copy link

@cjj884 cjj884 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered iframe sandbox with allow-* list for only what is needed to lock down beyond independent origin alone?

@jjspace jjspace requested a review from lukemckinstry January 30, 2026 17:30
@jjspace
Copy link
Contributor Author

jjspace commented Jan 30, 2026

Have you considered iframe sandbox with allow-* list for only what is needed to lock down beyond independent origin alone?

@cjj884 Yes, this pr adds sandbox="allow-scripts allow-same-origin" to the iframe. This wasn't possible with how we "reached into" the iframe to run the code before but now it is. The iframe is located in the Bucket.tsx component if you feel like taking a look

@jjspace jjspace mentioned this pull request Jan 30, 2026
7 tasks
@cjj884
Copy link

cjj884 commented Jan 30, 2026

Have you considered iframe sandbox with allow-* list for only what is needed to lock down beyond independent origin alone?

@cjj884 Yes, this pr adds sandbox="allow-scripts allow-same-origin" to the iframe. This wasn't possible with how we "reached into" the iframe to run the code before but now it is. The iframe is located in the Bucket.tsx component if you feel like taking a look

Sounds good. Please add these details into TM to clarify the iframe isolation in place for viewer. https://github.com/CesiumGS/cesium-analytics/pull/1084#discussion_r2742899254

@lukemckinstry
Copy link
Contributor

One initial reaction is that this may be unclear to devs spinning up locally.

Screenshot from 2026-02-02 11-50-41

It seems going to http://localhost:8081/ loads the landing page, but sandcastle does not work, is that expected? If we only want devs to go to 8080, perhaps we should suppress the Sandcastle viewer running locally message or make it clear some other way?

@lukemckinstry
Copy link
Contributor

I am also not able to run the sandcastle package with npm run dev. Perhaps I did not build correctly, I checked here and here for docs.

@jjspace
Copy link
Contributor Author

jjspace commented Feb 2, 2026

It seems going to http://localhost:8081/ loads the landing page, but sandcastle does not work, is that expected? If we only want devs to go to 8080, perhaps we should suppress the Sandcastle viewer running locally message or make it clear some other way?

I was not expecting people to try and open 8081 directly. I think it's helpful for devs to know something is running on that port so I'm not sure a better way to share that information. People should still just use 8080 as normal

I am also not able to run the sandcastle package with npm run dev

Where are you trying to run that? That command only works when you're in the sandcastle package directory. Are you getting any errors or messages?

@lukemckinstry

This comment was marked as duplicate.

@lukemckinstry
Copy link
Contributor

lukemckinstry commented Feb 2, 2026

I am also not able to run the sandcastle package with npm run dev

Where are you trying to run that? That command only works when you're in the sandcastle package directory. Are you getting any errors or messages?

cd packages/sandcastle
npm run dev
then when I select any sandcastle from the gallery the viewer shows "Loading..." and there is a console error Failed to load resource: the server responded with a status of 404 (Not Found) and from the networking tab it shows the request url is http://localhost:5173/Source/Cesium.js

@jjspace
Copy link
Contributor Author

jjspace commented Feb 2, 2026

@lukemckinstry please check the console for any lines like this and try to resolve them

[vite-plugin-static-copy] Error: No file was found to copy on /home/[user]/Programming/cesium/packages/engine/index.d.ts src.

For example this one needs you to run npm run build-ts at the top level.
This is a consequence of how the static copy plugin works, it seems to not serve any of the files when any are missing which is really annoying. I'll look into if there's a way around this or an alternative to use for dev again.

@jjspace
Copy link
Contributor Author

jjspace commented Feb 4, 2026

@lukemckinstry I pushed a few small changes to relax the dev server and ignore missing files. This may mean it breaks but I think errors in devtools will hopefully point to what files are missing. Certain files like the types should already be handled by the UI gracefully.
There are other ways to serve static files from the vite server but I specifically want to try and stay in the same process using the static copy plugin to align with the actual builds

I also made the local dev server just fully "mirror" and reuse the express app the dev server was already using. This should mean it automatically rebuilds CesiumJS files when it needs to without duplicating all that logic.

@lukemckinstry
Copy link
Contributor

Still getting the same error when I try to run npm run dev in packages/sandcastle. I do not see any console errors like the one you showed above when I run the command.

@lukemckinstry
Copy link
Contributor

lukemckinstry commented Feb 11, 2026

@jjspace just got back to this, if I run npm run build, then running the sandcastle dev server works. ie running npm run dev from packages/sandcastle.

Is that something you want to fix, just document, or neither?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants