terminal: init xterm.js options before calling `open()`

This is more efficient and prevents some flashing.

Bug: b/236205389
Change-Id: I5832799ae0093854f8fef2eebd2d2fcf506aaa76
Reviewed-on: https://chromium-review.googlesource.com/c/apps/libapps/+/3868655
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 c346cc9..a8348c6 100644
--- a/terminal/js/terminal_emulator.js
+++ b/terminal/js/terminal_emulator.js
@@ -158,6 +158,7 @@
     this.ctrlVKeyDownHandler_ = this.ctrlVKeyDownHandler_.bind(this);
     this.zoomKeyDownHandler_ = this.zoomKeyDownHandler_.bind(this);
 
+    this.inited_ = false;
     this.profileId_ = profileId;
     /** @type {!hterm.PreferenceManager} */
     this.prefs_ = new hterm.PreferenceManager(storage, profileId);
@@ -169,7 +170,12 @@
     this.fitAddon = testParams?.fitAddon || new FitAddon();
 
     this.term.loadAddon(this.fitAddon);
-    this.scheduleFit_ = delayedScheduler(() => this.fitAddon.fit(),
+    this.scheduleFit_ = delayedScheduler(
+        () => {
+          if (this.inited_) {
+            this.fitAddon.fit();
+          }
+        },
         testParams ? 0 : 250);
 
     this.term.loadAddon(new WebLinksAddon());
@@ -294,15 +300,6 @@
   }
 
   /**
-   * One-time initialization at the beginning.
-   */
-  async init() {
-    await new Promise((resolve) => this.prefs_.readStorage(resolve));
-    this.prefs_.notifyAll();
-    this.onTerminalReady();
-  }
-
-  /**
    * Write data to the terminal.
    *
    * @param {string|!Uint8Array} data string for UTF-16 data, Uint8Array for
@@ -336,15 +333,26 @@
    * @override
    */
   decorate(elem) {
-    this.term.open(elem);
-    this.scheduleFit_();
-    if (this.enableWebGL_) {
-      this.term.loadAddon(new WebglAddon());
-    }
-    this.term.focus();
-    (new ResizeObserver(() => this.scheduleFit_())).observe(elem);
-    // TODO: Make a11y work. Maybe we can just use `hterm.AccessibilityReader`.
-    this.notificationCenter_ = new hterm.NotificationCenter(document.body);
+    (async () => {
+      await new Promise((resolve) => this.prefs_.readStorage(resolve));
+      // This will trigger all the observers to set the terminal options before
+      // we call `this.term.open()`.
+      this.prefs_.notifyAll();
+
+      this.inited_ = true;
+      this.term.open(elem);
+
+      this.scheduleFit_();
+      if (this.enableWebGL_) {
+        this.term.loadAddon(new WebglAddon());
+      }
+      this.term.focus();
+      (new ResizeObserver(() => this.scheduleFit_())).observe(elem);
+      // TODO: Make a11y work. Maybe we can just use hterm.AccessibilityReader.
+      this.notificationCenter_ = new hterm.NotificationCenter(document.body);
+
+      this.onTerminalReady();
+    })();
   }
 
   /** @override */
@@ -458,15 +466,25 @@
    * @param {!Object} theme
    */
   updateTheme_(theme) {
-    const newTheme = {...this.term.options.theme};
-    for (const key in theme) {
-      newTheme[key] = lib.colors.normalizeCSS(theme[key]);
+    const updateTheme = (target) => {
+      for (const [key, value] of Object.entries(theme)) {
+        target[key] = lib.colors.normalizeCSS(value);
+      }
+    };
+
+    // Must use a new theme object to trigger re-render if we have initialized.
+    if (this.inited_) {
+      const newTheme = {...this.term.options.theme};
+      updateTheme(newTheme);
+      this.term.options.theme = newTheme;
+      return;
     }
-    this.updateOption_('theme', newTheme);
+
+    updateTheme(this.term.options.theme);
   }
 
   /**
-   * Update one xterm.js option.
+   * Update one xterm.js option. Use updateTheme_() for theme.
    *
    * @param {string} key
    * @param {*} value
@@ -489,7 +507,7 @@
   async onFontLoadingDone_() {
     // If there is a pending font, the font is going to be refresh soon, so we
     // don't need to do anything.
-    if (!this.pendingFont_) {
+    if (this.inited_ && !this.pendingFont_) {
       this.scheduleRefreshFont_();
     }
   }
@@ -809,9 +827,6 @@
           profileId,
           enableWebGL: config.webgl,
         });
-        // Don't await it so that the caller can override
-        // `terminal.onTerminalReady()` before the terminal is ready.
-        terminal.init();
         return terminal;
       }
     case 'hterm':
diff --git a/terminal/js/terminal_emulator_tests.js b/terminal/js/terminal_emulator_tests.js
index e0f41ab..1e3b764 100644
--- a/terminal/js/terminal_emulator_tests.js
+++ b/terminal/js/terminal_emulator_tests.js
@@ -35,6 +35,7 @@
         enableWebGL: true,
         testParams: /** @type {!XtermTerminalTestParams} */(testParams),
       });
+      this.terminal.inited_ = true;
     });
 
     describe('updateFont_()', function() {