Commit bda664a8 authored by Robert Knight's avatar Robert Knight

Make client injection into VitalSource content frames work more often

Fix an issue where the client failed to inject into some VitalSource book
chapters, by revising the tests used to decide whether a content frame should
have the client injected into it. The previous logic worked for most EPUBs and
PDF books, but failed for:

 - EPUB chapters which had no paragraph content (eg. only an image,
   table or list)

 - "Great Book" format EPUBs, which are used for many of the free
   classic texts available in VitalSource. These use `<div
   class="para">` elements for paragraphs instead of `<p>` elements
   (sigh).
parent 5f7f925e
...@@ -44,11 +44,11 @@ class FakeVitalSourceViewer { ...@@ -44,11 +44,11 @@ class FakeVitalSourceViewer {
// The integration should not inject the client if the frame contains this // The integration should not inject the client if the frame contains this
// data content. // data content.
this.contentFrame.contentDocument.body.innerHTML = this.contentFrame.contentDocument.body.innerHTML =
'<div>Encrypted content</div>'; '<div id="page-content">Encrypted content</div>';
} }
finishChapterLoad() { finishChapterLoad(contentHTML = '<p>New content</p>') {
this.contentFrame.contentDocument.body.innerHTML = '<p>New content</p>'; this.contentFrame.contentDocument.body.innerHTML = contentHTML;
this.contentFrame.dispatchEvent(new Event('load')); this.contentFrame.dispatchEvent(new Event('load'));
} }
} }
...@@ -131,16 +131,32 @@ describe('annotator/integrations/vitalsource', () => { ...@@ -131,16 +131,32 @@ describe('annotator/integrations/vitalsource', () => {
assert.calledWith(fakeInjectClient, fakeViewer.contentFrame, fakeConfig); assert.calledWith(fakeInjectClient, fakeViewer.contentFrame, fakeConfig);
}); });
it('re-injects client when content frame is changed', async () => { [
fakeInjectClient.resetHistory(); // Typical EPUB book chapter which contains text paragraphs.
'<p>New content</p>',
fakeViewer.beginChapterLoad(); // "Great Book" EPUBs used in the freely available classic texts in VitalSource.
await delay(0); // These don't use `<p>` elements :(
assert.notCalled(fakeInjectClient); '<div class="para">New content</div>',
fakeViewer.finishChapterLoad(); // Book chapters which don't contain text content.
await delay(0); '<img src="cover-image.png">',
assert.calledWith(fakeInjectClient, fakeViewer.contentFrame, fakeConfig); ].forEach(newChapterContent => {
it('re-injects client when content frame is changed', async () => {
fakeInjectClient.resetHistory();
fakeViewer.beginChapterLoad();
await delay(0);
assert.notCalled(fakeInjectClient);
fakeViewer.finishChapterLoad(newChapterContent);
await delay(0);
assert.calledWith(
fakeInjectClient,
fakeViewer.contentFrame,
fakeConfig
);
});
}); });
it("doesn't re-inject if content frame is removed", async () => { it("doesn't re-inject if content frame is removed", async () => {
......
...@@ -59,17 +59,28 @@ export class VitalSourceInjector { ...@@ -59,17 +59,28 @@ export class VitalSourceInjector {
/** @param {HTMLIFrameElement} frame */ /** @param {HTMLIFrameElement} frame */
const injectIfContentReady = frame => { const injectIfContentReady = frame => {
// Check if this frame contains decoded ebook content, as opposed to // Check if this frame contains decoded ebook content. If the document has
// invisible and encrypted book content, which is created initially after a // not yet finished loading, then we rely on this function being called
// chapter navigation. These encrypted pages are replaced with the real // again once loading completes.
// content after a form submission.
// const body = frame.contentDocument?.body;
// The format of the decoded HTML can vary, but as a simple heuristic, const isBookContent =
// we look for a text paragraph. body &&
// // Check that this is not the blank document which is displayed in
// If the document has not yet finished loading, then we rely on this function // brand new iframes before any of their content has loaded.
// being called again once loading completes. body.children.length > 0 &&
const isBookContent = frame.contentDocument?.querySelector('p'); // Check that this is not the temporary page containing encrypted and
// invisible book content, which is replaced with the real content after
// a form submission. These pages look something like:
//
// ```
// <html>
// <title>content</title>
// <body><div id="page-content">{ Base64 encoded data }</div></body>
// </html>
// ```
!body.querySelector('#page-content');
if (isBookContent) { if (isBookContent) {
injectClient(frame, config); injectClient(frame, config);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment