From 0e92f995c9098952213706793d46899f9e604ffb Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Fri, 17 Sep 2021 18:17:40 +0200
Subject: [PATCH] Re-factor the `EventBus` and `isInAutomation` handling (PR
 11655 follow-up)

Rather than forcing the "regular" `EventBus` to check and handle `isInAutomation` for every `dispatch` call, we can take advantage of subclassing instead.
Hence this PR introduces a new `AutomationEventBus` class, which extends `EventBus`, and is used by the default viewer when `isInAutomation === true`.
---
 test/types/main.ts |  2 +-
 web/app.js         | 18 +++++++----
 web/ui_utils.js    | 76 +++++++++++++++++++---------------------------
 3 files changed, 45 insertions(+), 51 deletions(-)

diff --git a/test/types/main.ts b/test/types/main.ts
index e7bc379af..e0b30eb5d 100644
--- a/test/types/main.ts
+++ b/test/types/main.ts
@@ -6,7 +6,7 @@ class MainTest {
   task: ReturnType<typeof getDocument> | undefined;
 
   constructor(public file: string) {
-    this.eventBus = new EventBus({});
+    this.eventBus = new EventBus();
   }
 
   loadPdf() {
diff --git a/web/app.js b/web/app.js
index b0d4be166..f723da066 100644
--- a/web/app.js
+++ b/web/app.js
@@ -18,6 +18,7 @@ import {
   animationStarted,
   apiPageLayoutToSpreadMode,
   apiPageModeToSidebarView,
+  AutomationEventBus,
   AutoPrintRegExp,
   DEFAULT_SCALE_VALUE,
   EventBus,
@@ -458,11 +459,16 @@ const PDFViewerApplication = {
    * @private
    */
   async _initializeViewerComponents() {
-    const appConfig = this.appConfig;
+    const { appConfig, externalServices } = this;
 
-    const eventBus =
-      appConfig.eventBus ||
-      new EventBus({ isInAutomation: this.externalServices.isInAutomation });
+    let eventBus;
+    if (appConfig.eventBus) {
+      eventBus = appConfig.eventBus;
+    } else if (externalServices.isInAutomation) {
+      eventBus = new AutomationEventBus();
+    } else {
+      eventBus = new EventBus();
+    }
     this.eventBus = eventBus;
 
     this.overlayManager = new OverlayManager();
@@ -479,7 +485,7 @@ const PDFViewerApplication = {
     });
     this.pdfLinkService = pdfLinkService;
 
-    const downloadManager = this.externalServices.createDownloadManager();
+    const downloadManager = externalServices.createDownloadManager();
     this.downloadManager = downloadManager;
 
     const findController = new PDFFindController({
@@ -495,7 +501,7 @@ const PDFViewerApplication = {
         PDFJSDev.test("!PRODUCTION || GENERIC || CHROME")
           ? AppOptions.get("sandboxBundleSrc")
           : null,
-      scriptingFactory: this.externalServices,
+      scriptingFactory: externalServices,
       docPropertiesLookup: this._scriptingDocProperties.bind(this),
     });
     this.pdfScriptingManager = pdfScriptingManager;
diff --git a/web/ui_utils.js b/web/ui_utils.js
index ee612c7a2..2b62e0e58 100644
--- a/web/ui_utils.js
+++ b/web/ui_utils.js
@@ -708,43 +708,13 @@ const animationStarted = new Promise(function (resolve) {
   window.requestAnimationFrame(resolve);
 });
 
-/**
- * NOTE: Only used to support various PDF viewer tests in `mozilla-central`.
- */
-function dispatchDOMEvent(eventName, args = null) {
-  if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("MOZCENTRAL")) {
-    throw new Error("Not implemented: dispatchDOMEvent");
-  }
-  const details = Object.create(null);
-  if (args?.length > 0) {
-    const obj = args[0];
-    for (const key in obj) {
-      const value = obj[key];
-      if (key === "source") {
-        if (value === window || value === document) {
-          return; // No need to re-dispatch (already) global events.
-        }
-        continue; // Ignore the `source` property.
-      }
-      details[key] = value;
-    }
-  }
-  const event = document.createEvent("CustomEvent");
-  event.initCustomEvent(eventName, true, true, details);
-  document.dispatchEvent(event);
-}
-
 /**
  * Simple event bus for an application. Listeners are attached using the `on`
  * and `off` methods. To raise an event, the `dispatch` method shall be used.
  */
 class EventBus {
-  constructor(options) {
+  constructor() {
     this._listeners = Object.create(null);
-
-    if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) {
-      this._isInAutomation = options?.isInAutomation === true;
-    }
   }
 
   /**
@@ -774,13 +744,6 @@ class EventBus {
   dispatch(eventName) {
     const eventListeners = this._listeners[eventName];
     if (!eventListeners || eventListeners.length === 0) {
-      if (
-        (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) &&
-        this._isInAutomation
-      ) {
-        const args = Array.prototype.slice.call(arguments, 1);
-        dispatchDOMEvent(eventName, args);
-      }
       return;
     }
     // Passing all arguments after the eventName to the listeners.
@@ -806,12 +769,6 @@ class EventBus {
       }
       externalListeners = null;
     }
-    if (
-      (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) &&
-      this._isInAutomation
-    ) {
-      dispatchDOMEvent(eventName, args);
-    }
   }
 
   /**
@@ -843,6 +800,36 @@ class EventBus {
   }
 }
 
+/**
+ * NOTE: Only used to support various PDF viewer tests in `mozilla-central`.
+ */
+class AutomationEventBus extends EventBus {
+  dispatch(eventName) {
+    if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("MOZCENTRAL")) {
+      throw new Error("Not implemented: AutomationEventBus.dispatch");
+    }
+    super.dispatch(...arguments);
+
+    const details = Object.create(null);
+    if (arguments.length > 1) {
+      const obj = arguments[1];
+      for (const key in obj) {
+        const value = obj[key];
+        if (key === "source") {
+          if (value === window || value === document) {
+            return; // No need to re-dispatch (already) global events.
+          }
+          continue; // Ignore the `source` property.
+        }
+        details[key] = value;
+      }
+    }
+    const event = document.createEvent("CustomEvent");
+    event.initCustomEvent(eventName, true, true, details);
+    document.dispatchEvent(event);
+  }
+}
+
 function clamp(v, min, max) {
   return Math.min(Math.max(v, min), max);
 }
@@ -1012,6 +999,7 @@ export {
   apiPageLayoutToSpreadMode,
   apiPageModeToSidebarView,
   approximateFraction,
+  AutomationEventBus,
   AutoPrintRegExp,
   backtrackBeforeAllVisibleElements, // only exported for testing
   binarySearchFirstItem,