From e7944d29b0380d46e44cc316e10a3088e9da3a8c Mon Sep 17 00:00:00 2001
From: toasted-nutbread <toasted-nutbread@users.noreply.github.com>
Date: Sat, 20 Aug 2022 14:32:34 -0400
Subject: TextSourceElement surrogate pair support (#2217)

* Update StringUtil

* Refactor

* Handle UTF-16 surrogate pairs
---
 ext/js/data/sandbox/string-util.js | 15 ++++++++++-----
 ext/js/dom/text-source-element.js  | 30 +++++++++++++++++-------------
 2 files changed, 27 insertions(+), 18 deletions(-)

(limited to 'ext')

diff --git a/ext/js/data/sandbox/string-util.js b/ext/js/data/sandbox/string-util.js
index 65d73eef..72b8fc7f 100644
--- a/ext/js/data/sandbox/string-util.js
+++ b/ext/js/data/sandbox/string-util.js
@@ -27,16 +27,19 @@ class StringUtil {
      * @returns {string} The code points from the string.
      */
     static readCodePointsForward(text, position, count) {
+        const textLength = text.length;
         let result = '';
         for (; count > 0; --count) {
             const char = text[position];
-            const charCode = char.charCodeAt(0);
             result += char;
-            if (charCode >= 0xd800 && charCode < 0xdc00 && ++position < text.length) {
+            if (++position >= textLength) { break; }
+            const charCode = char.charCodeAt(0);
+            if (charCode >= 0xd800 && charCode < 0xdc00) {
                 const char2 = text[position];
                 const charCode2 = char2.charCodeAt(0);
                 if (charCode2 >= 0xdc00 && charCode2 < 0xe000) {
                     result += char2;
+                    if (++position >= textLength) { break; }
                 }
             }
         }
@@ -54,13 +57,15 @@ class StringUtil {
         let result = '';
         for (; count > 0; --count) {
             const char = text[position];
-            const charCode = char.charCodeAt(0);
             result = char + result;
-            if (charCode >= 0xdc00 && charCode < 0xe000 && position > 0) {
-                const char2 = text[position - 1];
+            if (--position < 0) { break; }
+            const charCode = char.charCodeAt(0);
+            if (charCode >= 0xdc00 && charCode < 0xe000) {
+                const char2 = text[position];
                 const charCode2 = char2.charCodeAt(0);
                 if (charCode2 >= 0xd800 && charCode2 < 0xdc00) {
                     result = char2 + result;
+                    if (--position < 0) { break; }
                 }
             }
         }
diff --git a/ext/js/dom/text-source-element.js b/ext/js/dom/text-source-element.js
index 257b9df4..fe3fe083 100644
--- a/ext/js/dom/text-source-element.js
+++ b/ext/js/dom/text-source-element.js
@@ -15,6 +15,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+/* global
+ * StringUtil
+ */
+
 class TextSourceElement {
     constructor(element, fullContent=null, startOffset=0, endOffset=0) {
         this._element = element;
@@ -61,24 +65,24 @@ class TextSourceElement {
     }
 
     setEndOffset(length, _layoutAwareScan, fromEnd) {
-        if (fromEnd) {
-            const delta = Math.min(this._fullContent.length - this._endOffset, length);
-            this._endOffset += delta;
-            this._content = this._fullContent.substring(this._startOffset, this._endOffset);
-            return delta;
-        } else {
-            const delta = Math.min(this._fullContent.length - this._startOffset, length);
-            this._endOffset = this._startOffset + delta;
-            this._content = this._fullContent.substring(this._startOffset, this._endOffset);
-            return delta;
+        const offset = fromEnd ? this._endOffset : this._startOffset;
+        length = Math.min(this._fullContent.length - offset, length);
+        if (length > 0) {
+            length = StringUtil.readCodePointsForward(this._fullContent, offset, length).length;
         }
+        this._endOffset = offset + length;
+        this._content = this._fullContent.substring(this._startOffset, this._endOffset);
+        return length;
     }
 
     setStartOffset(length) {
-        const delta = Math.min(this._startOffset, length);
-        this._startOffset -= delta;
+        length = Math.min(this._startOffset, length);
+        if (length > 0) {
+            length = StringUtil.readCodePointsBackward(this._fullContent, this._startOffset - 1, length).length;
+        }
+        this._startOffset -= length;
         this._content = this._fullContent.substring(this._startOffset, this._endOffset);
-        return delta;
+        return length;
     }
 
     collapse(toStart) {
-- 
cgit v1.2.3