This is a web-component, lightweight, bare bones HTML editor that wraps the HTML in an <iframe sandbox>
so that malicious scripts embedded in the HTML cannot execute in your page.
I have a couple of restrictions:
- Must be compatible with web components and shadow DOM.
- Must handle a wide variety of possibly dodgy HTML.
- Should work when imported as a module or asychronously brought into the page.
- Should be open source.
Unfortunately those rule out every HTML editor component I've been able to find. Shadow DOM means libraries that rely on document...
methods (like the otherwise excellent Quill) can't work. Those that do work are too easy to inject <script>
tags into, or need loading with the initial page, or rely on Regex to find XSS.
Like most HTML editors this uses contenteditable
on the body of a new page in an <iframe>
and document.execCommand
to provide functions like Bold and Italic.
Where this differs is that the <iframe>
is flagged with the sandbox="allow-scripts"
attribute. This allows scripts to run inside the <iframe>
, but in their own unique origin - they can't execute anything in the context of the page that contains it, they can't POST, they can't resend cookies (no CSRF), they can't display dialogs, and they can't get out of the frame.
The problem with this sandbox
is that it isn't easy for the parent page and the frame to communicate any more. So this uses postMessage
is used to send content and commands.
So when a Bold command is executed the wrapper component uses postMessage
to sent that to the <iframe>
, and then in that page it picks up the message and calls document.execCommand
. When the user inputs changes to the contenteditable
body that uses postMessage
to sent the changed HTML back up to the containing page.
This component consists of two web components:
<sandbox-editor>
stand alone HTML editor sandbox with no UI<html-editor>
Polymer 2 UI, which depends on HTML imports and further Polymer components.
Include the script in your page:
<script type="module" src="{path}/html-editor/sandbox-editor.js"></script>
Include the tag in your HTML:
<sandbox-editor content={{HTML content to edit}}></sandbox-editor>
The content
attribute (it can also be set as a property) holds the HTML to set. content-changed
fires when the user updates the HTML. This is compatible with Polymer's notify
properties, so you can use its two-way binding.
To execute commands against the editor call editorAction
:
const sandbox = parentElement.querySelector('sandbox-editor');
sandbox.editorAction('bold'); // make the current selection bold
sandbox.editorAction('backColor', '#fdb5fb'); // make the background pink
For an example of this see the source of <html-editor>
Finally, <sandbox-editor>
fires an event when the body inside the editor gains focus. This appears as the focused
property and fires focused-changed
when it changes.
Include the HTML import:
<link rel="import" href="{path}/html-editor/html-editor.html" />
Include the tag in your HTML:
<html-editor content={{HTML content to edit}}></html-editor>
The content
attribute (it can also be set as a property) holds the HTML to set, Polymer two-way databinding is supported.
You can add your own buttons if you want in the slot
:
<html-editor content={{HTML content to edit}}>
<button on-tap="_actionBold">Maek Kustom Blod!</button>
</html-editor>
<html-editor>
only shows the toolbar while the <sandbox-editor>
or some other control in the shadow DOM has focus.