diff options
author | Claire <claire.github-309c@sitedethib.com> | 2022-11-14 22:43:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-14 22:43:56 +0100 |
commit | e589afa0ef520747919935956d2d87746000adc9 (patch) | |
tree | b4df6d9329686bebd09cbfcc107b8fe32c413af3 | |
parent | 7a8cd0cb0a61d5091071d682bfb263247a324d3a (diff) | |
parent | 8aa56aedfd5cb5a2caa4dd070fb6a233e960dbec (diff) |
Merge pull request #1929 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
-rw-r--r-- | CHANGELOG.md | 11 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/features/emoji/emoji.js | 15 | ||||
-rw-r--r-- | app/javascript/mastodon/features/emoji/__tests__/emoji-test.js | 42 | ||||
-rw-r--r-- | app/javascript/mastodon/features/emoji/emoji.js | 15 | ||||
-rw-r--r-- | lib/mastodon/version.rb | 4 |
5 files changed, 55 insertions, 32 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 20da71f80..4392cc658 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,12 @@ Changelog All notable changes to this project will be documented in this file. -## [Unreleased] +## [4.0.1] - 2022-11-14 +### Fixed + +- Fix nodes order being sometimes mangled when rewriting emoji ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20677)) + +## [4.0.0] - 2022-11-14 Some of the features in this release have been funded through the [NGI0 Discovery](https://nlnet.nl/discovery) Fund, a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 825322. @@ -196,6 +201,10 @@ Some of the features in this release have been funded through the [NGI0 Discover ### Security - Fix being able to spoof link verification ([Gargron](https://github.com/mastodon/mastodon/pull/20217)) +- Fix emoji substitution not applying only to text nodes in backend code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20641)) +- Fix emoji substitution not applying only to text nodes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20640)) +- Fix rate limiting for paths with formats ([Gargron](https://github.com/mastodon/mastodon/pull/20675)) +- Fix out-of-bound reads in blurhash transcoder ([delroth](https://github.com/mastodon/mastodon/pull/20388)) ## [3.5.3] - 2022-05-26 ### Added diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js index 6138164e0..880f9401d 100644 --- a/app/javascript/flavours/glitch/features/emoji/emoji.js +++ b/app/javascript/flavours/glitch/features/emoji/emoji.js @@ -19,10 +19,13 @@ const emojiFilename = (filename) => { return borderedEmoji.includes(filename) ? (filename + '_border') : filename; }; +const domParser = new DOMParser(); + const emojifyTextNode = (node, customEmojis) => { - const parentElement = node.parentElement; let str = node.textContent; + const fragment = new DocumentFragment(); + for (;;) { let match, i = 0; @@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => { } } + fragment.append(document.createTextNode(str.slice(0, i))); + if (replacement) { + fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]); + } node.textContent = str.slice(0, i); - parentElement.insertAdjacentHTML('beforeend', replacement); str = str.slice(rend); - node = document.createTextNode(str); - parentElement.append(node); } + + fragment.append(document.createTextNode(str)); + node.parentElement.replaceChild(fragment, node); }; const emojifyNode = (node, customEmojis) => { diff --git a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js index 07b3d8c53..2f19aab7e 100644 --- a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js +++ b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js @@ -11,8 +11,8 @@ describe('emoji', () => { }); it('works with unclosed tags', () => { - expect(emojify('hello>')).toEqual('hello>'); - expect(emojify('<hello')).toEqual('<hello'); + expect(emojify('hello>')).toEqual('hello>'); + expect(emojify('<hello')).toEqual(''); }); it('works with unclosed shortcodes', () => { @@ -22,23 +22,23 @@ describe('emoji', () => { it('does unicode', () => { expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual( - '<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆโ๐ฆ" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />'); + '<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆโ๐ฆ" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg">'); expect(emojify('๐จโ๐ฉโ๐งโ๐ง')).toEqual( - '<img draggable="false" class="emojione" alt="๐จโ๐ฉโ๐งโ๐ง" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />'); - expect(emojify('๐ฉโ๐ฉโ๐ฆ')).toEqual('<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆ" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />'); + '<img draggable="false" class="emojione" alt="๐จโ๐ฉโ๐งโ๐ง" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg">'); + expect(emojify('๐ฉโ๐ฉโ๐ฆ')).toEqual('<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆ" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg">'); expect(emojify('\u2757')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg">'); }); it('does multiple unicode', () => { expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg">'); expect(emojify('\u2757#\uFE0F\u20E3')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"><img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg">'); expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg"> <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg">'); expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual( - 'foo <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" /> bar'); + 'foo <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg"> bar'); }); it('ignores unicode inside of tags', () => { @@ -46,16 +46,16 @@ describe('emoji', () => { }); it('does multiple emoji properly (issue 5188)', () => { - expect(emojify('๐๐๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg" />'); - expect(emojify('๐ ๐ ๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg" />'); + expect(emojify('๐๐๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg"><img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg"><img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg">'); + expect(emojify('๐ ๐ ๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg"> <img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg"> <img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg">'); }); it('does an emoji that has no shortcode', () => { - expect(emojify('๐โ๐จ')).toEqual('<img draggable="false" class="emojione" alt="๐โ๐จ" title="" src="/emoji/1f441-200d-1f5e8.svg" />'); + expect(emojify('๐โ๐จ')).toEqual('<img draggable="false" class="emojione" alt="๐โ๐จ" title="" src="/emoji/1f441-200d-1f5e8.svg">'); }); it('does an emoji whose filename is irregular', () => { - expect(emojify('โ๏ธ')).toEqual('<img draggable="false" class="emojione" alt="โ๏ธ" title=":arrow_lower_left:" src="/emoji/2199.svg" />'); + expect(emojify('โ๏ธ')).toEqual('<img draggable="false" class="emojione" alt="โ๏ธ" title=":arrow_lower_left:" src="/emoji/2199.svg">'); }); it('avoid emojifying on invisible text', () => { @@ -67,26 +67,26 @@ describe('emoji', () => { it('avoid emojifying on invisible text with nested tags', () => { expect(emojify('<span class="invisible">๐<span class="foo">bar</span>๐ด</span>๐')) - .toEqual('<span class="invisible">๐<span class="foo">bar</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg" />'); + .toEqual('<span class="invisible">๐<span class="foo">bar</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg">'); expect(emojify('<span class="invisible">๐<span class="invisible">๐</span>๐ด</span>๐')) - .toEqual('<span class="invisible">๐<span class="invisible">๐</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg" />'); - expect(emojify('<span class="invisible">๐<br/>๐ด</span>๐')) - .toEqual('<span class="invisible">๐<br/>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg" />'); + .toEqual('<span class="invisible">๐<span class="invisible">๐</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg">'); + expect(emojify('<span class="invisible">๐<br>๐ด</span>๐')) + .toEqual('<span class="invisible">๐<br>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg">'); }); it('skips the textual presentation VS15 character', () => { expect(emojify('โด๏ธ')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15 - .toEqual('<img draggable="false" class="emojione" alt="โด" title=":eight_pointed_black_star:" src="/emoji/2734_border.svg" />'); + .toEqual('<img draggable="false" class="emojione" alt="โด" title=":eight_pointed_black_star:" src="/emoji/2734_border.svg">'); }); it('does an simple emoji properly', () => { expect(emojify('โโ')) - .toEqual('<img draggable="false" class="emojione" alt="โ" title=":female_sign:" src="/emoji/2640.svg" /><img draggable="false" class="emojione" alt="โ" title=":male_sign:" src="/emoji/2642.svg" />'); + .toEqual('<img draggable="false" class="emojione" alt="โ" title=":female_sign:" src="/emoji/2640.svg"><img draggable="false" class="emojione" alt="โ" title=":male_sign:" src="/emoji/2642.svg">'); }); it('does an emoji containing ZWJ properly', () => { expect(emojify('๐โโ๏ธ๐โโ๏ธ')) - .toEqual('<img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":female-guard:" src="/emoji/1f482-200d-2640-fe0f_border.svg" /><img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":male-guard:" src="/emoji/1f482-200d-2642-fe0f_border.svg" />'); + .toEqual('<img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":female-guard:" src="/emoji/1f482-200d-2640-fe0f_border.svg"><img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":male-guard:" src="/emoji/1f482-200d-2642-fe0f_border.svg">'); }); }); }); diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 0ab32767a..52a8458fb 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -19,10 +19,13 @@ const emojiFilename = (filename) => { return borderedEmoji.includes(filename) ? (filename + '_border') : filename; }; +const domParser = new DOMParser(); + const emojifyTextNode = (node, customEmojis) => { - const parentElement = node.parentElement; let str = node.textContent; + const fragment = new DocumentFragment(); + for (;;) { let match, i = 0; @@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => { } } + fragment.append(document.createTextNode(str.slice(0, i))); + if (replacement) { + fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]); + } node.textContent = str.slice(0, i); - parentElement.insertAdjacentHTML('beforeend', replacement); str = str.slice(rend); - node = document.createTextNode(str); - parentElement.append(node); } + + fragment.append(document.createTextNode(str)); + node.parentElement.replaceChild(fragment, node); }; const emojifyNode = (node, customEmojis) => { diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 86be34944..b41188814 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,11 +13,11 @@ module Mastodon end def patch - 0 + 1 end def flags - 'rc4' + '' end def suffix |