From a4786c96898de09cb6174f4abe6b7d19009d1cb6 Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Wed, 23 Dec 2020 11:39:54 +0100
Subject: [PATCH] [Scripting] Await manually triggered `dispatchEventInSandbox`
 calls in the viewer

Given that the `dispatchEventInSandbox` method (on the scripting-classes) is asynchronous, there's a very real risk that the events won't be dispatched/handled until *after* their associated functionality has actually run (with the "Will..." events being particularily susceptible to this issue).
To reduce the likelihood of that happening, we can simply `await` the `dispatchEventInSandbox` calls as necessary. A couple of methods are now marked as `async` to support these changes, however that shouldn't be a problem as far as I can tell.

*Please note:* Given that the browser "beforeprint"/"afterprint" events are *synchronous*, we unfortunately cannot await the `WillPrint`/`DidPrint` event dispatching. To fix this properly the web-platform would need support for asynchronous printing, and we'll thus have to hope that things work correctly anyway.
---
 web/app.js | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/web/app.js b/web/app.js
index 6040dc854..21266514b 100644
--- a/web/app.js
+++ b/web/app.js
@@ -1013,7 +1013,7 @@ const PDFViewerApplication = {
       .catch(downloadByUrl); // Error occurred, try downloading with the URL.
   },
 
-  save({ sourceEventType = "download" } = {}) {
+  async save({ sourceEventType = "download" } = {}) {
     if (this._saveInProgress) {
       return;
     }
@@ -1036,12 +1036,13 @@ const PDFViewerApplication = {
       this.download({ sourceEventType });
       return;
     }
-    this._scriptingInstance?.scripting.dispatchEventInSandbox({
+    this._saveInProgress = true;
+
+    await this._scriptingInstance?.scripting.dispatchEventInSandbox({
       id: "doc",
       name: "WillSave",
     });
 
-    this._saveInProgress = true;
     this.pdfDocument
       .saveDocument(this.pdfDocument.annotationStorage)
       .then(data => {
@@ -1051,8 +1052,8 @@ const PDFViewerApplication = {
       .catch(() => {
         this.download({ sourceEventType });
       })
-      .finally(() => {
-        this._scriptingInstance?.scripting.dispatchEventInSandbox({
+      .finally(async () => {
+        await this._scriptingInstance?.scripting.dispatchEventInSandbox({
           id: "doc",
           name: "DidSave",
         });
@@ -1614,7 +1615,7 @@ const PDFViewerApplication = {
       return;
     }
 
-    scripting.dispatchEventInSandbox({
+    await scripting.dispatchEventInSandbox({
       id: "doc",
       name: "Open",
     });
@@ -1967,6 +1968,8 @@ const PDFViewerApplication = {
   },
 
   beforePrint() {
+    // Given that the "beforeprint" browser event is synchronous, we
+    // unfortunately cannot await the scripting event dispatching here.
     this._scriptingInstance?.scripting.dispatchEventInSandbox({
       id: "doc",
       name: "WillPrint",
@@ -2033,6 +2036,8 @@ const PDFViewerApplication = {
   },
 
   afterPrint() {
+    // Given that the "afterprint" browser event is synchronous, we
+    // unfortunately cannot await the scripting event dispatching here.
     this._scriptingInstance?.scripting.dispatchEventInSandbox({
       id: "doc",
       name: "DidPrint",