From 23fc0df1f33bd32456a73e09ddc16e19c52a3087 Mon Sep 17 00:00:00 2001 From: Erek Speed Date: Sun, 3 Apr 2022 14:12:21 +0900 Subject: [PATCH] fix(detection): Copy text affecting style properties of input/textarea elements when creating fake (#886) Fixes #672 --- extension/rikaicontent.ts | 23 +++- extension/test/rikaicontent_test.ts | 183 ++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 4 deletions(-) diff --git a/extension/rikaicontent.ts b/extension/rikaicontent.ts index 890e7369b..57a8f5fba 100644 --- a/extension/rikaicontent.ts +++ b/extension/rikaicontent.ts @@ -1066,11 +1066,26 @@ class RcxContent { } else { textValue = real.value; } + const realStyles = window.getComputedStyle(real, ''); fake.innerText = textValue; - fake.style.cssText = document.defaultView!.getComputedStyle( - real, - '' - ).cssText; + // Text areas never visible collapse spaces so always set whiteSpace to pre-wrap + fake.style.whiteSpace = 'pre-wrap'; + fake.style.font = realStyles.font; + fake.style.height = realRect.height + 'px'; + // Without a width, the fake div will expand horizontally + fake.style.width = realRect.width + 'px'; + // Padding moves text inwards so needs to be copied + fake.style.padding = realStyles.padding; + // Border also affects total width sometimes + fake.style.border = realStyles.border; + // Effects total width when padding and border are set. + fake.style.boxSizing = realStyles.boxSizing; + // The overflow property can add scrollbars which affects text placement. + fake.style.overflow = realStyles.overflow; + fake.style.letterSpacing = realStyles.letterSpacing; + // Japanese text is often not broken into words but this could be important for + // mixed language text. + fake.style.wordSpacing = realStyles.wordSpacing; fake.scrollTop = real.scrollTop; fake.scrollLeft = real.scrollLeft; fake.style.position = 'absolute'; diff --git a/extension/test/rikaicontent_test.ts b/extension/test/rikaicontent_test.ts index 16004405e..d09363627 100644 --- a/extension/test/rikaicontent_test.ts +++ b/extension/test/rikaicontent_test.ts @@ -124,6 +124,179 @@ describe('RcxContent', function () { text: '生test', }); }); + + it('triggers xsearch message when text area has custom font styles', function () { + const clock = sinon.useFakeTimers(); + const measuringSpan = insertHtmlIntoDomAndReturnFirstTextNode( + '位の日本語訳・' + ) as HTMLSpanElement; + const input = insertHtmlIntoDomAndReturnFirstTextNode( + '' + ) as HTMLInputElement; + + triggerMousemoveAtElementStartWithOffset(input, { + x: measuringSpan.getBoundingClientRect().width + 1, + y: input.getBoundingClientRect().height / 2, + }); + // Tick the clock forward to account for the popup delay. + clock.tick(1); + + // This value is chosen via experimentation since it's hard to know exactly + // where a character is in an input element. + expect(chrome.runtime.sendMessage).to.have.been.calledWithMatch({ + type: 'xsearch', + text: '中国語訳にも', + }); + }); + + it('triggers xsearch message when text area has custom width and scrollbars', function () { + const clock = sinon.useFakeTimers(); + const measuringDiv = insertHtmlIntoDomAndReturnFirstTextNode( + '
位の
' + ) as HTMLDivElement; + const input = insertHtmlIntoDomAndReturnFirstTextNode( + '