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 {
// The integration should not inject the client if the frame contains this
// data content.
this.contentFrame.contentDocument.body.innerHTML =
'<div>Encrypted content</div>';
'<div id="page-content">Encrypted content</div>';
}
finishChapterLoad() {
this.contentFrame.contentDocument.body.innerHTML = '<p>New content</p>';
finishChapterLoad(contentHTML = '<p>New content</p>') {
this.contentFrame.contentDocument.body.innerHTML = contentHTML;
this.contentFrame.dispatchEvent(new Event('load'));
}
}
......@@ -131,16 +131,32 @@ describe('annotator/integrations/vitalsource', () => {
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();
await delay(0);
assert.notCalled(fakeInjectClient);
// "Great Book" EPUBs used in the freely available classic texts in VitalSource.
// These don't use `<p>` elements :(
'<div class="para">New content</div>',
fakeViewer.finishChapterLoad();
await delay(0);
assert.calledWith(fakeInjectClient, fakeViewer.contentFrame, fakeConfig);
// Book chapters which don't contain text content.
'<img src="cover-image.png">',
].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 () => {
......
......@@ -59,17 +59,28 @@ export class VitalSourceInjector {
/** @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');
// Check if this frame contains decoded ebook content. If the document has
// not yet finished loading, then we rely on this function being called
// again once loading completes.
const body = frame.contentDocument?.body;
const isBookContent =
body &&
// Check that this is not the blank document which is displayed in
// brand new iframes before any of their content has loaded.
body.children.length > 0 &&
// 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) {
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