diff --git a/package.json b/package.json index a0301e3e0..57053780c 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "test:e2e:open": "npm-run-all -p -r start:dev cy:open", "test:watch": "yarn test --no-single-run --auto-watch", "upgrade:annotations": "./build/upgrade_annotations.sh", - "upgrade-pdfjs": "./build/upgrade_pdfjs.sh" + "upgrade:pdfjs": "./build/upgrade_pdfjs.sh" }, "browserslist": [ "last 2 Chrome versions", diff --git a/src/lib/viewers/doc/DocBaseViewer.js b/src/lib/viewers/doc/DocBaseViewer.js index f4d5589e4..2ef9c4780 100644 --- a/src/lib/viewers/doc/DocBaseViewer.js +++ b/src/lib/viewers/doc/DocBaseViewer.js @@ -49,24 +49,28 @@ const MAX_SCALE = 10.0; const MIN_PINCH_SCALE_DELTA = 0.01; const MIN_PINCH_SCALE_VALUE = 0.25; const MIN_SCALE = 0.1; -const MINIMUM_RANGE_REQUEST_FILE_SIZE_NON_US = 26214400; // 25MB +const METRICS_WHITELIST = [ + USER_DOCUMENT_THUMBNAIL_EVENTS.CLOSE, + USER_DOCUMENT_THUMBNAIL_EVENTS.NAVIGATE, + USER_DOCUMENT_THUMBNAIL_EVENTS.OPEN, +]; const MOBILE_MAX_CANVAS_SIZE = 2949120; // ~3MP 1920x1536 const PAGES_UNIT_NAME = 'pages'; +const PDFJS_TEXT_LAYER_MODE = { + DISABLE: 0, // Should match TextLayerMode enum in pdf_viewer.js + ENABLE: 1, + ENABLE_ENHANCE: 2, +}; const PINCH_PAGE_CLASS = 'pinch-page'; const PINCHING_CLASS = 'pinching'; const PRINT_DIALOG_TIMEOUT_MS = 500; -const RANGE_REQUEST_CHUNK_SIZE_NON_US = 524288; // 512KB -const RANGE_REQUEST_CHUNK_SIZE_US = 1048576; // 1MB +const RANGE_CHUNK_SIZE_NON_US = 524288; // 512KB +const RANGE_CHUNK_SIZE_US = 1048576; // 1MB +const RANGE_REQUEST_MINIMUM_SIZE = 26214400; // 25MB const SAFARI_PRINT_TIMEOUT_MS = 1000; // Wait 1s before trying to print const SCROLL_EVENT_THROTTLE_INTERVAL = 200; const THUMBNAILS_SIDEBAR_TRANSITION_TIME = 301; // 301ms const THUMBNAILS_SIDEBAR_TOGGLED_MAP_KEY = 'doc-thumbnails-toggled-map'; -// List of metrics to be emitted only once per session -const METRICS_WHITELIST = [ - USER_DOCUMENT_THUMBNAIL_EVENTS.CLOSE, - USER_DOCUMENT_THUMBNAIL_EVENTS.NAVIGATE, - USER_DOCUMENT_THUMBNAIL_EVENTS.OPEN, -]; class DocBaseViewer extends BaseViewer { //-------------------------------------------------------------------------- @@ -608,6 +612,7 @@ class DocBaseViewer extends BaseViewer { */ initViewer(pdfUrl) { this.bindDOMListeners(); + this.startLoadTimer(); this.pdfEventBus = new this.pdfjsViewer.EventBus(); this.pdfEventBus.on('pagechanging', this.pagechangingHandler); @@ -629,23 +634,32 @@ class DocBaseViewer extends BaseViewer { this.pdfViewer = this.initPdfViewer(); this.pdfLinkService.setViewer(this.pdfViewer); - // If range requests are disabled, request the gzip compressed version of the representation - this.encoding = this.pdfjsLib.disableRange ? ENCODING_TYPES.GZIP : undefined; - - // Use chunk size set in viewer options if available - let rangeChunkSize = this.getViewerOption('rangeChunkSize'); - - // Otherwise, use large chunk size if locale is en-US and the default, - // smaller chunk size if not. This is using a rough assumption that - // en-US users have higher bandwidth to Box. - if (!rangeChunkSize) { - rangeChunkSize = - this.options.location.locale === 'en-US' - ? RANGE_REQUEST_CHUNK_SIZE_US - : RANGE_REQUEST_CHUNK_SIZE_NON_US; - } - + const { file, location } = this.options; + const { size, watermark_info: watermarkInfo } = file; + const assetUrlCreator = createAssetUrlCreator(location); const httpHeaders = {}; + const queryParams = {}; + + // Do not disable create object URL in IE11 or iOS Chrome - pdf.js issues #3977 and #8081 are + // not applicable to Box's use case and disabling causes performance issues + const disableCreateObjectURL = false; + + // Disable font faces on IOS 10.3.X + const disableFontFace = Browser.hasFontIssue(); + + // Disable streaming via fetch until performance is improved + const disableStream = true; + + // Disable range requests for files smaller than MINIMUM_RANGE_REQUEST_FILE_SIZE (25MB) for + // previews outside of the US since the additional latency overhead per range request can be + // more than the additional time for a continuous request. + const isRangeSupported = location.locale !== 'en-US' && size > RANGE_REQUEST_MINIMUM_SIZE; + const isWatermarked = watermarkInfo && watermarkInfo.is_watermarked; + const disableRange = isWatermarked || !isRangeSupported; + + // Use larger chunk sizes because we assume that en-US users have better connections to Box's servers + const rangeChunkSizeDefault = location.locale === 'en-US' ? RANGE_CHUNK_SIZE_US : RANGE_CHUNK_SIZE_NON_US; + const rangeChunkSize = this.getViewerOption('rangeChunkSize') || rangeChunkSizeDefault; // Fix incorrectly cached range requests on older versions of iOS webkit browsers, // see: https://bugs.webkit.org/show_bug.cgi?id=82672 @@ -653,19 +667,21 @@ class DocBaseViewer extends BaseViewer { httpHeaders['If-None-Match'] = 'webkit-no-cache'; } - const queryParams = {}; + // If range requests are disabled, request the gzip compressed version of the representation + this.encoding = disableRange ? ENCODING_TYPES.GZIP : undefined; if (this.encoding) { queryParams[QUERY_PARAM_ENCODING] = this.encoding; } - // Start timing document load - this.startLoadTimer(); - // Load PDF from representation URL and set as document for pdf.js. Cache task for destruction this.pdfLoadingTask = this.pdfjsLib.getDocument({ cMapPacked: true, - cMapUrl: this.pdfJsCmapUrl, + cMapUrl: assetUrlCreator(`third-party/doc/${DOC_STATIC_ASSETS_VERSION}/cmaps/`), + disableCreateObjectURL, + disableFontFace, + disableRange, + disableStream, httpHeaders, rangeChunkSize, url: appendQueryParams(pdfUrl, queryParams), @@ -703,19 +719,37 @@ class DocBaseViewer extends BaseViewer { } /** - * Initialize pdf.js viewer. + * Initialize the pdf.js viewer. * * @protected - * @override * @return {pdfjsViewer.PDFViewer} PDF viewer type */ initPdfViewer() { - return new this.pdfjsViewer.PDFViewer({ + return this.initPdfViewerClass(this.pdfjsViewer.PDFViewer); + } + + /** + * Initialize the inner pdf.js viewer class. + * + * @protected + * @param {Function} PdfViewerClass - the pdf viewer class (PDFViewer, PDFSinglePageViewer, etc.) to initailize + * @returns {*} + */ + initPdfViewerClass(PdfViewerClass) { + const { file, location } = this.options; + const assetUrlCreator = createAssetUrlCreator(location); + const hasDownload = checkPermission(file, PERMISSION_DOWNLOAD); + const hasTextLayer = hasDownload && !this.getViewerOption('disableTextLayer'); + const textLayerMode = this.isMobile ? PDFJS_TEXT_LAYER_MODE.ENABLE : PDFJS_TEXT_LAYER_MODE.ENABLE_ENHANCE; + + return new PdfViewerClass({ container: this.docEl, - enhanceTextSelection: !this.isMobile, // Uses more memory, so disable on mobile eventBus: this.pdfEventBus, findController: this.pdfFindController, + imageResourcesPath: assetUrlCreator(`third-party/doc/${DOC_STATIC_ASSETS_VERSION}/images/`), linkService: this.pdfLinkService, + maxCanvasPixels: this.isMobile ? MOBILE_MAX_CANVAS_SIZE : -1, + textLayerMode: hasTextLayer ? textLayerMode : PDFJS_TEXT_LAYER_MODE.DISABLE, }); } @@ -833,43 +867,12 @@ class DocBaseViewer extends BaseViewer { this.pdfjsLib = window.pdfjsLib; this.pdfjsViewer = window.pdfjsViewer; - // Set pdf.js worker & character maps - const { file, location } = this.options; - const { size, watermark_info: watermarkInfo } = file; + // Set pdf.js worker source location + const { location } = this.options; const assetUrlCreator = createAssetUrlCreator(location); + const workerSrc = assetUrlCreator(`third-party/doc/${DOC_STATIC_ASSETS_VERSION}/pdf.worker.min.js`); - // Set pdf.js worker, image, and character map locations - this.pdfJsCmapUrl = `${location.staticBaseURI}third-party/doc/${DOC_STATIC_ASSETS_VERSION}/cmaps/`; - this.pdfjsLib.imageResourcesPath = assetUrlCreator(`third-party/doc/${DOC_STATIC_ASSETS_VERSION}/images/`); - this.pdfjsLib.workerSrc = assetUrlCreator(`third-party/doc/${DOC_STATIC_ASSETS_VERSION}/pdf.worker.min.js`); - - // Disable streaming via fetch until performance is improved - this.pdfjsLib.disableStream = true; - - // Disable font faces on IOS 10.3.X - // @NOTE(JustinHoldstock) 2017-04-11: Check to remove this after next IOS release after 10.3.1 - this.pdfjsLib.disableFontFace = this.pdfjsLib.disableFontFace || Browser.hasFontIssue(); - - // Disable range requests for files smaller than MINIMUM_RANGE_REQUEST_FILE_SIZE (25MB) for - // previews outside of the US since the additional latency overhead per range request can be - // more than the additional time for a continuous request. This also overrides any range request - // disabling that may be set by pdf.js's compatibility checking since the browsers we support - // should all be able to properly handle range requests. - this.pdfjsLib.disableRange = location.locale !== 'en-US' && size < MINIMUM_RANGE_REQUEST_FILE_SIZE_NON_US; - - // Disable range requests for watermarked files since they are streamed - this.pdfjsLib.disableRange = this.pdfjsLib.disableRange || (watermarkInfo && watermarkInfo.is_watermarked); - - // Disable text layer if user doesn't have download permissions - this.pdfjsLib.disableTextLayer = - !checkPermission(file, PERMISSION_DOWNLOAD) || !!this.getViewerOption('disableTextLayer'); - - // Decrease mobile canvas size to ~3MP (1920x1536) - this.pdfjsLib.maxCanvasPixels = this.isMobile ? MOBILE_MAX_CANVAS_SIZE : this.pdfjsLib.maxCanvasPixels; - - // Do not disable create object URL in IE11 or iOS Chrome - pdf.js issues #3977 and #8081 are - // not applicable to Box's use case and disabling causes performance issues - this.pdfjsLib.disableCreateObjectURL = false; + this.pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc; } /** diff --git a/src/lib/viewers/doc/SinglePageViewer.js b/src/lib/viewers/doc/SinglePageViewer.js index 77dfef14e..5846f9058 100644 --- a/src/lib/viewers/doc/SinglePageViewer.js +++ b/src/lib/viewers/doc/SinglePageViewer.js @@ -13,13 +13,7 @@ class SinglePageViewer extends DocumentViewer { * @return {Object} PDF viewer type */ initPdfViewer() { - return new this.pdfjsViewer.PDFSinglePageViewer({ - container: this.docEl, - enhanceTextSelection: !this.isMobile, // Uses more memory, so disable on mobile - eventBus: this.pdfEventBus, - findController: this.pdfFindController, - linkService: this.pdfLinkService, - }); + return this.initPdfViewerClass(this.pdfjsViewer.PDFSinglePageViewer); } } diff --git a/src/lib/viewers/doc/__tests__/DocBaseViewer-test.js b/src/lib/viewers/doc/__tests__/DocBaseViewer-test.js index f0e4b0804..04dffc62d 100644 --- a/src/lib/viewers/doc/__tests__/DocBaseViewer-test.js +++ b/src/lib/viewers/doc/__tests__/DocBaseViewer-test.js @@ -83,6 +83,7 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { stubs.api = new Api(); stubs.classListAdd = sandbox.stub(rootEl.classList, 'add'); stubs.classListRemove = sandbox.stub(rootEl.classList, 'remove'); + stubs.urlCreator = sandbox.stub(util, 'createAssetUrlCreator').returns(() => 'asset'); }); afterEach(() => { @@ -1016,11 +1017,13 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { describe('initViewer()', () => { beforeEach(() => { stubs.bindDOMListeners = sandbox.stub(docBase, 'bindDOMListeners'); + stubs.checkPermission = sandbox.stub(file, 'checkPermission'); stubs.emit = sandbox.stub(docBase, 'emit'); stubs.getDocument = sandbox.stub().returns({ destroy: sandbox.stub(), promise: Promise.resolve(), }); + stubs.getViewerOption = sandbox.stub(docBase, 'getViewerOption'); stubs.pdfEventBus = { off: sandbox.stub(), on: sandbox.stub(), @@ -1032,12 +1035,19 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { stubs.shouldThumbnailsBeToggled = sandbox.stub(docBase, 'shouldThumbnailsBeToggled'); stubs.resize = sandbox.stub(docBase, 'resize'); + docBase.isMobile = false; + docBase.options.file = { + size: 1000000, + watermark_info: {}, + }; + docBase.options.location = { + locale: 'en-US', + }; docBase.pdfjsLib = { disableRange: false, getDocument: stubs.getDocument, LinkTarget: { NONE: 0, SELF: 1, BLANK: 2, PARENT: 3, TOP: 4 }, }; - docBase.pdfjsViewer = { EventBus: sandbox.stub().returns(stubs.pdfEventBus), PDFFindController: sandbox.stub().returns({ @@ -1049,10 +1059,6 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { }), PDFViewer: stubs.pdfViewerClass, }; - - docBase.options.location = { - locale: 'en-US', - }; }); it('should create an event bus and subscribe to relevant events', () => { @@ -1064,49 +1070,122 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { }); }); + it('should set maxCanvasPixels if on mobile', () => { + docBase.isMobile = true; + + return docBase.initViewer('').then(() => { + expect(stubs.pdfViewerClass).to.be.calledWith( + sinon.match({ maxCanvasPixels: MOBILE_MAX_CANVAS_SIZE }), + ); + }); + }); + + it('should enable the text layer based on download permissions', () => { + stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(true); + + return docBase.initViewer('').then(() => { + expect(stubs.pdfViewerClass).to.be.calledWith(sinon.match({ textLayerMode: 2 })); + }); + }); + + it('should simplify the text layer if the user is on mobile', () => { + docBase.isMobile = true; + stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(true); + + return docBase.initViewer('').then(() => { + expect(stubs.pdfViewerClass).to.be.calledWith(sinon.match({ textLayerMode: 1 })); + }); + }); + + it('should disable the text layer based on download permissions', () => { + stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(false); + + return docBase.initViewer('').then(() => { + expect(stubs.pdfViewerClass).to.be.calledWith(sinon.match({ textLayerMode: 0 })); + }); + }); + + it('should disable the text layer if disableTextLayer viewer option is set', () => { + stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(true); + stubs.getViewerOption.withArgs('disableTextLayer').returns(true); + + return docBase.initViewer('').then(() => { + expect(stubs.pdfViewerClass).to.be.calledWith(sinon.match({ textLayerMode: 0 })); + }); + }); + it('should setup the link controller settings correctly', () => { return docBase.initViewer('').then(() => { - expect(docBase.pdfjsViewer.PDFLinkService).to.be.calledWith({ - eventBus: sinon.match.any, - externalLinkRel: 'noopener noreferrer nofollow', - externalLinkTarget: 2, // window.pdfjsLib.LinkTarget.BLANK - }); + expect(docBase.pdfjsViewer.PDFLinkService).to.be.calledWith( + sinon.match({ + externalLinkRel: 'noopener noreferrer nofollow', + externalLinkTarget: 2, // window.pdfjsLib.LinkTarget.BLANK + }), + ); }); }); - [true, false].forEach(isMobile => { - it('should disable enhanced text selection if on mobile', () => { - docBase.options.location = { - locale: 'en-US', - }; - docBase.isMobile = isMobile; - - return docBase.initViewer('').then(() => { - expect(stubs.pdfViewerClass).to.be.calledWith({ - container: sinon.match.any, - enhanceTextSelection: !isMobile, - eventBus: sinon.match.any, - findController: sinon.match.any, - linkService: sinon.match.any, - }); - }); + it('should test if browser has font rendering issue', () => { + sandbox.stub(Browser, 'hasFontIssue').returns(true); + + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableFontFace: true })); }); }); - it('should set a chunk size based on viewer options if available', () => { - const url = 'url'; - const rangeChunkSize = 100; + it('should not disable streaming', () => { + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableStream: true })); + }); + }); - sandbox.stub(docBase, 'getViewerOption').returns(rangeChunkSize); + it('should enable range requests if locale is not en-US, the file is greater than 25MB', () => { + docBase.options.file.size = 26500000; + docBase.options.location.locale = 'ja-JP'; - return docBase.initViewer(url).then(() => { - expect(stubs.getDocument).to.be.calledWith({ - cMapPacked: true, - cMapUrl: sinon.match.any, - httpHeaders: sinon.match.object, - rangeChunkSize, - url: sinon.match.string, - }); + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableRange: false })); + }); + }); + + it('should disable range requests if locale is not en-US and the file is smaller than 25MB', () => { + docBase.options.file.size = 26000000; + docBase.options.location.locale = 'ja-JP'; + + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableRange: true })); + }); + }); + + it('should disable range requests if the file is greater than 25MB but watermarked', () => { + docBase.options.file.size = 26500000; + docBase.options.file.watermark_info.is_watermarked = true; + docBase.options.location.locale = 'ja-JP'; + + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableRange: true })); + }); + }); + + it('should disable range requests if the locale is en-US', () => { + docBase.options.location.locale = 'en-US'; + + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableRange: true })); + }); + }); + + it('should set disableCreateObjectURL to false', () => { + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ disableCreateObjectURL: false })); + }); + }); + + it('should set a chunk size based on viewer options if available', () => { + stubs.getViewerOption.returns(100); + + return docBase.initViewer('').then(() => { + expect(stubs.getDocument).to.be.calledWith(sinon.match({ rangeChunkSize: 100 })); }); }); @@ -1117,16 +1196,10 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { docBase.options.location = { locale: 'not-en-US', }; - sandbox.stub(docBase, 'getViewerOption').returns(null); + stubs.getViewerOption.returns(null); return docBase.initViewer(url).then(() => { - expect(stubs.getDocument).to.be.calledWith({ - cMapPacked: true, - cMapUrl: sinon.match.any, - httpHeaders: sinon.match.object, - rangeChunkSize: defaultChunkSize, - url: sinon.match.string, - }); + expect(stubs.getDocument).to.be.calledWith(sinon.match({ rangeChunkSize: defaultChunkSize })); }); }); @@ -1137,16 +1210,15 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { docBase.options.location = { locale: 'en-US', }; - sandbox.stub(docBase, 'getViewerOption').returns(null); + stubs.getViewerOption.returns(null); return docBase.initViewer(url).then(() => { - expect(stubs.getDocument).to.be.calledWith({ - cMapPacked: true, - cMapUrl: sinon.match.any, - httpHeaders: sinon.match.object, - rangeChunkSize: largeChunkSize, - url: sinon.match.string, - }); + expect(stubs.getDocument).to.be.calledWith( + sinon.match({ + httpHeaders: sinon.match.object, + rangeChunkSize: largeChunkSize, + }), + ); }); }); @@ -1157,15 +1229,14 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { sandbox.stub(Browser, 'isIOS').returns(true); return docBase.initViewer('').then(() => { - expect(stubs.getDocument).to.be.calledWith({ - cMapPacked: true, - cMapUrl: sinon.match.any, - httpHeaders: { - 'If-None-Match': 'webkit-no-cache', - }, - rangeChunkSize: 1048576, - url: sinon.match.string, - }); + expect(stubs.getDocument).to.be.calledWith( + sinon.match({ + httpHeaders: { + 'If-None-Match': 'webkit-no-cache', + }, + rangeChunkSize: 1048576, + }), + ); }); }); @@ -1193,27 +1264,22 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { }); it('should append encoding query parameter for gzip content when range requests are disabled', () => { - // en-US allows for disabled range requests - docBase.options.location = { - locale: 'en-US', - }; const defaultChunkSize = 1048576; // Taken from RANGE_REQUEST_CHUNK_SIZE_US const url = 'www.myTestPDF.com/123456'; const paramsList = `${QUERY_PARAM_ENCODING}=${ENCODING_TYPES.GZIP}`; - const isDisabled = docBase.pdfjsLib.disableRange; - sandbox.stub(Browser, 'isIOS').returns(false); - docBase.pdfjsLib.disableRange = true; - return docBase.initViewer(url).then(() => { - expect(stubs.getDocument).to.be.calledWith({ - cMapPacked: true, - cMapUrl: sinon.match.any, - httpHeaders: sinon.match.object, - rangeChunkSize: defaultChunkSize, - url: `${url}?${paramsList}`, - }); - // Reset to original value - docBase.pdfjsLib.disableRange = isDisabled; + docBase.options.location = { + locale: 'en-US', // Disables range requests + }; + + return docBase.initViewer(url).then(() => { + expect(stubs.getDocument).to.be.calledWith( + sinon.match({ + httpHeaders: sinon.match.object, + rangeChunkSize: defaultChunkSize, + url: `${url}?${paramsList}`, + }), + ); }); }); @@ -1222,7 +1288,7 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { url: 'url', }; stubs.getDocument.returns({ promise: Promise.resolve(doc) }); - sandbox.stub(docBase, 'getViewerOption').returns(100); + stubs.getViewerOption.returns(100); return docBase.initViewer('url').then(() => { expect(stubs.bindDOMListeners).to.be.called; @@ -1239,7 +1305,7 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { url: 'url', }; stubs.getDocument.returns({ promise: Promise.resolve(doc) }); - sandbox.stub(docBase, 'getViewerOption').returns(100); + stubs.getViewerOption.returns(100); sandbox.stub(docBase, 'startLoadTimer'); docBase.initViewer('url'); @@ -1266,7 +1332,7 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { const doc = { url: 'url', }; - sandbox.stub(docBase, 'getViewerOption').returns(100); + stubs.getViewerOption.returns(100); stubs.getDocument.returns({ promise: Promise.resolve(doc) }); stubs.shouldThumbnailsBeToggled.returns(true); @@ -1442,112 +1508,17 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => { }); describe('setupPdfjs()', () => { - beforeEach(() => { - stubs.browser = sandbox.stub(Browser, 'getName').returns('Safari'); - stubs.checkPermission = sandbox.stub(file, 'checkPermission'); - stubs.getViewerOption = sandbox.stub(docBase, 'getViewerOption'); - stubs.urlCreator = sandbox.stub(util, 'createAssetUrlCreator').returns(() => 'asset'); - + it('should set the worker source asset url', () => { docBase.options = { + file: {}, location: { staticBaseURI: 'test/', locale: 'en-US', }, - file: { - size: 10000000, - extension: 'pdf', - watermark_info: { - is_watermarked: false, - }, - permissions: { - can_download: undefined, - }, - }, - }; - - docBase.pdfJsLib = { - disableFontFace: false, - disableRange: false, }; - }); - - it('should create the asset url', () => { docBase.setupPdfjs(); - expect(docBase.pdfjsLib.workerSrc).to.equal('asset'); - }); - - // @NOTE(JustinHoldstock) 2017-04-11: Check to remove or modify this after next IOS release after 10.3.1 or - // Safari version - it('should test if browser has font rendering issue', () => { - sandbox.stub(Browser, 'hasFontIssue').returns(true); - - docBase.setupPdfjs(); - - expect(docBase.pdfjsLib.disableFontFace).to.be.true; - }); - it('should not disable streaming', () => { - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableStream).to.be.true; - }); - - it('should not disable range requests if the locale is en-US', () => { - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableRange).to.be.false; - }); - - it('should disable range requests if locale is not en-US, the file is smaller than 25MB and is not an Excel file', () => { - docBase.options.file.size = 26000000; - docBase.options.extension = 'pdf'; - docBase.options.location.locale = 'ja-JP'; - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableRange).to.be.true; - }); - - it('should enable range requests if locale is not en-US, the file is greater than 25MB and is not watermarked', () => { - docBase.options.location.locale = 'ja-JP'; - docBase.options.file.size = 26500000; - docBase.options.extension = 'pdf'; - docBase.options.file.watermark_info.is_watermarked = false; - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableRange).to.be.false; - }); - - it('should disable range requests if the file is watermarked', () => { - docBase.options.location.locale = 'ja-JP'; - docBase.options.file.watermark_info.is_watermarked = true; - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableRange).to.be.true; - }); - - it('should disable or enable text layer based on download permissions', () => { - stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(true); - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableTextLayer).to.be.false; - - stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(false); - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableTextLayer).to.be.true; - }); - - it('should disable the text layer if disableTextLayer viewer option is set', () => { - stubs.checkPermission.withArgs(docBase.options.file, PERMISSION_DOWNLOAD).returns(true); - stubs.getViewerOption.withArgs('disableTextLayer').returns(true); - - docBase.setupPdfjs(); - - expect(docBase.pdfjsLib.disableTextLayer).to.be.true; - }); - - it('should decrease max canvas size to 3MP if on mobile', () => { - docBase.isMobile = true; - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.maxCanvasPixels).to.equal(MOBILE_MAX_CANVAS_SIZE); - }); - - it('should set disableCreateObjectURL to false', () => { - docBase.setupPdfjs(); - expect(docBase.pdfjsLib.disableCreateObjectURL).to.equal(false); + expect(docBase.pdfjsLib.GlobalWorkerOptions.workerSrc).to.equal('asset'); }); }); diff --git a/src/lib/viewers/doc/__tests__/DocFindBar-test.js b/src/lib/viewers/doc/__tests__/DocFindBar-test.js index d9d2a7227..afe3b3b23 100644 --- a/src/lib/viewers/doc/__tests__/DocFindBar-test.js +++ b/src/lib/viewers/doc/__tests__/DocFindBar-test.js @@ -28,7 +28,7 @@ describe('lib/viewers/doc/DocFindBar', () => { beforeEach(() => { fixture.load('viewers/doc/__tests__/DocFindBar-test.html'); - eventBus = { off: sandbox.stub, on: sandbox.stub() }; + eventBus = { off: sandbox.stub(), on: sandbox.stub() }; findBarEl = document.querySelector('.test-find-bar'); findController = { executeCommand: sandbox.stub(), diff --git a/src/lib/viewers/doc/__tests__/SinglePageViewer-test.js b/src/lib/viewers/doc/__tests__/SinglePageViewer-test.js index 3fea660a8..213763f13 100644 --- a/src/lib/viewers/doc/__tests__/SinglePageViewer-test.js +++ b/src/lib/viewers/doc/__tests__/SinglePageViewer-test.js @@ -1,6 +1,7 @@ /* eslint-disable no-unused-expressions */ import SinglePageViewer from '../SinglePageViewer'; import BaseViewer from '../../BaseViewer'; +import * as util from '../../../util'; const sandbox = sinon.sandbox.create(); let containerEl; @@ -52,6 +53,7 @@ describe('lib/viewers/doc/SinglePageViewer', () => { setDocument: sandbox.stub(), }; stubs.pdfViewerClass = sandbox.stub().returns(stubs.pdfViewer); + stubs.urlCreator = sandbox.stub(util, 'createAssetUrlCreator').returns(() => 'asset'); doc.pdfjsViewer = { PDFSinglePageViewer: stubs.pdfViewerClass, @@ -61,13 +63,7 @@ describe('lib/viewers/doc/SinglePageViewer', () => { it('should return the default pdfViewer', () => { const result = doc.initPdfViewer(); - expect(doc.pdfjsViewer.PDFSinglePageViewer).to.be.calledWith({ - container: sinon.match.any, - enhanceTextSelection: true, - eventBus: sinon.match.any, - findController: sinon.match.any, - linkService: sinon.match.any, - }); + expect(doc.pdfjsViewer.PDFSinglePageViewer).to.be.called; expect(result).to.equal(stubs.pdfViewer); }); }); diff --git a/src/lib/viewers/doc/_docBase.scss b/src/lib/viewers/doc/_docBase.scss index 0ec9f1097..61968eb2c 100644 --- a/src/lib/viewers/doc/_docBase.scss +++ b/src/lib/viewers/doc/_docBase.scss @@ -5,7 +5,9 @@ $thumbnail-border-radius: 3px; $thumbnail-sidebar-width: 226px; @mixin bp-breakpoint-medium { - @media (min-width: 600px) { @content; } + @media (min-width: 600px) { + @content; + } } .bp { @@ -266,11 +268,27 @@ $thumbnail-sidebar-width: 226px; user-select: text; } - // This is so hover and click targets for links inside the document show - // up correctly. We add 15px top margin since PDF.js sets the top for these - // manually assuming there is no vertical padding on the page - .annotationLayer section { - margin-top: 15px; + .annotationLayer { + h1 { + font-size: 1em; + } + + // This is so hover and click targets for links inside the document show + // up correctly. We add 15px top margin since PDF.js sets the top for these + // manually assuming there is no vertical padding on the page + section { + margin-top: 15px; + } + + // Prevent placeholder {{ date }}, {{ time }} text from displaying when + // hovering over baked-in pdf annotations + span[data-l10n-id] { + display: none; + } + + .popup { + font-size: inherit; + } } }