Skip to content

Commit 162b2d4

Browse files
committed
Use the postMessage mechanism to set the iFrame's height.
Remove `allow-same-origin` from the iframe's sandbox.
1 parent 1e70568 commit 162b2d4

File tree

3 files changed

+102
-30
lines changed

3 files changed

+102
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## master
44

55
- Fixes the link in the gist view will be opened in the iFrame.
6+
- Use the `postMessage` mechanism to set the iFrame's height, which should be much secure.
67

78
## 0.6.0
89

main.ts

Lines changed: 97 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Plugin } from 'obsidian';
2+
import { nanoid } from 'nanoid';
23

34
type GistJSON = {
45
description: string,
@@ -10,8 +11,13 @@ type GistJSON = {
1011
stylesheet: string
1112
}
1213

14+
const pluginName = "obsidian-gist"
15+
const obsidianAppOrigin = 'app://obsidian.md'
16+
1317
export default class GistPlugin extends Plugin {
1418
async onload() {
19+
this._injectContainerHeightAdjustmentScript()
20+
1521
this.registerMarkdownCodeBlockProcessor("gist", async (sourceString: string, el, ctx) => {
1622
const gists = sourceString.trim().split("\n")
1723

@@ -23,21 +29,20 @@ export default class GistPlugin extends Plugin {
2329
});
2430
}
2531

26-
onunload() {
27-
}
28-
2932
// private
3033

31-
async _processGist(el: HTMLElement, gist: string) {
34+
async _processGist(el: HTMLElement, gistString: string) {
3235
const pattern = /(?<protocol>https?:\/\/)?(?<host>gist\.github\.com\/)?((?<username>\w+)\/)?(?<gistID>\w+)(\#(?<filename>.+))?/
3336

34-
const matchResult = gist.match(pattern).groups
37+
const matchResult = gistString.match(pattern).groups
3538

36-
if (matchResult.gistID === undefined) {
37-
return this._showError(el, gist)
39+
const gistID = matchResult.gistID
40+
41+
if (gistID === undefined) {
42+
return this._showError(el, gistString, `Could not found a valid Gist ID, please make sure your content and format is correct.`)
3843
}
3944

40-
let gistURL = `https://gist.github.com/${matchResult.gistID}.json`
45+
let gistURL = `https://gist.github.com/${gistID}.json`
4146

4247
if (matchResult.filename !== undefined) {
4348
gistURL = `${gistURL}?file=${matchResult.filename}`
@@ -47,22 +52,29 @@ export default class GistPlugin extends Plugin {
4752
const response = await fetch(gistURL)
4853

4954
if (response.ok) {
50-
const gistJSON = await response.json()
51-
return this._insertGistElement(el, gistJSON as GistJSON)
55+
const gistJSON = await response.json() as GistJSON
56+
return this._insertGistElement(el, gistID, gistJSON)
5257
} else {
53-
return this._showError(el, gist)
58+
return this._showError(el, gistString, `Could not fetch the Gist info from GitHub server. (Code: ${response.status})`)
5459
}
5560
} catch (error) {
56-
return this._showError(el, gist)
61+
return this._showError(el, gistString, `Could not fetch the Gist from GitHub server. (Error: ${error})`)
5762
}
5863
}
5964

60-
async _insertGistElement(el: HTMLElement, gistJSON: GistJSON) {
65+
async _insertGistElement(el: HTMLElement, gistID: string, gistJSON: GistJSON) {
66+
// generate an uuid for each gist element
67+
const gistUUID = `${pluginName}-${gistID}-${nanoid()}`
68+
6169
// container
6270
const container = document.createElement('iframe');
71+
container.id = gistUUID
72+
container.classList.add(`${pluginName}-container`)
73+
container.setAttribute('sandbox', 'allow-scripts allow-top-navigation-by-user-activation')
74+
container.setAttribute('loading', 'lazy')
6375

64-
// auto adjust container height
65-
const innerStyle = `
76+
// reset the default things on HTML
77+
const resetStylesheet = `
6678
<style>
6779
html, body {
6880
margin: 0;
@@ -72,36 +84,92 @@ export default class GistPlugin extends Plugin {
7284
</style>
7385
`
7486

87+
// height adjustment script
88+
const heightAdjustmentScript = `
89+
<script>
90+
deliverHeightMessage = () => {
91+
const contentHeight = document.body.scrollHeight;
92+
93+
top.postMessage({
94+
sender: '${pluginName}',
95+
gistUUID: '${gistUUID}',
96+
contentHeight: contentHeight
97+
}, '${obsidianAppOrigin}');
98+
}
99+
100+
window.addEventListener('load', () => {
101+
deliverHeightMessage();
102+
})
103+
</script>
104+
`
105+
75106
// build stylesheet link
76107
const stylesheetLink = document.createElement('link');
77108
stylesheetLink.rel = "stylesheet";
78109
stylesheetLink.href = gistJSON.stylesheet
79110

80-
// build link hacker
111+
// hack to make links open in the parent
81112
const parentLinkHack = document.createElement('base')
82113
parentLinkHack.target = "_parent"
83114

84115
// Inject content into the iframe
85116
container.srcdoc = `
86-
<head>
87-
${stylesheetLink.outerHTML}
88-
${parentLinkHack.outerHTML}
89-
90-
${innerStyle}
91-
</head>
92-
93117
<html>
94-
${gistJSON.div}
118+
<head>
119+
<!-- hack -->
120+
${resetStylesheet}
121+
${parentLinkHack.outerHTML}
122+
${heightAdjustmentScript}
123+
124+
<!-- gist style -->
125+
${stylesheetLink.outerHTML}
126+
</head>
127+
128+
<body>
129+
${gistJSON.div}
130+
</body>
95131
</html>
96132
`
97-
container.setAttribute('sandbox', 'allow-same-origin allow-top-navigation-by-user-activation')
98-
container.setAttribute('onload', 'this.height=this.contentDocument.body.scrollHeight;')
99133

100-
// insert into the DOM
134+
// insert container into the DOM
101135
el.appendChild(container)
102136
}
103137

104-
async _showError(el: HTMLElement, gistIDAndFilename: String) {
105-
el.createEl('pre', { text: `Failed to load the Gist (${gistIDAndFilename}).` })
138+
async _showError(el: HTMLElement, gistIDAndFilename: String, errorMessage: String = '') {
139+
const errorText = `
140+
Failed to load the Gist (${gistIDAndFilename}).
141+
142+
Error:
143+
144+
${errorMessage}
145+
`.trim()
146+
147+
el.createEl('pre', { text: errorText })
148+
}
149+
150+
_injectContainerHeightAdjustmentScript() {
151+
const containerHeightAdjustmentScript = document.createElement('script')
152+
containerHeightAdjustmentScript.id = `${pluginName}-container-height-adjustment`
153+
containerHeightAdjustmentScript.textContent = `
154+
window.addEventListener("message", (messageEvent) => {
155+
const sender = messageEvent.data.sender
156+
157+
if (messageEvent.origin !== 'null') {
158+
// a message received from the iFrame with \`srcdoc\` attribute, the \`origin\` will be \`null\`.
159+
return;
160+
}
161+
162+
// only process message coming from this plugin
163+
if (sender === '${pluginName}') {
164+
const gistUUID = messageEvent.data.gistUUID
165+
const contentHeight = messageEvent.data.contentHeight
166+
167+
const gistContainer = document.querySelector('iframe#' + gistUUID)
168+
gistContainer.height = contentHeight
169+
}
170+
}, false)
171+
`
172+
173+
document.head.appendChild(containerHeightAdjustmentScript)
106174
}
107175
}

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@
2121
"rollup": "^2.32.1",
2222
"tslib": "^2.2.0",
2323
"typescript": "^4.2.4"
24+
},
25+
"dependencies": {
26+
"nanoid": "^3.1.23"
2427
}
25-
}
28+
}

0 commit comments

Comments
 (0)