Add destroy method

Which revokes all blob URLs and removes the element from document.
This commit is contained in:
John Factotum
2023-05-09 14:02:07 +08:00
parent a817932b08
commit 5da3be5200
7 changed files with 45 additions and 5 deletions
+4
View File
@@ -36,5 +36,9 @@ export const makeComicBook = ({ entries, loadBlob, getSize }, file) => {
book.resolveHref = href => ({ index: book.sections.findIndex(s => s.id === href) }) book.resolveHref = href => ({ index: book.sections.findIndex(s => s.id === href) })
book.splitTOCHref = href => [href, null] book.splitTOCHref = href => [href, null]
book.getTOCFragment = doc => doc.documentElement book.getTOCFragment = doc => doc.documentElement
book.destroy = () => {
for (const arr of urls.values())
for (const url of arr) URL.revokeObjectURL(url)
}
return book return book
} }
+10 -3
View File
@@ -611,6 +611,9 @@ class Loader {
unloadItem(item) { unloadItem(item) {
this.unref(item?.href) this.unref(item?.href)
} }
destroy() {
for (const url of this.#cache.values()) URL.revokeObjectURL(url)
}
} }
const getHTMLFragment = (doc, id) => doc.getElementById(id) const getHTMLFragment = (doc, id) => doc.getElementById(id)
@@ -628,6 +631,7 @@ const getPageSpread = properties => {
export class EPUB { export class EPUB {
parser = new DOMParser() parser = new DOMParser()
#loader
#encryption #encryption
constructor({ loadText, loadBlob, getSize, sha1 }) { constructor({ loadText, loadBlob, getSize, sha1 }) {
this.loadText = loadText this.loadText = loadText
@@ -662,7 +666,7 @@ export class EPUB {
opf, opf,
resolveHref: url => resolveURL(url, opfPath), resolveHref: url => resolveURL(url, opfPath),
}) })
const loader = new Loader({ this.#loader = new Loader({
loadText: this.loadText, loadText: this.loadText,
loadBlob: uri => Promise.resolve(this.loadBlob(uri)) loadBlob: uri => Promise.resolve(this.loadBlob(uri))
.then(this.#encryption.getDecoder(uri)), .then(this.#encryption.getDecoder(uri)),
@@ -677,8 +681,8 @@ export class EPUB {
} }
return { return {
id: this.resources.getItemByID(idref)?.href, id: this.resources.getItemByID(idref)?.href,
load: () => loader.loadItem(item), load: () => this.#loader.loadItem(item),
unload: () => loader.unloadItem(item), unload: () => this.#loader.unloadItem(item),
createDocument: () => this.loadDocument(item), createDocument: () => this.loadDocument(item),
size: this.getSize(item.href), size: this.getSize(item.href),
cfi: this.resources.cfis[index], cfi: this.resources.cfis[index],
@@ -805,4 +809,7 @@ export class EPUB {
return JSON.parse(json) return JSON.parse(json)
} }
} }
destroy() {
this.#loader?.destroy()
}
} }
+5
View File
@@ -272,6 +272,7 @@ export const makeFB2 = async blob => {
}), converted] }), converted]
}) })
const urls = []
const sectionData = bodyData[0][0] const sectionData = bodyData[0][0]
// make a separate section for each section in the first body // make a separate section for each section in the first body
.map(({ el, ids }) => { .map(({ el, ids }) => {
@@ -294,6 +295,7 @@ export const makeFB2 = async blob => {
const str = template(el.outerHTML) const str = template(el.outerHTML)
const blob = new Blob([str], { type: MIME.XHTML }) const blob = new Blob([str], { type: MIME.XHTML })
const url = URL.createObjectURL(blob) const url = URL.createObjectURL(blob)
urls.push(url)
const title = normalizeWhitespace( const title = normalizeWhitespace(
el.querySelector('.title, .subtitle, p')?.textContent el.querySelector('.title, .subtitle, p')?.textContent
?? (el.classList.contains('title') ? el.textContent : '')) ?? (el.classList.contains('title') ? el.textContent : ''))
@@ -338,5 +340,8 @@ export const makeFB2 = async blob => {
book.splitTOCHref = href => href?.split('#')?.map(x => Number(x)) ?? [] book.splitTOCHref = href => href?.split('#')?.map(x => Number(x)) ?? []
book.getTOCFragment = (doc, id) => doc.querySelector(`[${dataID}="${id}"]`) book.getTOCFragment = (doc, id) => doc.querySelector(`[${dataID}="${id}"]`)
book.destroy = () => {
for (const url of urls) URL.revokeObjectURL(url)
}
return book return book
} }
+9 -1
View File
@@ -30,6 +30,7 @@ const getViewport = (doc, viewport) => {
} }
class Container { class Container {
#observer = new ResizeObserver(() => this.render())
#element = document.createElement('div') #element = document.createElement('div')
defaultViewport defaultViewport
spread spread
@@ -46,7 +47,7 @@ class Container {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}) })
new ResizeObserver(() => this.render()).observe(this.#element) this.#observer.observe(this.#element)
} }
get element() { get element() {
return this.#element return this.#element
@@ -170,6 +171,10 @@ class Container {
return true return true
} }
} }
destroy() {
this.#observer.unobserve(this.#element)
this.#element.remove()
}
} }
export class FixedLayout { export class FixedLayout {
@@ -289,4 +294,7 @@ export class FixedLayout {
for (const frame of this.#container.element.querySelectorAll('iframe')) for (const frame of this.#container.element.querySelectorAll('iframe'))
frame.contentWindow.getSelection().removeAllRanges() frame.contentWindow.getSelection().removeAllRanges()
} }
destroy() {
this.#container.destroy()
}
} }
+7
View File
@@ -842,6 +842,10 @@ class MOBI6 {
isExternal(uri) { isExternal(uri) {
return /^(?!blob|filepos)\w+:/i.test(uri) return /^(?!blob|filepos)\w+:/i.test(uri)
} }
destroy() {
for (const url of this.#resourceCache.values()) URL.revokeObjectURL(url)
for (const url of this.#cache.values()) URL.revokeObjectURL(url)
}
} }
// handlers for `kindle:` uris // handlers for `kindle:` uris
@@ -1154,4 +1158,7 @@ class KF8 {
isExternal(uri) { isExternal(uri) {
return /^(?!blob|kindle)\w+:/i.test(uri) return /^(?!blob|kindle)\w+:/i.test(uri)
} }
destroy() {
for (const url of this.#cache.values()) URL.revokeObjectURL(url)
}
} }
+6 -1
View File
@@ -329,6 +329,7 @@ class View {
export class Paginator { export class Paginator {
#gap = 0 #gap = 0
#shouldUpdateGap = true #shouldUpdateGap = true
#observer = new ResizeObserver(() => this.render())
#element = document.createElement('div') #element = document.createElement('div')
#background = document.createElement('div') #background = document.createElement('div')
#maxSizeContainer = document.createElement('div') #maxSizeContainer = document.createElement('div')
@@ -394,7 +395,7 @@ export class Paginator {
this.#maxSizeContainer.append(this.#header) this.#maxSizeContainer.append(this.#header)
this.#maxSizeContainer.append(this.#footer) this.#maxSizeContainer.append(this.#footer)
new ResizeObserver(() => this.render()).observe(this.#element) this.#observer.observe(this.#element)
this.#container.addEventListener('scroll', debounce(() => { this.#container.addEventListener('scroll', debounce(() => {
if (this.scrolled) this.#afterScroll('scroll') if (this.scrolled) this.#afterScroll('scroll')
}, 250)) }, 250))
@@ -774,4 +775,8 @@ export class Paginator {
this.#anchor = anchor this.#anchor = anchor
await this.#scrollToAnchor(select) await this.#scrollToAnchor(select)
} }
destroy() {
this.#observer.unobserve(this.#element)
this.#element.remove()
}
} }
+4
View File
@@ -270,4 +270,8 @@ export class View {
subitems: result.subitems, subitems: result.subitems,
} : result } : result
} }
destroy() {
this.book.destroy?.()
this.renderer?.destroy?.()
}
} }