Commit bfe0e9a7 authored by Robert Knight's avatar Robert Knight

Limit VitalSource iframe height in PDFs

VitalSource PDF books currently create very tall iframes. When a user
scrolls through a PDF page, they are scrolling the _parent_ frame rather
than the iframe itself. The bucket bar and scrolling logic in the client
don't support this. The result is that:

 - Bucket bar buckets are in the wrong location and don't scroll as the
   user scrolls the parent frame
 - Clicking on an annotation / bucket tries to scroll only the iframe,
   not the parent frame.

To resolve this we need to do at least one of:

1. Support these tall iframes in the client generally
2. Forcibly change the height of the iframe and enable the iframe to scroll
3. Persuade VitalSource to change the iframe layout so that the content frame
   scrolls, rather than the parent frame. They previously did this for EPUBs
   already.

This commit implements a version of (2). If VS later apply (3) then we can
remove this workaround.
parent 24b65649
...@@ -335,6 +335,41 @@ describe('annotator/integrations/vitalsource', () => { ...@@ -335,6 +335,41 @@ describe('annotator/integrations/vitalsource', () => {
assert.notCalled(FakeImageTextLayer); assert.notCalled(FakeImageTextLayer);
}); });
it('installs scrolling workaround for tall frames', async () => {
// Create a shadow DOM and iframe structure that matches the relevant
// parts of the real VS reader.
const bookElement = document.createElement('mosaic-book');
const shadowRoot = bookElement.attachShadow({ mode: 'open' });
document.body.append(bookElement);
const frame = document.createElement('iframe');
frame.style.height = '2000px';
frame.setAttribute('scrolling', 'no');
shadowRoot.append(frame);
const frameElementStub = sinon
.stub(window, 'frameElement')
.get(() => frame);
try {
createIntegration();
assert.isFalse(frame.hasAttribute('scrolling'));
assert.equal(
getComputedStyle(frame).height,
`${window.innerHeight}px` // "100%" in pixels
);
// Try re-adding the scrolling attribute. It should get re-removed.
frame.setAttribute('scrolling', 'no');
await delay(0);
assert.isFalse(frame.hasAttribute('scrolling'));
} finally {
frameElementStub.restore();
bookElement.remove();
}
});
it('creates hidden text layer in PDF documents', () => { it('creates hidden text layer in PDF documents', () => {
createPageImageAndData(); createPageImageAndData();
createIntegration(); createIntegration();
......
...@@ -146,6 +146,38 @@ function getPDFPageImage() { ...@@ -146,6 +146,38 @@ function getPDFPageImage() {
); );
} }
/**
* Fix how a VitalSource book content frame scrolls, so that various related
* Hypothesis behaviors (the bucket bar, scrolling annotations into view) work
* as intended.
*
* Some VitalSource books (PDFs) make content scrolling work by making the
* content iframe really tall and having the parent frame scroll. This stops the
* Hypothesis bucket bar and scrolling annotations into view from working.
*
* @param {HTMLIFrameElement} frame
*/
function makeContentFrameScrollable(frame) {
if (frame.getAttribute('scrolling') !== 'no') {
// This is a book (eg. EPUB) where the workaround is not required.
return;
}
// Override inline styles of iframe (hence `!important`). The iframe lives
// in Shadow DOM, so the element styles won't affect the rest of the app.
const style = document.createElement('style');
style.textContent = `iframe { height: 100% !important; }`;
frame.insertAdjacentElement('beforebegin', style);
const removeScrollingAttr = () => frame.removeAttribute('scrolling');
removeScrollingAttr();
// Sometimes the attribute gets re-added by VS. Remove it if that
// happens.
const attrObserver = new MutationObserver(removeScrollingAttr);
attrObserver.observe(frame, { attributes: true });
}
/** /**
* Integration for the content frame in VitalSource's Bookshelf ebook reader. * Integration for the content frame in VitalSource's Bookshelf ebook reader.
* *
...@@ -184,6 +216,14 @@ export class VitalSourceContentIntegration { ...@@ -184,6 +216,14 @@ export class VitalSourceContentIntegration {
}); });
} }
// Install scrolling workaround for PDFs. We do this in the content frame
// so that it works whether Hypothesis is loaded directly into the content
// frame or injected by VitalSourceInjector from the parent frame.
const frame = /** @type {HTMLIFrameElement|null} */ (window.frameElement);
if (frame) {
makeContentFrameScrollable(frame);
}
// If this is a PDF, create the hidden text layer above the rendered PDF // If this is a PDF, create the hidden text layer above the rendered PDF
// image. // image.
const bookImage = getPDFPageImage(); const bookImage = getPDFPageImage();
......
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