terminal: support OSC 52 for copy for xterm.js

Bug: b/236205389
Change-Id: I90168ef268058d26f5abadeed966224962d32283
Reviewed-on: https://chromium-review.googlesource.com/c/apps/libapps/+/3864200
Reviewed-by: Joel Hockey <joelhockey@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/terminal/js/terminal_emulator.js b/terminal/js/terminal_emulator.js
index 4568fdc..b821926 100644
--- a/terminal/js/terminal_emulator.js
+++ b/terminal/js/terminal_emulator.js
@@ -178,6 +178,7 @@
         () => this.onFontLoadingDone_());
 
     this.installUnimplementedStubs_();
+    this.installEscapeSequenceHandlers_();
 
     this.term.onResize(({cols, rows}) => this.io.onTerminalResize(cols, rows));
     // We could also use `this.io.sendString()` except for the nassh exit
@@ -261,6 +262,32 @@
     };
   }
 
+  installEscapeSequenceHandlers_() {
+    // OSC 52 for copy.
+    this.term.parser.registerOscHandler(52, (args) => {
+      // Args comes in as a single 'clipboard;b64-data' string.  The clipboard
+      // parameter is used to select which of the X clipboards to address. Since
+      // we're not integrating with X, we treat them all the same.
+      const parsedArgs = args.match(/^[cps01234567]*;(.*)/);
+      if (!parsedArgs) {
+        return true;
+      }
+
+      let data;
+      try {
+        data = window.atob(parsedArgs[1]);
+      } catch (e) {
+        // If the user sent us invalid base64 content, silently ignore it.
+        return true;
+      }
+      const decoder = new TextDecoder();
+      const bytes = lib.codec.stringToCodeUnitArray(data);
+      this.copyString_(decoder.decode(bytes));
+
+      return true;
+    });
+  }
+
   /**
    * One-time initialization at the beginning.
    */
@@ -463,11 +490,15 @@
   }
 
   copySelection_() {
-    const selection = this.term.getSelection();
-    if (!selection) {
+    this.copyString_(this.term.getSelection());
+  }
+
+  /** @param {string} data */
+  copyString_(data) {
+    if (!data) {
       return;
     }
-    navigator.clipboard?.writeText(selection);
+    navigator.clipboard?.writeText(data);
     if (!this.copyNotice_) {
       this.copyNotice_ = document.createElement('terminal-copy-notice');
     }