From 1fac29d1849ebc4b2b29a090b587d1ffed9f3adf Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Sat, 7 Mar 2020 13:11:51 +0100
Subject: [PATCH 1/2] Slightly improve the `BaseViewer.{firstPagePromise,
 onePageRendered, pagesPromise}` functionality

There's a couple of issues with this functionality:
 - The respective `PromiseCapability` instances are not being reset, in `BaseViewer._resetView`, when the document is closed which is inconsistent with all other state.
 - While the default viewer depends on these promises, and they thus ought to be considered part of e.g. the `PDFViewer` API-surface, they're not really defined in a particularily user-visible way (being that they're attached to the `BaseViewer` instance *inline* in `BaseViewer.setDocument`).
 - There's some internal `BaseViewer` state, e.g. `BaseViewer._pageViewsReady`, which is tracked manually and could instead be tracked indirectly via the relevant `PromiseCapability`, thus reducing the need to track state *twice* since that's always best to avoid.

*Please note:* In the existing implementation, these promises are not defined *until* the `BaseViewer.setDocument` method has been called.
While it would've been simple to lift that restriction in this patch, I'm purposely choosing *not* to do so since this ensures that any Promise handlers added inside of `BaseViewer.setDocument` are always invoked *before* any external ones (and keeping that behaviour seems generally reasonable).
---
 web/app.js         |  4 +---
 web/base_viewer.js | 52 +++++++++++++++++++++++++++-------------------
 2 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/web/app.js b/web/app.js
index ea154e465..00ba6264a 100644
--- a/web/app.js
+++ b/web/app.js
@@ -1057,9 +1057,7 @@ const PDFViewerApplication = {
 
     const pdfViewer = this.pdfViewer;
     pdfViewer.setDocument(pdfDocument);
-    const firstPagePromise = pdfViewer.firstPagePromise;
-    const pagesPromise = pdfViewer.pagesPromise;
-    const onePageRendered = pdfViewer.onePageRendered;
+    const { firstPagePromise, onePageRendered, pagesPromise } = pdfViewer;
 
     const pdfThumbnailViewer = this.pdfThumbnailViewer;
     pdfThumbnailViewer.setDocument(pdfDocument);
diff --git a/web/base_viewer.js b/web/base_viewer.js
index 6395fe177..3861acf95 100644
--- a/web/base_viewer.js
+++ b/web/base_viewer.js
@@ -198,13 +198,13 @@ class BaseViewer {
    * @type {boolean} - True if all {PDFPageView} objects are initialized.
    */
   get pageViewsReady() {
-    if (!this._pageViewsReady) {
+    if (!this._pagesCapability.settled) {
       return false;
     }
     // Prevent printing errors when 'disableAutoFetch' is set, by ensuring
     // that *all* pages have in fact been completely loaded.
     return this._pages.every(function(pageView) {
-      return !!(pageView && pageView.pdfPage);
+      return pageView && pageView.pdfPage;
     });
   }
 
@@ -376,6 +376,21 @@ class BaseViewer {
     }
   }
 
+  get firstPagePromise() {
+    return this.pdfDocument ? this._firstPageCapability.promise : null;
+  }
+
+  get onePageRendered() {
+    return this.pdfDocument ? this._onePageRenderedCapability.promise : null;
+  }
+
+  get pagesPromise() {
+    return this.pdfDocument ? this._pagesCapability.promise : null;
+  }
+
+  /**
+   * @private
+   */
   get _setDocumentViewerElement() {
     // In most viewers, e.g. `PDFViewer`, this should return `this.viewer`.
     throw new Error("Not implemented: _setDocumentViewerElement");
@@ -399,24 +414,15 @@ class BaseViewer {
       return;
     }
     const pagesCount = pdfDocument.numPages;
+    const firstPagePromise = pdfDocument.getPage(1);
 
-    const pagesCapability = createPromiseCapability();
-    this.pagesPromise = pagesCapability.promise;
-
-    pagesCapability.promise.then(() => {
-      this._pageViewsReady = true;
+    this._pagesCapability.promise.then(() => {
       this.eventBus.dispatch("pagesloaded", {
         source: this,
         pagesCount,
       });
     });
 
-    const onePageRenderedCapability = createPromiseCapability();
-    this.onePageRendered = onePageRenderedCapability.promise;
-
-    const firstPagePromise = pdfDocument.getPage(1);
-    this.firstPagePromise = firstPagePromise;
-
     this._onBeforeDraw = evt => {
       const pageView = this._pages[evt.pageNumber - 1];
       if (!pageView) {
@@ -429,10 +435,10 @@ class BaseViewer {
     this.eventBus._on("pagerender", this._onBeforeDraw);
 
     this._onAfterDraw = evt => {
-      if (evt.cssTransform || onePageRenderedCapability.settled) {
+      if (evt.cssTransform || this._onePageRenderedCapability.settled) {
         return;
       }
-      onePageRenderedCapability.resolve();
+      this._onePageRenderedCapability.resolve();
 
       this.eventBus._off("pagerendered", this._onAfterDraw);
       this._onAfterDraw = null;
@@ -443,6 +449,8 @@ class BaseViewer {
     // viewport for all pages
     firstPagePromise
       .then(firstPdfPage => {
+        this._firstPageCapability.resolve(firstPdfPage);
+
         const scale = this.currentScale;
         const viewport = firstPdfPage.getViewport({ scale: scale * CSS_UNITS });
         for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
@@ -485,7 +493,7 @@ class BaseViewer {
         // Fetch all the pages since the viewport is needed before printing
         // starts to create the correct size canvas. Wait until one page is
         // rendered so we don't tie up too many resources early on.
-        onePageRenderedCapability.promise.then(() => {
+        this._onePageRenderedCapability.promise.then(() => {
           if (this.findController) {
             this.findController.setDocument(pdfDocument); // Enable searching.
           }
@@ -497,13 +505,13 @@ class BaseViewer {
             pagesCount > 7500
           ) {
             // XXX: Printing is semi-broken with auto fetch disabled.
-            pagesCapability.resolve();
+            this._pagesCapability.resolve();
             return;
           }
           let getPagesLeft = pagesCount - 1; // The first page was already loaded.
 
           if (getPagesLeft <= 0) {
-            pagesCapability.resolve();
+            this._pagesCapability.resolve();
             return;
           }
           for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
@@ -515,7 +523,7 @@ class BaseViewer {
                 }
                 this.linkService.cachePageRef(pageNum, pdfPage.ref);
                 if (--getPagesLeft === 0) {
-                  pagesCapability.resolve();
+                  this._pagesCapability.resolve();
                 }
               },
               reason => {
@@ -524,7 +532,7 @@ class BaseViewer {
                   reason
                 );
                 if (--getPagesLeft === 0) {
-                  pagesCapability.resolve();
+                  this._pagesCapability.resolve();
                 }
               }
             );
@@ -577,7 +585,9 @@ class BaseViewer {
     this._location = null;
     this._pagesRotation = 0;
     this._pagesRequests = new WeakMap();
-    this._pageViewsReady = false;
+    this._firstPageCapability = createPromiseCapability();
+    this._onePageRenderedCapability = createPromiseCapability();
+    this._pagesCapability = createPromiseCapability();
     this._scrollMode = ScrollMode.VERTICAL;
     this._spreadMode = SpreadMode.NONE;
 

From 3eb4c1940dd94fd11d239bf1b36d3582b6aa3781 Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Sat, 7 Mar 2020 22:24:27 +0100
Subject: [PATCH 2/2] Initialize the `textLayerFactory` once in
 `BaseViewer.setDocument`, rather than repeating it for every page

For reasons that I don't even pretend to understand, the `textLayerFactory` property is determined for *every single* page in the PDF document.
Given that the `TextLayerMode` should be consistent for *all* pages in a document, we obviously could/should define `textLayerFactory` just once instead.
---
 web/base_viewer.js | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/web/base_viewer.js b/web/base_viewer.js
index 3861acf95..4b2fbe734 100644
--- a/web/base_viewer.js
+++ b/web/base_viewer.js
@@ -453,11 +453,10 @@ class BaseViewer {
 
         const scale = this.currentScale;
         const viewport = firstPdfPage.getViewport({ scale: scale * CSS_UNITS });
+        const textLayerFactory =
+          this.textLayerMode !== TextLayerMode.DISABLE ? this : null;
+
         for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
-          let textLayerFactory = null;
-          if (this.textLayerMode !== TextLayerMode.DISABLE) {
-            textLayerFactory = this;
-          }
           const pageView = new PDFPageView({
             container: this._setDocumentViewerElement,
             eventBus: this.eventBus,