terminal: paste on drop for xterm.js

Fixed: b/261292699
Change-Id: If37a94388fa2c332d158a227d2fad9845bb97d25
Reviewed-on: https://chromium-review.googlesource.com/c/apps/libapps/+/4080083
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Emil Mikulic <easy@google.com>
diff --git a/terminal/js/terminal_emulator.js b/terminal/js/terminal_emulator.js
index 190961b..64e7d94 100644
--- a/terminal/js/terminal_emulator.js
+++ b/terminal/js/terminal_emulator.js
@@ -709,33 +709,16 @@
 
       elem.appendChild(this.contextMenu_);
 
+      elem.addEventListener('dragover', (e) => e.preventDefault());
+      elem.addEventListener('drop',
+          (e) => this.onDrop_(/** @type {!DragEvent} */(e)));
+
       // Block the default context menu from popping up.
       elem.addEventListener('contextmenu', (e) => e.preventDefault());
 
       // Add a handler for pasting with the mouse.
-      elem.addEventListener('mousedown', async (e) => {
-        this.contextMenu_.hide();
-        if (this.term.modes.mouseTrackingMode !== 'none') {
-          // xterm.js is in mouse mode and will handle the event.
-          return;
-        }
-        const MIDDLE = 1;
-        const RIGHT = 2;
-
-        if (e.button === RIGHT && e.ctrlKey) {
-          this.contextMenu_.show({x: e.clientX, y: e.clientY});
-          return;
-        }
-
-        if (e.button === MIDDLE || (e.button === RIGHT &&
-                this.prefs_.getBoolean('mouse-right-click-paste'))) {
-          // Paste.
-          if (navigator.clipboard && navigator.clipboard.readText) {
-            const text = await navigator.clipboard.readText();
-            this.term.paste(text);
-          }
-        }
-      });
+      elem.addEventListener('mousedown',
+          (e) => this.onMouseDown_(/** @type {!MouseEvent} */(e)));
 
       await this.scheduleFit_();
       this.a11yButtons_ = new A11yButtons(this.term, this.htermA11yReader_);
@@ -1001,6 +984,53 @@
     }
   }
 
+  /**
+   * @param {!DragEvent} e
+   */
+  onDrop_(e) {
+    e.preventDefault();
+
+    // If the shift key active, try to find a "rich" text source (but not plain
+    // text).  e.g. text/html is OK. This is the same behavior as hterm.
+    if (e.shiftKey) {
+      for (const type of e.dataTransfer.types) {
+        if (type !== 'text/plain' && type.startsWith('text/')) {
+          this.term.paste(e.dataTransfer.getData(type));
+          return;
+        }
+      }
+    }
+
+    this.term.paste(e.dataTransfer.getData('text/plain'));
+  }
+
+  /**
+   * @param {!MouseEvent} e
+   */
+  async onMouseDown_(e) {
+    this.contextMenu_.hide();
+    if (this.term.modes.mouseTrackingMode !== 'none') {
+      // xterm.js is in mouse mode and will handle the event.
+      return;
+    }
+    const MIDDLE = 1;
+    const RIGHT = 2;
+
+    if (e.button === RIGHT && e.ctrlKey) {
+      this.contextMenu_.show({x: e.clientX, y: e.clientY});
+      return;
+    }
+
+    if (e.button === MIDDLE || (e.button === RIGHT &&
+          this.prefs_.getBoolean('mouse-right-click-paste'))) {
+      // Paste.
+      if (navigator.clipboard && navigator.clipboard.readText) {
+        const text = await navigator.clipboard.readText();
+        this.term.paste(text);
+      }
+    }
+  }
+
   copySelection_() {
     this.copyString_(this.term.getSelection());
   }