-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle multi-step chapter load process more reliably in VitalSource integration #4145
Changes from all commits
8c86870
db186b3
3973afc
742d1e2
351a8c4
43fb409
dab139a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,20 +55,45 @@ export class VitalSourceContainerIntegration { | |
throw new Error('Book container element not found'); | ||
} | ||
|
||
/** @type {WeakSet<HTMLIFrameElement>} */ | ||
const contentFrames = new WeakSet(); | ||
|
||
/** @param {HTMLIFrameElement} frame */ | ||
const injectIfContentReady = frame => { | ||
// Check if this frame contains decoded ebook content, as opposed to | ||
// invisible and encrypted book content, which is created initially after a | ||
// chapter navigation. These encrypted pages are replaced with the real | ||
// content after a form submission. | ||
// | ||
// The format of the decoded HTML can vary, but as a simple heuristic, | ||
// we look for a text paragraph. | ||
// | ||
// If the document has not yet finished loading, then we rely on this function | ||
// being called again once loading completes. | ||
const isBookContent = frame.contentDocument?.querySelector('p'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The document could be not ready yet. I suggest you wait for the readiness of the document before making the query. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the document is not ready yet, this check may fail. However in that case we'll retry when the |
||
if (isBookContent) { | ||
annotator.injectClient(frame); | ||
} | ||
}; | ||
|
||
const shadowRoot = /** @type {ShadowRoot} */ (bookElement.shadowRoot); | ||
const injectClientIntoContentFrame = () => { | ||
const frame = shadowRoot.querySelector('iframe'); | ||
if (frame) { | ||
annotator.injectClient(frame); | ||
if (!frame || contentFrames.has(frame)) { | ||
// Either there is no content frame or we are already watching it. | ||
return; | ||
} | ||
contentFrames.add(frame); | ||
|
||
injectIfContentReady(frame); | ||
frame.addEventListener('load', () => { | ||
injectIfContentReady(frame); | ||
}); | ||
}; | ||
|
||
injectClientIntoContentFrame(); | ||
|
||
// Re-inject client into content frame after a chapter navigation. | ||
// | ||
// We currently don't do any debouncing here and rely on `injectClient` to | ||
// be idempotent and cheap. | ||
this._frameObserver = new MutationObserver(injectClientIntoContentFrame); | ||
this._frameObserver.observe(shadowRoot, { childList: true, subtree: true }); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is to track if the Hypothesis client has already been injected into this iframe's document. However, it stores the iframe element.
Is re-injection of the client possible?
I think this check is unnecessary if
hasHypothesis
function worked.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used to track whether we have set up the
load
event listener on the frame.