diff --git a/.github/release.yaml b/.github/release.yaml
index 0f4884c9444e4b563eb90bd01b51e0dd3a29bf98..f550e520207cc346358dd69b5cdb8a135aa3ea71 100644
--- a/.github/release.yaml
+++ b/.github/release.yaml
@@ -1,18 +1,18 @@
-changelog:
-  exclude:
-    labels:
-      - ignore-for-release
-    authors:
-      - dependabot 
-  categories:
-    - title: Breaking Changes 🛠
-      labels:
-        - semver-major
-        - breaking-change
-    - title: New Features 🎉
-      labels:
-        - semver-minor
-        - enhancement
-    - title: Other Changes
-      labels:
-        - '*'
+changelog:
+  exclude:
+    labels:
+      - ignore-for-release
+    authors:
+      - dependabot
+  categories:
+    - title: Breaking Changes 🛠
+      labels:
+        - semver-major
+        - breaking-change
+    - title: New Features 🎉
+      labels:
+        - semver-minor
+        - enhancement
+    - title: Other Changes
+      labels:
+        - '*'
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 70a3747b71b18dce10e5f5714dec4eb372359a9d..df49f5fa420d8c4a2136de4a89eb4b6a8715d93c 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -24,6 +24,8 @@ jobs:
     outputs:
       viewer_channel: ${{ steps.build.outputs.viewer_channel }}
       viewer_version: ${{ steps.build.outputs.viewer_version }}
+      viewer_branch:  ${{ steps.which-branch.outputs.branch }}
+      relnotes:       ${{ steps.which-branch.outputs.relnotes }}
       imagename: ${{ steps.build.outputs.imagename }}
     env:
       AUTOBUILD_ADDRSIZE: 64
@@ -334,7 +336,7 @@ jobs:
           version: ${{ needs.build.outputs.viewer_version }}
 
   release:
-    needs: [sign-and-package-windows, sign-and-package-mac]
+    needs: [build, sign-and-package-windows, sign-and-package-mac]
     runs-on: ubuntu-latest
     if: github.ref_type == 'tag' && startsWith(github.ref_name, 'Second_Life_')
     steps:
@@ -365,17 +367,31 @@ jobs:
           mv newview/viewer_version.txt macOS-viewer_version.txt
 
       # forked from softprops/action-gh-release
-      - uses: secondlife-3p/action-gh-release@v1
+      - name: Create GitHub release
+        id: release
+        uses: secondlife-3p/action-gh-release@v1
         with:
-          # name the release page for the build number so we can find it
-          # easily (analogous to looking up a codeticket build page)
-          name: "v${{ github.run_id }}"
+          # name the release page for the branch
+          name: "${{ needs.build.outputs.viewer_branch }}"
+          # SL-20546: want the channel and version to be visible on the
+          # release page
+          body: |
+            Build ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+            ${{ needs.build.outputs.viewer_channel }}
+            ${{ needs.build.outputs.viewer_version }}
+            ${{ needs.build.outputs.relnotes }}
           prerelease: true
           generate_release_notes: true
-          # the only reason we generate a GH release is to post build products
+          target_commitish: ${{ github.sha }}
+          previous_tag: release
+          append_body: true
           fail_on_unmatched_files: true
           files: |
             *.dmg 
             *.exe
             *-autobuild-package.xml
             *-viewer_version.txt
+
+      - name: post release URL
+        run: |
+          echo "::notice::Release ${{ steps.release.outputs.url }}"
diff --git a/.gitignore b/.gitignore
index 371b8383c0cb9a42c0c6bb0f8e3256bff2a3f5e2..3b3666af84348403a310430cc39cea408b46940a 100755
--- a/.gitignore
+++ b/.gitignore
@@ -57,6 +57,7 @@ indra/newview/dbghelp.dll
 indra/newview/filters.xml
 indra/newview/fmod.dll
 indra/newview/fmod.log
+indra/newview/fonts
 indra/newview/mozilla-theme
 indra/newview/mozilla-universal-darwin.tgz
 indra/newview/pilot.txt
@@ -68,6 +69,7 @@ indra/newview/teleport_history.txt
 indra/newview/typed_locations.txt
 indra/newview/vivox-runtime
 indra/newview/skins/default/html/common/equirectangular/js
+emoji_characters.xml
 indra/server-linux-*
 indra/temp
 indra/test/linden_file.dat
diff --git a/autobuild.xml b/autobuild.xml
index 531b3586d6f7d73dabe8d305d710f6195a580ea3..11b2783bd72c29f0f93bed460517bf77a4b842eb 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -104,37 +104,25 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>59c1827cab82516504a2eb31e0aa7e38035b5085</string>
+              <string>8539775e0a0783bd252bc548b20b3472a8254c31</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-boost/releases/download/v1.81-90bb2df/boost-1.81-darwin64-90bb2df.tar.zst</string>
+              <string>https://github.com/secondlife/3p-boost/releases/download/v1.81-09d25a7/boost-1.81-darwin64-09d25a7.tar.zst</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
           </map>
-          <key>linux64</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>038853b97307a9b65de20c4c50098023</string>
-              <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9675/45694/boost-1.65.1-linux64-509640.tar.bz2</string>
-            </map>
-            <key>name</key>
-            <string>linux64</string>
-          </map>
           <key>windows64</key>
           <map>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>26214a33c568929ffeeb3463ce183f2888ce4fe4</string>
+              <string>d40c86fbcb6ce064d546165cbabbf035ea80e07b</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-boost/releases/download/v1.81-90bb2df/boost-1.81-windows64-90bb2df.tar.zst</string>
+              <string>https://github.com/secondlife/3p-boost/releases/download/v1.81-09d25a7/boost-1.81-windows64-09d25a7.tar.zst</string>
             </map>
             <key>name</key>
             <string>windows64</string>
@@ -147,7 +135,7 @@
         <key>copyright</key>
         <string>(see individual source files)</string>
         <key>version</key>
-        <string>1.81</string>
+        <string>1.81-09d25a7</string>
         <key>name</key>
         <string>boost</string>
         <key>description</key>
@@ -208,37 +196,25 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>7f447d30d7add80270a55cf3c53000392821a1cb</string>
+              <string>b1bb8a9c8d458d8842d79f9633fb61df12f1b0ad</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-colladadom/releases/download/v2.3.d1ef72a/colladadom-2.3.d1ef72a-darwin64-d1ef72a.tar.zst</string>
+              <string>https://github.com/secondlife/3p-colladadom/releases/download/v2.3.ab0c124/colladadom-2.3.ab0c124-darwin64-ab0c124.tar.zst</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
           </map>
-          <key>linux64</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>c90613240ba3e3a171d3379275ae4ee3</string>
-              <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9695/45732/colladadom-2.3.509683-linux64-509683.tar.bz2</string>
-            </map>
-            <key>name</key>
-            <string>linux64</string>
-          </map>
           <key>windows64</key>
           <map>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>b32294a2f31f5b4ca49928e66832aad1bb4a88ac</string>
+              <string>0df4c05d4efa3019afa4cbf09599df60b586fc5c</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-colladadom/releases/download/v2.3.d1ef72a/colladadom-2.3.d1ef72a-windows64-d1ef72a.tar.zst</string>
+              <string>https://github.com/secondlife/3p-colladadom/releases/download/v2.3.ab0c124/colladadom-2.3.ab0c124-windows64-ab0c124.tar.zst</string>
             </map>
             <key>name</key>
             <string>windows64</string>
@@ -251,7 +227,7 @@
         <key>copyright</key>
         <string>Copyright 2006 Sony Computer Entertainment Inc.</string>
         <key>version</key>
-        <string>2.3.d1ef72a</string>
+        <string>2.3.ab0c124</string>
         <key>name</key>
         <string>colladadom</string>
       </map>
@@ -477,6 +453,50 @@
         <key>description</key>
         <string>A headless browser SDK that uses the Chromium Embedded Framework (CEF). It is designed to make it easier to write applications that render modern web content directly to a memory buffer, inject synthesized mouse and keyboard events as well as interact with web based features like JavaScript or cookies.</string>
       </map>
+      <key>emoji_shortcodes</key>
+      <map>
+        <key>canonical_repo</key>
+        <string>https://github.com/secondlife/3p-emoji-shortcodes</string>
+        <key>copyright</key>
+        <string>Copyright 2017-2019 Miles Johnson.</string>
+        <key>description</key>
+        <string>Emoji shortcodes</string>
+        <key>license</key>
+        <string>MIT</string>
+        <key>license_file</key>
+        <string>LICENSES/emojibase-license.txt</string>
+        <key>name</key>
+        <string>emoji_shortcodes</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>7ac35da9b1b5c9a05954edeef3fe8e54</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/113242/980233/emoji_shortcodes-6.1.0.579438-darwin64-579438.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin64</string>
+          </map>
+          <key>windows64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>087ce7e6d93dcd88b477b10d8e1ab259</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/113243/980244/emoji_shortcodes-6.1.0.579438-windows64-579438.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>6.1.0.579438</string>
+      </map>
       <key>expat</key>
       <map>
         <key>platforms</key>
@@ -635,6 +655,16 @@
       </map>
       <key>freetype</key>
       <map>
+        <key>copyright</key>
+        <string>Copyright 2006, 2007, 2008, 2009, 2010 by David Turner, Robert Wilhelm, and Werner Lemberg.</string>
+        <key>description</key>
+        <string>Font rendering library</string>
+        <key>license</key>
+        <string>FreeType</string>
+        <key>license_file</key>
+        <string>LICENSES/freetype.txt</string>
+        <key>name</key>
+        <string>freetype</string>
         <key>platforms</key>
         <map>
           <key>darwin64</key>
@@ -642,11 +672,11 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>912d122aae996483ba814fe8e569394ddca0d42e</string>
+              <string>d90a5c2fb4a729eeff3965ea6dd0a35cf146d379</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-freetype/releases/download/v2.4.4.4f739fa/freetype-2.4.4.4f739fa-darwin64-4f739fa.tar.zst</string>
+              <string>https://github.com/secondlife/3p-freetype/releases/download/v.2.12.1.557becd/freetype-2.12.1.557becd-darwin64-557becd.tar.zst</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -656,42 +686,46 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>14f57822f0cedef957a50a03a7b5372075cf8e1c</string>
+              <string>4a999279562e8f3e4ba02d3e78a844ddf6fe18f1</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-freetype/releases/download/v2.4.4.4f739fa/freetype-2.4.4.4f739fa-linux64-4f739fa.tar.zst</string>
+              <string>https://github.com/secondlife/3p-freetype/releases/download/v.2.12.1.557becd/freetype-2.12.1.557becd-linux64-557becd.tar.zst</string>
             </map>
             <key>name</key>
             <string>linux64</string>
           </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>4fecff41a38d67d6b1c97c7c73980667b6a8a1bd</string>
+              <key>hash_algorithm</key>
+              <string>sha1</string>
+              <key>url</key>
+              <string>https://github.com/secondlife/3p-freetype/releases/download/v.2.12.1.557becd/freetype-2.12.1.557becd-windows-557becd.tar.zst</string>
+            </map>
+            <key>name</key>
+            <string>windows</string>
+          </map>
           <key>windows64</key>
           <map>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>d175b39257b691a957724e655c6cffe0b5a7b104</string>
+              <string>1837fdfd44204c78e79134944f824b0211817883</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-freetype/releases/download/v2.4.4.4f739fa/freetype-2.4.4.4f739fa-windows64-4f739fa.tar.zst</string>
+              <string>https://github.com/secondlife/3p-freetype/releases/download/v.2.12.1.557becd/freetype-2.12.1.557becd-windows64-557becd.tar.zst</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
-        <key>license</key>
-        <string>FreeType</string>
-        <key>license_file</key>
-        <string>LICENSES/freetype.txt</string>
-        <key>copyright</key>
-        <string>Copyright 2006, 2007, 2008, 2009, 2010 by David Turner, Robert Wilhelm, and Werner Lemberg.</string>
         <key>version</key>
-        <string>2.4.4.4f739fa</string>
-        <key>name</key>
-        <string>freetype</string>
-        <key>description</key>
-        <string>Font rendering library</string>
+        <string>2.12.1.557becd</string>
       </map>
       <key>glext</key>
       <map>
@@ -766,37 +800,25 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>c016d7333a3ded88c060119b4e3a5847015a8711</string>
+              <string>dce3174b12136746f5f910e311e895c1b47bf8fb</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-googlemock/releases/download/v1.7.0.77bba00/googlemock-1.7.0.77bba00-darwin64-77bba00.tar.zst</string>
+              <string>https://github.com/secondlife/3p-googlemock/releases/download/v1.7.0.2b109d4/googlemock-1.7.0.2b109d4-darwin64-2b109d4.tar.zst</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
           </map>
-          <key>linux64</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>ff459b58695c76838782847a0b792104</string>
-              <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9697/45717/googlemock-1.7.0.509686-linux64-509686.tar.bz2</string>
-            </map>
-            <key>name</key>
-            <string>linux64</string>
-          </map>
           <key>windows64</key>
           <map>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>df51dff9a820fc96c18c2bc00b64467e541633a5</string>
+              <string>265813f84b04c3b03f3d7d33e149b3d5e3cf31db</string>
               <key>hash_algorithm</key>
               <string>sha1</string>
               <key>url</key>
-              <string>https://github.com/secondlife/3p-googlemock/releases/download/v1.7.0.77bba00/googlemock-1.7.0.77bba00-windows64-77bba00.tar.zst</string>
+              <string>https://github.com/secondlife/3p-googlemock/releases/download/v1.7.0.2b109d4/googlemock-1.7.0.2b109d4-windows64-2b109d4.tar.zst</string>
             </map>
             <key>name</key>
             <string>windows64</string>
@@ -809,7 +831,7 @@
         <key>copyright</key>
         <string>Copyright 2008, Google Inc.</string>
         <key>version</key>
-        <string>1.7.0.77bba00</string>
+        <string>1.7.0.2b109d4</string>
         <key>name</key>
         <string>googlemock</string>
         <key>description</key>
@@ -933,6 +955,54 @@
         <key>description</key>
         <string>Havok source code for libs and demos</string>
       </map>
+      <key>icu4c</key>
+      <map>
+        <key>canonical_repo</key>
+        <string>https://bitbucket.org/lindenlab/3p-icu4c</string>
+        <key>copyright</key>
+        <string>Copyright (c) 1995-2011 International Business Machines Corporation and others &lt;http://source.icu-project.org&gt;</string>
+        <key>description</key>
+        <string>ICU is a mature, widely used set of C/C++ and Java libraries providing Unicode and Globalization support for software applications. ICU is widely portable and gives applications the same results on all platforms and between C/C++ and Java software.</string>
+        <key>license</key>
+        <string>ICU, permissive non-copyleft free software license</string>
+        <key>license_file</key>
+        <string>LICENSES/icu.txt</string>
+        <key>name</key>
+        <string>icu4c</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>47bc32b991385f1a6530e4c6179b07f64ca6edc7</string>
+              <key>hash_algorithm</key>
+              <string>sha1</string>
+              <key>url</key>
+              <string>https://github.com/secondlife/3p-icu4c/releases/download/v4.8.1-7d08d82/icu4c-4.8.1-darwin64-7d08d82.tar.zst</string>
+            </map>
+            <key>name</key>
+            <string>darwin64</string>
+          </map>
+          <key>windows64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>b7db881dac80302e4d9010af34c0bf6ca9897df9</string>
+              <key>hash_algorithm</key>
+              <string>sha1</string>
+              <key>url</key>
+              <string>https://github.com/secondlife/3p-icu4c/releases/download/v4.8.1-7d08d82/icu4c-4.8.1-windows64-7d08d82.tar.zst</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>4.8.1-7d08d82</string>
+      </map>
       <key>jpegencoderbasic</key>
       <map>
         <key>platforms</key>
@@ -1844,6 +1914,62 @@
         <key>description</key>
         <string>minizip-ng is a zip manipulation library. Based on work of Gilles Vollant.</string>
       </map>
+      <key>nanosvg</key>
+      <map>
+        <key>canonical_repo</key>
+        <string>https://bitbucket.org/lindenlab/3p-nanosvg</string>
+        <key>copyright</key>
+        <string>Copyright (c) 2013-14 Mikko Mononen</string>
+        <key>description</key>
+        <string>NanoSVG is a simple single-header-file SVG parser and rasterizer</string>
+        <key>license</key>
+        <string>Zlib</string>
+        <key>license_file</key>
+        <string>LICENSES/nanosvg.txt</string>
+        <key>name</key>
+        <string>nanosvg</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>32ead724319c2ea6f65fc5be0e3157cc</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/115452/994130/nanosvg-2022.09.27-darwin64-580364.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin64</string>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>84698f044598ff79e255965f3d1c3e80</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/115397/993664/nanosvg-2022.09.27-linux-580337.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>linux</string>
+          </map>
+          <key>windows64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>ee61ff8b866be04c325f1fe2db516d71</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/115454/994144/nanosvg-2022.09.27-windows64-580364.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>2022.09.27</string>
+      </map>
       <key>nghttp2</key>
       <map>
         <key>platforms</key>
@@ -2550,6 +2676,48 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
         <key>description</key>
         <string>uriparser is a strictly RFC 3986 compliant URI parsing and handling library written in C. uriparser is cross-platform, fast, supports Unicode and is licensed under the New BSD license.</string>
       </map>
+      <key>viewer-fonts</key>
+      <map>
+        <key>copyright</key>
+        <string>Copyright 2016-2022 Brad Erickson CC-BY-4.0/MIT, Copyright 2016-2022 Twitter, Inc. CC-BY-4.0, Copyright 2013 Joe Loughry and Terence Eden MIT</string>
+        <key>description</key>
+        <string>Viewer fonts</string>
+        <key>license</key>
+        <string>Various open source</string>
+        <key>license_file</key>
+        <string>LICENSES/fonts.txt</string>
+        <key>name</key>
+        <string>viewer-fonts</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>6041bbd4001e3951f96ac3456c7906da</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/113314/980656/viewer_fonts-1.579464-darwin64-579464.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>darwin64</string>
+          </map>
+          <key>windows64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>1745ba6eec0108250446fe01d4aa065c</string>
+              <key>url</key>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/113307/980631/viewer_fonts-1.579464-windows64-579464.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>1.579464</string>
+      </map>
       <key>viewer-manager</key>
       <map>
         <key>platforms</key>
@@ -2867,7 +3035,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=TRUE</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -2887,11 +3055,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>../indra</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>RelWithDebInfoOS</string>
@@ -2908,7 +3076,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=TRUE</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -2928,11 +3096,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
                   <string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>../indra</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>ReleaseOS</string>
@@ -2953,11 +3121,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                 <array>
                   <string>-G</string>
                   <string>Xcode</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>../indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -2970,7 +3138,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-project</string>
                   <string>SecondLife.xcodeproj</string>
                   <string>-parallelizeTargets</string>
-</array>
+                </array>
               </map>
               <key>default</key>
               <string>True</string>
@@ -2985,7 +3153,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                 <array>
                   <string>-G</string>
                   <string>Xcode</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -2998,7 +3166,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-project</string>
                   <string>SecondLife.xcodeproj</string>
                   <string>-parallelizeTargets</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>RelWithDebInfoOS</string>
@@ -3011,11 +3179,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                 <array>
                   <string>-G</string>
                   <string>Xcode</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>../indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3028,7 +3196,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-project</string>
                   <string>SecondLife.xcodeproj</string>
                   <string>-parallelizeTargets</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>Release</string>
@@ -3041,7 +3209,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                 <array>
                   <string>-G</string>
                   <string>Xcode</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3054,7 +3222,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-project</string>
                   <string>SecondLife.xcodeproj</string>
                   <string>-parallelizeTargets</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>ReleaseOS</string>
@@ -3078,11 +3246,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-G</string>
                   <string>Ninja</string>
                   <string>-DLL_TESTS=Off</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>../indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3103,7 +3271,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-G</string>
                   <string>Ninja</string>
                   <string>-DLL_TESTS=Off</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3141,11 +3309,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN}</string>
                   <string>-A</string>
                   <string>${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>..\indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3155,11 +3323,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                 <array>
                   <string>/build</string>
                   <string>RelWithDebInfo|${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>SecondLife.sln</string>
-</array>
+                </array>
               </map>
               <key>default</key>
               <string>True</string>
@@ -3179,11 +3347,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
                   <string>-DUSE_KDU=FALSE</string>
                   <string>-DUSE_OPENAL:BOOL=ON</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>..\indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3197,11 +3365,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>/p:useenv=true</string>
                   <string>/verbosity:minimal</string>
                   <string>/p:VCBuildAdditionalOptions= /incremental</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>SecondLife.sln</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>RelWithDebInfoOS</string>
@@ -3216,11 +3384,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN}</string>
                   <string>-A</string>
                   <string>${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>..\indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3230,11 +3398,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                 <array>
                   <string>/build</string>
                   <string>Release|${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>SecondLife.sln</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>Release</string>
@@ -3253,11 +3421,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>-DINSTALL_PROPRIETARY=FALSE</string>
                   <string>-DUSE_KDU=FALSE</string>
                   <string>-DUSE_OPENAL:BOOL=ON</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>..\indra</string>
-</array>
+                </array>
               </map>
               <key>build</key>
               <map>
@@ -3271,11 +3439,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>/p:useenv=true</string>
                   <string>/verbosity:minimal</string>
                   <string>/p:VCBuildAdditionalOptions= /incremental</string>
-</array>
+                </array>
                 <key>arguments</key>
                 <array>
                   <string>SecondLife.sln</string>
-</array>
+                </array>
               </map>
               <key>name</key>
               <string>ReleaseOS</string>
diff --git a/doc/contributions.txt b/doc/contributions.txt
index a097aad7f63da59771d358f7da0365671ed02dc1..c902a118741b405747e79b30f1f560fe459e6c9e 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -239,6 +239,8 @@ Ansariel Hiller
 	SL-15398
 	SL-18432
 	SL-19140
+	SL-19575
+	SL-19623
 	SL-4126
 	SL-20224
 Aralara Rajal
@@ -898,6 +900,7 @@ Kitty Barnett
 	STORM-2149
 	MAINT-7581
 	MAINT-7081
+	DRTVWR-489 (Internal JIRA that tracks Kitty's sizeable, epic contribution: support for Emoji characters in the Viewer [April 2023])
     SL-18988
 Kolor Fall
 Komiko Okamoto
diff --git a/doc/testplans/pbr_terrain_composition.md b/doc/testplans/pbr_terrain_composition.md
new file mode 100644
index 0000000000000000000000000000000000000000..127fe950a8039324640ba0391a0064a77e16a149
--- /dev/null
+++ b/doc/testplans/pbr_terrain_composition.md
@@ -0,0 +1,59 @@
+# PBR Terrain Composition
+
+## Feature Availability
+
+PBR Terrain is visible for all viewers with the PBR Terrain feature, regardless of if the feature flag is enabled.
+
+There is only one set of four asset IDs applied to the terrain. In other words, unlike PBR materials on prims, there is no fallback texture set for viewers that do not support PBR terrain. Viewers without support will view terrain as blank (solid grey or white).
+
+## Editing Terrain Composition
+
+All tests in this section assume the PBR terrain feature flag is enabled, and that the user has appropriate permissions to modify the terrain textures.
+
+### Feature Availability
+
+When the PBR terrain feature flag is disabled:
+
+- The "PBR Metallic Roughness" checkbox should not be visible
+- The user should not be able to apply PBR terrain to the region, only textures.
+
+When the PBR terrain feature flag is enabled:
+
+- The "PBR Metallic Roughness" checkbox should be visible
+- The user should be able to apply PBR terrain or textures to the region, depending on if the "PBR Metallic Roughness" checkbox is checked.
+
+### Current Composition Type
+
+When the Region/Estate floater is opened to the terrain Tab, the current terrain should be shown in the four swatches, and the "PBR Metallic Roughness" checkbox should be checked or unchecked accordingly.
+
+- If it is texture terrain, the "PBR Metallic Roughness" checkbox should be unchecked, and the floater should display the four textures applied to the terrain.
+- If it is material terrain, the "PBR Metallic Roughness" checkbox should be checked, and the floater should display the four materials applied to the terrain.
+
+### Toggling Composition Type
+
+When toggling the "PBR Metallic Roughness" checkbox to the opposite value, which does not correspond to the current terrain type, one of the following sets of four terrain swatches will be displayed:
+
+- The default textures/materials
+    - For textures, this is the default terrain texture set
+    - For materials, this is blank/null, but this is subject to change
+- The previously applied texture/material set
+    - History is available on a best-effort basis only. In particular, the history does not persist on viewer restart.
+
+When toggling back the "PBR Metallic Roughness" checkbox to the original value, assuming nothing else has changed, then the current terrain should be shown in the four swatches again.
+
+### Saving Composition
+
+A user with appropriate permissions can change and save the textures or materials to the terrain. If the "PBR Metallic Roughness" checkbox is checked, the user applies materials, otherwise the user applies textures.
+
+Saving may fail for the following reasons:
+
+- A terrain or material texture is invalid or null
+- A terrain texture is greater than 1024 resolution (Subject to change. See https://github.com/secondlife/viewer/issues/760)
+
+Unlike a viewer without PBR terrain support, the new viewer will no longer treat textures with alpha channels as invalid.
+
+## Graphics Features
+
+Texture terrain with transparency will render as opaque. Parts of the texture that would be partially transparent will instead display as a mix of the color and black, depending on how transparent the texture is.
+
+See [PBR Terrain Feature Gating](./pbr_terrain_feature_gating.md) for supported PBR terrain features.
diff --git a/doc/testplans/pbr_terrain_feature_gating.md b/doc/testplans/pbr_terrain_feature_gating.md
index 13108c1534f2870c30a105a8f09cc34ec291a4ce..2d27a5d73e6ac89ed0bbd13b88b0dcf7a4f6918f 100644
--- a/doc/testplans/pbr_terrain_feature_gating.md
+++ b/doc/testplans/pbr_terrain_feature_gating.md
@@ -20,3 +20,7 @@ Availability of PBR textures varies by machine and graphics setting:
 - Low: Base color only (looks similar to texture terrain)
 - Medium-Low, and machines that do not support greater than 16 textures such as Macs: All PBR textures enabled except emissive textures.
 - Medium: All PBR textures enabled
+
+## PBR Alpha
+
+PBR terrain does not support materials with alpha blend or double-sided. In addition, the viewer does not make any guarantees about what will render behind the terrain if alpha is used.
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 1fd83eadff3bb9509152d7b9c40d802076e5d815..05c51c018d7084281ca1ea50416e67e5dda826d8 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -30,6 +30,7 @@ set(cmake_SOURCE_FILES
         GoogleMock.cmake
         Havok.cmake
         Hunspell.cmake
+        ICU4C.cmake
         JsonCpp.cmake
         LLAddBuildTest.cmake
         LLAppearance.cmake
@@ -64,7 +65,7 @@ set(cmake_SOURCE_FILES
         VisualLeakDetector.cmake
         LibVLCPlugin.cmake
         XmlRpcEpi.cmake
-    xxHash.cmake
+        xxHash.cmake
         ZLIBNG.cmake
         )
 
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 7938d4f54b7b648992e3847e80476643464f817f..9f79c13a97b33be19aecc024bd55c450b05533c1 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -62,6 +62,15 @@ if(WINDOWS)
         uriparser.dll
         )
 
+    # ICU4C (same filenames for 32 and 64 bit builds)
+    set(release_files ${release_files} icudt48.dll)
+    set(release_files ${release_files} icuin48.dll)
+    set(release_files ${release_files} icuio48.dll)
+    set(release_files ${release_files} icule48.dll)
+    set(release_files ${release_files} iculx48.dll)
+    set(release_files ${release_files} icutu48.dll)
+    set(release_files ${release_files} icuuc48.dll)
+
     # OpenSSL
     if(ADDRESS_SIZE EQUAL 64)
         set(release_files ${release_files} libcrypto-1_1-x64.dll)
diff --git a/indra/cmake/ICU4C.cmake b/indra/cmake/ICU4C.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..7b27665483b462ee5bf8c2656a4fd10edcdb9422
--- /dev/null
+++ b/indra/cmake/ICU4C.cmake
@@ -0,0 +1,23 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+include_guard()
+
+add_library( ll::icu4c INTERFACE IMPORTED )
+
+
+use_system_binary(icu4c)
+use_prebuilt_binary(icu4c)
+if (WINDOWS)
+  target_link_libraries( ll::icu4c INTERFACE  icuuc)
+elseif(DARWIN)
+  target_link_libraries( ll::icu4c INTERFACE  icuuc)
+#elseif(LINUX)
+##  target_link_libraries( ll::icu4c INTERFACE  )
+else()
+  message(FATAL_ERROR "Invalid platform")
+endif()
+
+target_include_directories( ll::icu4c SYSTEM INTERFACE  ${LIBS_PREBUILT_DIR}/include/unicode )
+
+use_prebuilt_binary(dictionaries)
diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake
index 00f8b77106c705f4c52e5e019db3ddac1f6137bc..cae68fbc1195a6b92ba0ecf8792d6a62114b6e80 100644
--- a/indra/cmake/ViewerMiscLibs.cmake
+++ b/indra/cmake/ViewerMiscLibs.cmake
@@ -18,3 +18,6 @@ endif()
 
 use_prebuilt_binary(slvoice)
 
+use_prebuilt_binary(nanosvg)
+use_prebuilt_binary(viewer-fonts)
+use_prebuilt_binary(emoji_shortcodes)
diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h
index 793a33cc8717e2acbded8270038dda31d427d981..1fbe19ddd1be0d70c9f170b3fc0f34570690cbdf 100644
--- a/indra/llappearance/llwearabletype.h
+++ b/indra/llappearance/llwearabletype.h
@@ -37,7 +37,7 @@ class LLWearableType : public LLParamSingleton<LLWearableType>
 {
 	LLSINGLETON(LLWearableType, LLTranslationBridge::ptr_t &trans);
 	~LLWearableType();
-	void initSingleton();
+	void initSingleton() override;
 public: 
 	enum EType
 	{
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 5f4ed2fffaaa7b371b7bf31d4acbf3b1ec756bbf..c947184dc82a155134c72176d9c01276fb3ba3ff 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -3,6 +3,7 @@
 project(llcommon)
 
 include(00-Common)
+include(ICU4C)
 include(LLCommon)
 include(bugsplat)
 include(Linking)
@@ -282,6 +283,7 @@ target_link_libraries(
         ll::uriparser
         ll::oslibraries
         ll::tracy
+        ll::icu4c
     )
 
 target_include_directories(llcommon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 3ab97b557f051dd0518528b4c765504a56cb29e4..1d383f174df10c69e0b34481c13208c275cf9d23 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -278,6 +278,7 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
     catch (std::bad_alloc&)
     {
         // Out of memory on stack allocation?
+        LLError::LLUserWarningMsg::showOutOfMemory();
         printActiveCoroutines();
         LL_ERRS("LLCoros") << "Bad memory allocation in LLCoros::launch(" << prefix << ")!" << LL_ENDL;
     }
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 966ce032965d6eb284cdfcfafd1d4dae3bf47949..fd878f20ad20f1af0c35624aaa9c5e9fa38b436c 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -92,7 +92,7 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
     LLSINGLETON(LLCoros);
     ~LLCoros();
 
-    void cleanupSingleton();
+    void cleanupSingleton() override;
 public:
     /// The viewer's use of the term "coroutine" became deeply embedded before
     /// the industry term "fiber" emerged to distinguish userland threads from
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 414515854a241be125a3d33ab5b662b6bb75b40d..3de641fcba6d2fc04d9d8637aa5176bb11f75d09 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -1601,6 +1601,48 @@ namespace LLError
     {
         return out << boost::stacktrace::stacktrace();
     }
+
+    // LLOutOfMemoryWarning
+    std::string LLUserWarningMsg::sLocalizedOutOfMemoryTitle;
+    std::string LLUserWarningMsg::sLocalizedOutOfMemoryWarning;
+    LLUserWarningMsg::Handler LLUserWarningMsg::sHandler;
+
+    void LLUserWarningMsg::show(const std::string& message)
+    {
+        if (sHandler)
+        {
+            sHandler(std::string(), message);
+        }
+    }
+
+    void LLUserWarningMsg::showOutOfMemory()
+    {
+        if (sHandler && !sLocalizedOutOfMemoryTitle.empty())
+        {
+            sHandler(sLocalizedOutOfMemoryTitle, sLocalizedOutOfMemoryWarning);
+        }
+    }
+
+    void LLUserWarningMsg::showMissingFiles()
+    {
+        // Files Are missing, likely can't localize.
+        const std::string error_string =
+            "Second Life viewer couldn't access some of the files it needs and will be closed."
+            "\n\nPlease reinstall viewer from  https://secondlife.com/support/downloads/ and "
+            "contact https://support.secondlife.com if issue persists after reinstall.";
+        sHandler("Missing Files", error_string);
+    }
+
+    void LLUserWarningMsg::setHandler(const LLUserWarningMsg::Handler &handler)
+    {
+        sHandler = handler;
+    }
+
+    void LLUserWarningMsg::setOutOfMemoryStrings(const std::string& title, const std::string& message)
+    {
+        sLocalizedOutOfMemoryTitle = title;
+        sLocalizedOutOfMemoryWarning = message;
+    }
 }
 
 void crashdriver(void (*callback)(int*))
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 05dd88ee514fc5a2097b7888d0744359e3699efd..6f6b349cf54c7b67043167c83fa68e86735a6658 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -39,6 +39,7 @@
 #include "llpreprocessor.h"
 
 #include <boost/static_assert.hpp>
+#include <functional> // std::function
 
 const int LL_ERR_NOERR = 0;
 
@@ -301,6 +302,28 @@ namespace LLError
     {
         friend std::ostream& operator<<(std::ostream& out, const LLStacktrace&);
     };
+
+    // Provides access to OS notification popup on error, since
+    // not everything has access to OS's messages
+    class LLUserWarningMsg
+    {
+    public:
+        typedef std::function<void(const std::string&, const std::string&)> Handler;
+        static void setHandler(const Handler&);
+        static void setOutOfMemoryStrings(const std::string& title, const std::string& message);
+
+        // When viewer encounters bad alloc or can't access files try warning user about reasons
+        static void showOutOfMemory();
+        static void showMissingFiles();
+        // Genering error
+        static void show(const std::string&);
+
+    private:
+        // needs to be preallocated before viewer runs out of memory
+        static std::string sLocalizedOutOfMemoryTitle;
+        static std::string sLocalizedOutOfMemoryWarning;
+        static Handler sHandler;
+    };
 }
 
 //this is cheaper than llcallstacks if no need to output other variables to call stacks. 
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
index 46560b5e4ce39ac7c12dc45b7ae1d080448090d7..0787bde57f101b02253d89cd5889dc82032ef24a 100644
--- a/indra/llcommon/llexception.cpp
+++ b/indra/llcommon/llexception.cpp
@@ -37,6 +37,7 @@
 #include "llerror.h"
 #include "llerrorcontrol.h"
 
+
 // used to attach and extract stacktrace information to/from boost::exception,
 // see https://www.boost.org/doc/libs/release/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.exceptions_with_stacktrace
 // apparently the struct passed as the first template param needs no definition?
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index fba8301c4d23efd99dbeeebc23b95bfcf1c186a5..64027c16c76274a4c8550ab1138b6168d488a553 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -802,7 +802,7 @@ class LLLockedSingleton : public LLParamSingleton<DT>
 private:                                                                \
     /* implement LLSingleton pure virtual method whose sole purpose */  \
     /* is to remind people to use this macro */                         \
-    virtual void you_must_use_LLSINGLETON_macro() {}                    \
+    virtual void you_must_use_LLSINGLETON_macro() override {}                    \
     friend class LLSingleton<DERIVED_CLASS>;                            \
     DERIVED_CLASS(__VA_ARGS__)
 
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index f6629803ee333baab56c23fe4f904f45e2a45f4f..82dc7c9f805c0dfa2876b3db93eba065e71a8483 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -30,6 +30,7 @@
 #include "llerror.h"
 #include "llfasttimer.h"
 #include "llsd.h"
+#include <unicode/uchar.h>
 #include <vector>
 
 #if LL_WINDOWS
@@ -338,8 +339,6 @@ S32 wchar_utf8_length(const llwchar wc)
 {
 	if (wc < 0x80)
 	{
-		// This case will also catch negative values which are
-		// technically invalid.
 		return 1;
 	}
 	else if (wc < 0x800)
@@ -364,6 +363,30 @@ S32 wchar_utf8_length(const llwchar wc)
 	}
 }
 
+std::string wchar_utf8_preview(const llwchar wc)
+{
+    std::ostringstream oss;
+    oss << std::hex << std::uppercase << (U32)wc;
+
+    U8 out_bytes[8];
+    U32 size = (U32)wchar_to_utf8chars(wc, (char*)out_bytes);
+
+    if (size > 1)
+    {
+        oss << " [";
+        for (U32 i = 0; i < size; ++i)
+        {
+            if (i)
+            {
+                oss << ", ";
+            }
+            oss << (int)out_bytes[i];
+        }
+        oss << "]";
+    }
+
+    return oss.str();
+}
 
 S32 wstring_utf8_length(const LLWString& wstr)
 {
@@ -600,6 +623,7 @@ std::string mbcsstring_makeASCII(const std::string& wstr)
 	}
 	return out_str;
 }
+
 std::string utf8str_removeCRLF(const std::string& utf8str)
 {
 	if (0 == utf8str.length())
@@ -621,6 +645,119 @@ std::string utf8str_removeCRLF(const std::string& utf8str)
 	return out;
 }
 
+llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length)
+{
+    switch (length)
+    {
+    case 2:
+        return ((utf8str[offset] & 0x1F) << 6) +
+                (utf8str[offset + 1] & 0x3F);
+    case 3:
+        return ((utf8str[offset] & 0x0F) << 12) +
+                ((utf8str[offset + 1] & 0x3F) << 6) +
+                (utf8str[offset + 2] & 0x3F);
+    case 4:
+        return ((utf8str[offset] & 0x07) << 18) +
+                ((utf8str[offset + 1] & 0x3F) << 12) +
+                ((utf8str[offset + 2] & 0x3F) << 6) +
+                (utf8str[offset + 3] & 0x3F);
+    case 5:
+        return ((utf8str[offset] & 0x03) << 24) +
+                ((utf8str[offset + 1] & 0x3F) << 18) +
+                ((utf8str[offset + 2] & 0x3F) << 12) +
+                ((utf8str[offset + 3] & 0x3F) << 6) +
+                (utf8str[offset + 4] & 0x3F);
+    case 6:
+        return ((utf8str[offset] & 0x01) << 30) +
+                ((utf8str[offset + 1] & 0x3F) << 24) +
+                ((utf8str[offset + 2] & 0x3F) << 18) +
+                ((utf8str[offset + 3] & 0x3F) << 12) +
+                ((utf8str[offset + 4] & 0x3F) << 6) +
+                (utf8str[offset + 5] & 0x3F);
+    case 7:
+        return ((utf8str[offset + 1] & 0x03) << 30) +
+                ((utf8str[offset + 2] & 0x3F) << 24) +
+                ((utf8str[offset + 3] & 0x3F) << 18) +
+                ((utf8str[offset + 4] & 0x3F) << 12) +
+                ((utf8str[offset + 5] & 0x3F) << 6) +
+                (utf8str[offset + 6] & 0x3F);
+    }
+    return LL_UNKNOWN_CHAR;
+}
+
+std::string utf8str_showBytesUTF8(const std::string& utf8str)
+{
+    std::string result;
+
+    bool in_sequence = false;
+    size_t sequence_size = 0;
+    size_t byte_index = 0;
+    size_t source_length = utf8str.size();
+
+    auto open_sequence = [&]()
+        {
+            if (!result.empty() && result.back() != '\n')
+                result += '\n'; // Use LF as a separator before new UTF-8 sequence
+            result += '[';
+            in_sequence = true;
+        };
+
+    auto close_sequence = [&]()
+        {
+            llwchar unicode = utf8str_to_wchar(utf8str, byte_index - sequence_size, sequence_size);
+            if (unicode != LL_UNKNOWN_CHAR)
+            {
+                result += llformat("+%04X", unicode);
+            }
+            result += ']';
+            in_sequence = false;
+            sequence_size = 0;
+        };
+
+    while (byte_index < source_length)
+    {
+        U8 byte = utf8str[byte_index];
+        if (byte >= 0x80) // Part of an UTF-8 sequence
+        {
+            if (!in_sequence) // Start new UTF-8 sequence
+            {
+                open_sequence();
+            }
+            else if (byte >= 0xC0) // Start another UTF-8 sequence
+            {
+                close_sequence();
+                open_sequence();
+            }
+            else // Continue the same UTF-8 sequence
+            {
+                result += '.';
+            }
+            result += llformat("%02X", byte); // The byte is represented in hexadecimal form
+            ++sequence_size;
+        }
+        else // ASCII symbol is represented as a character
+        {
+            if (in_sequence) // End of UTF-8 sequence
+            {
+                close_sequence();
+                if (byte != '\n')
+                {
+                    result += '\n'; // Use LF as a separator between UTF-8 and ASCII
+                }
+            }
+            result += byte;
+        }
+        ++byte_index;
+    }
+
+    if (in_sequence) // End of UTF-8 sequence
+    {
+        close_sequence();
+    }
+
+    return result;
+}
+
 #if LL_WINDOWS
 unsigned int ll_wstring_default_code_page()
 {
@@ -833,6 +970,40 @@ std::string LLStringOps::sDayFormat;
 std::string LLStringOps::sAM;
 std::string LLStringOps::sPM;
 
+// static
+bool LLStringOps::isEmoji(llwchar wch)
+{
+	int ublock = ublock_getCode(wch);
+	switch (ublock)
+	{
+		case UBLOCK_GENERAL_PUNCTUATION:
+		case UBLOCK_LETTERLIKE_SYMBOLS:
+		case UBLOCK_ARROWS:
+		case UBLOCK_MISCELLANEOUS_TECHNICAL:
+		case UBLOCK_ENCLOSED_ALPHANUMERICS:
+		case UBLOCK_GEOMETRIC_SHAPES:
+		case UBLOCK_MISCELLANEOUS_SYMBOLS:
+		case UBLOCK_DINGBATS:
+		case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
+		case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
+		case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS:
+		case UBLOCK_EMOTICONS:
+		case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS:
+#if U_ICU_VERSION_MAJOR_NUM > 56
+		// Boost uses ICU so we can't update it independently
+		case UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS:
+#endif // U_ICU_VERSION_MAJOR_NUM > 56
+			return true;
+		default:
+#if U_ICU_VERSION_MAJOR_NUM > 56
+			return false;
+#else
+			// See https://en.wikipedia.org/wiki/Supplemental_Symbols_and_Pictographs
+			return wch >= 0x1F900 && wch <= 0x1F9FF;
+#endif // U_ICU_VERSION_MAJOR_NUM > 56
+	}
+}
+
 
 S32	LLStringOps::collate(const llwchar* a, const llwchar* b)
 { 
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 1fd6cac14a6020354f92bf6d21b3903c8dfa94d9..bfbf25d9abb23d93a9e3db62aaf1ec545348b4bc 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -189,6 +189,8 @@ class LL_COMMON_API LLStringOps
 	static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
 	static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }
 
+	static bool isEmoji(llwchar wch);
+
 	static S32	collate(const char* a, const char* b) { return strcoll(a, b); }
 	static S32	collate(const llwchar* a, const llwchar* b);
 
@@ -355,6 +357,8 @@ class LLStringUtilBase
 	static void	replaceNonstandardASCII( string_type& string, T replacement );
 	static void	replaceChar( string_type& string, T target, T replacement );
 	static void replaceString( string_type& string, string_type target, string_type replacement );
+	static string_type capitalize(const string_type& str);
+	static void capitalize(string_type& str);
 	
 	static BOOL	containsNonprintable(const string_type& string);
 	static void	stripNonprintable(string_type& string);
@@ -678,6 +682,8 @@ LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
 // Length in bytes of this wide char in a UTF8 string
 LL_COMMON_API S32 wchar_utf8_length(const llwchar wc); 
 
+LL_COMMON_API std::string wchar_utf8_preview(const llwchar wc);
+
 LL_COMMON_API std::string utf8str_tolower(const std::string& utf8str);
 
 // Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string.
@@ -737,6 +743,9 @@ LL_COMMON_API std::string mbcsstring_makeASCII(const std::string& str);
 
 LL_COMMON_API std::string utf8str_removeCRLF(const std::string& utf8str);
 
+LL_COMMON_API llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length);
+
+LL_COMMON_API std::string utf8str_showBytesUTF8(const std::string& utf8str);
 
 #if LL_WINDOWS
 /* @name Windows string helpers
@@ -1593,6 +1602,29 @@ void LLStringUtilBase<T>::replaceTabsWithSpaces( string_type& str, size_type spa
 	str = out_str;
 }
 
+//static
+template<class T>
+std::basic_string<T> LLStringUtilBase<T>::capitalize(const string_type& str)
+{
+	string_type result(str);
+	capitalize(result);
+	return result;
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::capitalize(string_type& str)
+{
+	if (str.size())
+	{
+		auto last = str[0] = toupper(str[0]);
+		for (U32 i = 1; i < str.size(); ++i)
+		{
+			last = (last == ' ' || last == '-' || last == '_') ? str[i] = toupper(str[i]) : str[i];
+		}
+	}
+}
+
 //static
 template<class T> 
 BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp
index 15ffe68e67251ec7001fe29185787433cbf8bd00..6f8aaaa0cbcfe9d91eff50e42c34e9962afd2107 100644
--- a/indra/llcommon/tests/llsingleton_test.cpp
+++ b/indra/llcommon/tests/llsingleton_test.cpp
@@ -47,8 +47,8 @@ public:                                             \
         DEP_INIT  /* dependency in initSingleton */ \
     } sDepFlag;                                     \
                                                     \
-    void initSingleton();                           \
-    void cleanupSingleton();                        \
+    void initSingleton() override;                  \
+    void cleanupSingleton() override;               \
 };                                                  \
                                                     \
 CLS::dep_flag CLS::sDepFlag = DEP_NONE
@@ -300,7 +300,7 @@ namespace tut
     {
         LLSINGLETON_EMPTY_CTOR(CircularPInit);
     public:
-        virtual void initSingleton()
+        virtual void initSingleton() override
         {
             // never mind indirection, just go straight for the circularity
             CircularPInit *pt = getInstance();
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index 294acd7f638175501216323678e8d0cb59a9b929..517076804d2b40140d135b62018f8e0a772358a5 100644
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -320,6 +320,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread)
             LLMemory::logMemoryInfo(TRUE);
 
             //output possible call stacks to log file.
+            LLError::LLUserWarningMsg::showOutOfMemory();
             LLError::LLCallStacks::print();
 
             LL_ERRS() << "Bad memory allocation in HttpService::threadRun()!" << LL_ENDL;
diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp
index 90b7272efa78f17d4314f8b07a763b23f0ba3b23..cdea0da68ddad653d88f9439b7e2d60ae1786a86 100644
--- a/indra/llimage/llimagebmp.cpp
+++ b/indra/llimage/llimagebmp.cpp
@@ -321,6 +321,7 @@ bool LLImageBMP::updateData()
 		mColorPalette = new(std::nothrow) U8[color_palette_size];
 		if (!mColorPalette)
 		{
+            LLError::LLUserWarningMsg::showOutOfMemory();
 			LL_ERRS() << "Out of memory in LLImageBMP::updateData()" << LL_ENDL;
 			return false;
 		}
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index 36317a5ba883c39cd973549c308ba66a658e7f73..ae76c5243f0a4215f9563ab3a614294b0f05fac3 100644
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -437,6 +437,7 @@ bool LLImageDXT::convertToDXR()
 	U8* newdata = (U8*)ll_aligned_malloc_16(total_bytes);
 	if (!newdata)
 	{
+        LLError::LLUserWarningMsg::showOutOfMemory();
 		LL_ERRS() << "Out of memory in LLImageDXT::convertToDXR()" << LL_ENDL;
 		return false;
 	}
diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
index 88bdae9b80f4fe3f1247759bb4a2f98a51a275ac..152a7f309c0417d9b91edf142937cea3a2e7307c 100644
--- a/indra/llimage/llimagetga.cpp
+++ b/indra/llimage/llimagetga.cpp
@@ -266,6 +266,7 @@ bool LLImageTGA::updateData()
 			mColorMap = new(std::nothrow) U8[ color_map_bytes ];  
 			if (!mColorMap)
 			{
+                LLError::LLUserWarningMsg::showOutOfMemory();
 				LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL;
 				return false;
 			}
diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp
index d2c3b419abe67ce1c5628032fa7ad0ecb3060435..4856fe461944dd96cd092d6d70d48bc1de2ebdff 100644
--- a/indra/llinventory/llfoldertype.cpp
+++ b/indra/llinventory/llfoldertype.cpp
@@ -60,7 +60,7 @@ class LLFolderDictionary : public LLSingleton<LLFolderDictionary>,
 {
 	LLSINGLETON(LLFolderDictionary);
 protected:
-	virtual LLFolderType::EType notFound() const
+	virtual LLFolderType::EType notFound() const override
 	{
 		return LLFolderType::FT_NONE;
 	}
diff --git a/indra/llinventory/llinventorysettings.cpp b/indra/llinventory/llinventorysettings.cpp
index 81485b3a975183050ade43ba87b4013af63a58e4..bc604097da79ba67d012bb3c6b365b03e794331e 100644
--- a/indra/llinventory/llinventorysettings.cpp
+++ b/indra/llinventory/llinventorysettings.cpp
@@ -62,7 +62,7 @@ class LLSettingsDictionary : public LLSingleton<LLSettingsDictionary>,
 {
     LLSINGLETON(LLSettingsDictionary);
 
-    void initSingleton();
+    void initSingleton() override;
 };
 
 LLSettingsDictionary::LLSettingsDictionary() 
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
index 1c97133723aeca89b512f5a7c2383d110d37a14e..8be4c64dfca9d48fc75960c579bc0617b71fb375 100644
--- a/indra/llmessage/llexperiencecache.h
+++ b/indra/llmessage/llexperiencecache.h
@@ -106,7 +106,7 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache >
 private:
     virtual ~LLExperienceCache();
 
-    virtual void initSingleton();
+    virtual void initSingleton() override;
 
     typedef boost::function<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLCore::HttpRequest::ptr_t, std::string)> permissionInvoker_fn;
 
diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h
index 25f6977e145afcdfa5248708c34286d7c30564c5..8a64cdbfaaf55f6cde1e0309f4203022924d8eb8 100644
--- a/indra/llmessage/llproxy.h
+++ b/indra/llmessage/llproxy.h
@@ -226,7 +226,7 @@ class LLProxy: public LLSingleton<LLProxy>
 	LLSINGLETON(LLProxy);
 	LOG_CLASS(LLProxy);
 
-    /*virtual*/ void initSingleton();
+    /*virtual*/ void initSingleton() override;
 
 public:
 	// Static check for enabled status for UDP packets. Call from main thread only.
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt
index a95daed6cd60079caebd34db8c032c2139845c09..7f881c8bb3e62de69199ae0f01b7b44b60843d93 100644
--- a/indra/llrender/CMakeLists.txt
+++ b/indra/llrender/CMakeLists.txt
@@ -15,6 +15,7 @@ set(llrender_SOURCE_FILES
     llcubemaparray.cpp
     llfontbitmapcache.cpp
     llfontfreetype.cpp
+    llfontfreetypesvg.cpp
     llfontgl.cpp
     llfontregistry.cpp
     llgl.cpp
@@ -43,6 +44,7 @@ set(llrender_HEADER_FILES
     llcubemaparray.h
     llfontgl.h
     llfontfreetype.h
+    llfontfreetypesvg.h
     llfontbitmapcache.h
     llfontregistry.h
     llgl.h
diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp
index c71e24c83a172e4685710dadb289b442a99c3cd9..42b0045cf3aabf2ddf60462239539c81cc6467e4 100644
--- a/indra/llrender/llfontbitmapcache.cpp
+++ b/indra/llrender/llfontbitmapcache.cpp
@@ -30,14 +30,7 @@
 #include "llfontbitmapcache.h"
 
 LLFontBitmapCache::LLFontBitmapCache()
-:	mNumComponents(0),
-	mBitmapWidth(0),
-	mBitmapHeight(0),
-	mBitmapNum(-1),
-	mMaxCharWidth(0),
-	mMaxCharHeight(0),
-	mCurrentOffsetX(1),
-	mCurrentOffsetY(1)
+
 {
 }
 
@@ -45,121 +38,149 @@ LLFontBitmapCache::~LLFontBitmapCache()
 {
 }
 
-void LLFontBitmapCache::init(S32 num_components,
-							 S32 max_char_width,
+void LLFontBitmapCache::init(S32 max_char_width,
 							 S32 max_char_height)
 {
 	reset();
 	
-	mNumComponents = num_components;
 	mMaxCharWidth = max_char_width;
 	mMaxCharHeight = max_char_height;
+
+	S32 image_width = mMaxCharWidth * 20;
+	S32 pow_iw = 2;
+	while (pow_iw < image_width)
+	{
+		pow_iw <<= 1;
+	}
+	image_width = pow_iw;
+	image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
+
+	mBitmapWidth = image_width;
+	mBitmapHeight = image_width;
 }
 
-LLImageRaw *LLFontBitmapCache::getImageRaw(U32 bitmap_num) const
+LLImageRaw *LLFontBitmapCache::getImageRaw(EFontGlyphType bitmap_type, U32 bitmap_num) const
 {
-	if (bitmap_num >= mImageRawVec.size())
-		return NULL;
+	const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageRawVec[bitmap_idx].size())
+		return nullptr;
 
-	return mImageRawVec[bitmap_num];
+	return mImageRawVec[bitmap_idx][bitmap_num];
 }
 
-LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const
+LLImageGL *LLFontBitmapCache::getImageGL(EFontGlyphType bitmap_type, U32 bitmap_num) const
 {
-	if (bitmap_num >= mImageGLVec.size())
-		return NULL;
+	const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageGLVec[bitmap_idx].size())
+		return nullptr;
 
-	return mImageGLVec[bitmap_num];
+	return mImageGLVec[bitmap_idx][bitmap_num];
 }
 
 
-BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num)
+BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyphType bitmap_type, U32& bitmap_num)
 {
-	if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth)
+	if (bitmap_type >= EFontGlyphType::Count)
+	{
+		return FALSE;
+	}
+
+	const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+	if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)
 	{
-		if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight)
+		if ((mImageRawVec[bitmap_idx].empty()) || (mCurrentOffsetY[bitmap_idx] + 2*mMaxCharHeight + 2) > mBitmapHeight)
 		{
 			// We're out of space in the current image, or no image
 			// has been allocated yet.  Make a new one.
-			
-			mImageRawVec.push_back(new LLImageRaw);
-			mBitmapNum = mImageRawVec.size()-1;
-			LLImageRaw *image_raw = getImageRaw(mBitmapNum);
-
-			// Make corresponding GL image.
-			mImageGLVec.push_back(new LLImageGL(FALSE));
-			LLImageGL *image_gl = getImageGL(mBitmapNum);
-			
-			S32 image_width = mMaxCharWidth * 20;
-			S32 pow_iw = 2;
-			while (pow_iw < image_width)
+            
+            S32 image_width = mMaxCharWidth * 20;
+            S32 pow_iw = 2;
+            while (pow_iw < image_width)
+            {
+                pow_iw *= 2;
+            }
+            image_width = pow_iw;
+            image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
+            S32 image_height = image_width;
+            
+            mBitmapWidth = image_width;
+            mBitmapHeight = image_height;
+            
+			S32 num_components = getNumComponents(bitmap_type);
+			mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components));
+			bitmap_num = mImageRawVec[bitmap_idx].size() - 1;
+
+			LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num);
+			if (EFontGlyphType::Grayscale == bitmap_type)
 			{
-				pow_iw *= 2;
+				image_raw->clear(255, 0);
 			}
-			image_width = pow_iw;
-			image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
-			S32 image_height = image_width;
-
-			image_raw->resize(image_width, image_height, mNumComponents);
-
-			mBitmapWidth = image_width;
-			mBitmapHeight = image_height;
 
-			switch (mNumComponents)
-			{
-				case 1:
-					image_raw->clear();
-				break;
-				case 2:
-					image_raw->clear(255, 0);
-				break;
-			}
+			// Make corresponding GL image.
+			mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false));
+			LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);
 
 			// Start at beginning of the new image.
-			mCurrentOffsetX = 1;
-			mCurrentOffsetY = 1;
+			mCurrentOffsetX[bitmap_idx] = 1;
+			mCurrentOffsetY[bitmap_idx] = 1;
 
-			// Attach corresponding GL texture.
-			image_gl->createGLTexture(0, image_raw);
+			// Attach corresponding GL texture. (*TODO: is this needed?)
 			gGL.getTexUnit(0)->bind(image_gl);
 			image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(TRUE, TRUE);
 		}
 		else
 		{
 			// Move to next row in current image.
-			mCurrentOffsetX = 1;
-			mCurrentOffsetY += mMaxCharHeight + 1;
+			mCurrentOffsetX[bitmap_idx] = 1;
+			mCurrentOffsetY[bitmap_idx] += mMaxCharHeight + 1;
 		}
 	}
 
-	pos_x = mCurrentOffsetX;
-	pos_y = mCurrentOffsetY;
-	bitmap_num = mBitmapNum;
+	pos_x = mCurrentOffsetX[bitmap_idx];
+	pos_y = mCurrentOffsetY[bitmap_idx];
+	bitmap_num = getNumBitmaps(bitmap_type) - 1;
 
-	mCurrentOffsetX += width + 1;
+	mCurrentOffsetX[bitmap_idx] += width + 1;
 
 	return TRUE;
 }
 
 void LLFontBitmapCache::destroyGL()
 {
-	for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin();
-		 it != mImageGLVec.end(); ++it)
+	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
 	{
-		(*it)->destroyGLTexture();
+		for (LLImageGL* image_gl : mImageGLVec[idx])
+		{
+			image_gl->destroyGLTexture();
+		}
 	}
 }
 
 void LLFontBitmapCache::reset()
 {
-	mImageRawVec.clear();
-
-	mImageGLVec.clear();
+	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
+	{
+		mImageRawVec[idx].clear();
+		mImageGLVec[idx].clear();
+		mCurrentOffsetX[idx] = 1;
+		mCurrentOffsetY[idx] = 1;
+	}
 	
 	mBitmapWidth = 0;
 	mBitmapHeight = 0;
-	mBitmapNum = -1;
-	mCurrentOffsetX = 1;
-	mCurrentOffsetY = 1;
 }
 
+//static
+U32 LLFontBitmapCache::getNumComponents(EFontGlyphType bitmap_type)
+{
+	switch (bitmap_type)
+	{
+		case EFontGlyphType::Grayscale:
+			return 2;
+		case EFontGlyphType::Color:
+			return 4;
+		default:
+			llassert(false);
+			return 2;
+	}
+}
diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h
index 7de3a6b56f06ebcd5e2a8e1852042a200da62d13..c63281ab70f7b8cf250959fd775f8f033f6cf432 100644
--- a/indra/llrender/llfontbitmapcache.h
+++ b/indra/llrender/llfontbitmapcache.h
@@ -30,6 +30,14 @@
 #include <vector>
 #include "lltrace.h"
 
+enum class EFontGlyphType : U32
+{
+	Grayscale = 0,
+	Color,
+	Count,
+	Unspecified,
+};
+
 // Maintain a collection of bitmaps containing rendered glyphs.
 // Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL.
 class LLFontBitmapCache
@@ -39,35 +47,35 @@ class LLFontBitmapCache
 	~LLFontBitmapCache();
 
 	// Need to call this once, before caching any glyphs.
- 	void init(S32 num_components,
-			  S32 max_char_width,
+ 	void init(S32 max_char_width,
 			  S32 max_char_height);
 
 	void reset();
 
-	BOOL nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum);
+	BOOL nextOpenPos(S32 width, S32& posX, S32& posY, EFontGlyphType bitmapType, U32& bitmapNum);
 	
 	void destroyGL();
 	
- 	LLImageRaw *getImageRaw(U32 bitmapNum = 0) const;
- 	LLImageGL *getImageGL(U32 bitmapNum = 0) const;
-	
+ 	LLImageRaw* getImageRaw(EFontGlyphType bitmapType, U32 bitmapNum) const;
+ 	LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const;
+
 	S32 getMaxCharWidth() const { return mMaxCharWidth; }
-	S32 getNumComponents() const { return mNumComponents; }
+	U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast<U32>(bitmapType)].size() : 0; }
 	S32 getBitmapWidth() const { return mBitmapWidth; }
 	S32 getBitmapHeight() const { return mBitmapHeight; }
 
+protected:
+	static U32 getNumComponents(EFontGlyphType bitmap_type);
+
 private:
-	S32 mNumComponents;
-	S32 mBitmapWidth;
-	S32 mBitmapHeight;
-	S32 mBitmapNum;
-	S32 mMaxCharWidth;
-	S32 mMaxCharHeight;
-	S32 mCurrentOffsetX;
-	S32 mCurrentOffsetY;
-	std::vector<LLPointer<LLImageRaw> >	mImageRawVec;
-	std::vector<LLPointer<LLImageGL> > mImageGLVec;
+	S32 mBitmapWidth = 0;
+	S32 mBitmapHeight = 0;
+	S32 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
+	S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
+	S32 mMaxCharWidth = 0;
+	S32 mMaxCharHeight = 0;
+	std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)];
+	std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];
 };
 
 #endif //LL_LLFONTBITMAPCACHE_H
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index e964d1586faa1657e8efa609cbc6c8e9c25d7f2d..d87fb5245c199199dab3ef94292ab3a980804c4f 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -34,14 +34,17 @@
 #ifdef LL_WINDOWS
 #include <freetype2\freetype\ftsystem.h>
 #endif
+#include "llfontfreetypesvg.h"
 
 // For some reason, this won't work if it's not wrapped in the ifdef
 #ifdef FT_FREETYPE_H
 #include FT_FREETYPE_H
 #endif
 
+#include "lldir.h"
 #include "llerror.h"
 #include "llimage.h"
+#include "llimagepng.h"
 //#include "llimagej2c.h"
 #include "llmath.h"	// Linden math
 #include "llstring.h"
@@ -49,6 +52,8 @@
 #include "llfontbitmapcache.h"
 #include "llgl.h"
 
+#define ENABLE_OT_SVG_SUPPORT
+
 FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;
 
 LLFontManager *gFontManagerp = NULL;
@@ -81,6 +86,16 @@ LLFontManager::LLFontManager()
 		LL_ERRS() << "Freetype initialization failure!" << LL_ENDL;
 		FT_Done_FreeType(gFTLibrary);
 	}
+
+#ifdef ENABLE_OT_SVG_SUPPORT
+	SVG_RendererHooks hooks = {
+		LLFontFreeTypeSvgRenderer::OnInit,
+		LLFontFreeTypeSvgRenderer::OnFree,
+		LLFontFreeTypeSvgRenderer::OnRender,
+		LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot,
+	};
+	FT_Property_Set(gFTLibrary, "ot-svg", "svg-hooks", &hooks);
+#endif
 }
 
 LLFontManager::~LLFontManager()
@@ -89,8 +104,9 @@ LLFontManager::~LLFontManager()
 }
 
 
-LLFontGlyphInfo::LLFontGlyphInfo(U32 index)
+LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)
 :	mGlyphIndex(index),
+	mGlyphType(glyph_type),
 	mWidth(0),			// In pixels
 	mHeight(0),			// In pixels
 	mXAdvance(0.f),		// In pixels
@@ -99,10 +115,25 @@ LLFontGlyphInfo::LLFontGlyphInfo(U32 index)
 	mYBitmapOffset(0), 	// Offset to the origin in the bitmap
 	mXBearing(0),		// Distance from baseline to left in pixels
 	mYBearing(0),		// Distance from baseline to top in pixels
-	mBitmapNum(0) // Which bitmap in the bitmap cache contains this glyph
+	mBitmapEntry(std::make_pair(EFontGlyphType::Unspecified, -1)) // Which bitmap in the bitmap cache contains this glyph
 {
 }
 
+LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi)
+	: mGlyphIndex(fgi.mGlyphIndex)
+	, mGlyphType(fgi.mGlyphType)
+	, mWidth(fgi.mWidth)
+	, mHeight(fgi.mHeight)
+	, mXAdvance(fgi.mXAdvance)
+	, mYAdvance(fgi.mYAdvance)
+	, mXBitmapOffset(fgi.mXBitmapOffset)
+	, mYBitmapOffset(fgi.mYBitmapOffset)
+	, mXBearing(fgi.mXBearing)
+	, mYBearing(fgi.mYBearing)
+{
+	mBitmapEntry = fgi.mBitmapEntry;
+}
+
 LLFontFreetype::LLFontFreetype()
 :	mFontBitmapCachep(new LLFontBitmapCache),
 	mAscender(0.f),
@@ -156,7 +187,7 @@ void ft_close_cb(FT_Stream stream) {
 }
 #endif
 
-BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n)
+BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n)
 {
 	// Don't leak face objects.  This is also needed to deal with
 	// changed font file names.
@@ -220,7 +251,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v
 	S32 max_char_width = ll_round(0.5f + (x_max - x_min));
 	S32 max_char_height = ll_round(0.5f + (y_max - y_min));
 
-	mFontBitmapCachep->init(components, max_char_width, max_char_height);
+	mFontBitmapCachep->init(max_char_width, max_char_height);
 
 	if (!mFTFace->charmap)
 	{
@@ -231,7 +262,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v
 	if (!mIsFallback)
 	{
 		// Add the default glyph
-		addGlyphFromFont(this, 0, 0);
+		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);
 	}
 
 	mName = filename;
@@ -323,14 +354,11 @@ void LLFontFreetype::clearFontStreams()
 }
 #endif
 
-void LLFontFreetype::setFallbackFonts(const font_vector_t &font)
-{
-	mFallbackFonts = font;
-}
-
-const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const
+void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor)
 {
-	return mFallbackFonts;
+	// Insert functor fallbacks before generic fallbacks
+	mFallbackFonts.insert((functor) ? std::find_if(mFallbackFonts.begin(), mFallbackFonts.end(), [](const fallback_font_t& fe) { return !fe.second; }) : mFallbackFonts.end(),
+	                      std::make_pair(fallback_font, functor));
 }
 
 F32 LLFontFreetype::getLineHeight() const
@@ -354,7 +382,7 @@ F32 LLFontFreetype::getXAdvance(llwchar wch) const
 		return 0.0;
 
 	// Return existing info only if it is current
-	LLFontGlyphInfo* gi = getGlyphInfo(wch);
+	LLFontGlyphInfo* gi = getGlyphInfo(wch, EFontGlyphType::Unspecified);
 	if (gi)
 	{
 		return gi->mXAdvance;
@@ -386,10 +414,10 @@ F32 LLFontFreetype::getXKerning(llwchar char_left, llwchar char_right) const
 		return 0.0;
 
 	//llassert(!mIsFallback);
-	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left);;
+	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left, EFontGlyphType::Unspecified);;
 	U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
 	// Kern this puppy.
-	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right);
+	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right, EFontGlyphType::Unspecified);
 	U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;
 
 	FT_Vector  delta;
@@ -420,60 +448,94 @@ BOOL LLFontFreetype::hasGlyph(llwchar wch) const
 	return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end());
 }
 
-LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const
+LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type) const
 {
 	if (mFTFace == NULL)
 		return FALSE;
 
 	llassert(!mIsFallback);
+	llassert(glyph_type < EFontGlyphType::Count);
 	//LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL;
 
 	FT_UInt glyph_index;
 
+	// Fallback fonts with a functor have precedence over everything else
+	fallback_font_vector_t::const_iterator it_fallback = mFallbackFonts.cbegin();
+	/* This leads to a bug SL-19831 "Check marks in the menu are less visible."
+	** Also, LLFontRegistry::createFont() says: "Fallback fonts don't render"
+	for (; it_fallback != mFallbackFonts.cend() && it_fallback->second; ++it_fallback)
+	{
+		if (it_fallback->second(wch))
+		{
+			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch);
+			if (glyph_index)
+			{
+				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type);
+			}
+		}
+	}
+	*/
+
 	// Initialize char to glyph map
 	glyph_index = FT_Get_Char_Index(mFTFace, wch);
 	if (glyph_index == 0)
 	{
 		//LL_INFOS() << "Trying to add glyph from fallback font!" << LL_ENDL;
-		font_vector_t::const_iterator iter;
-		for(iter = mFallbackFonts.begin(); iter != mFallbackFonts.end(); iter++)
+		for (; it_fallback != mFallbackFonts.cend(); ++it_fallback)
 		{
-			glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch);
+			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch);
 			if (glyph_index)
 			{
-				return addGlyphFromFont(*iter, wch, glyph_index);
+				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type);
 			}
 		}
 	}
 	
-	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
-	if (iter == mCharGlyphInfoMap.end())
+	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
+	char_glyph_info_map_t::iterator iter = 
+		std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; });
+	if (iter == range_it.second)
 	{
-		return addGlyphFromFont(this, wch, glyph_index);
+		return addGlyphFromFont(this, wch, glyph_index, glyph_type);
 	}
 	return NULL;
 }
 
-LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const
+LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType requested_glyph_type) const
 {
     LL_PROFILE_ZONE_SCOPED;
 	if (mFTFace == NULL)
 		return NULL;
 
 	llassert(!mIsFallback);
-	fontp->renderGlyph(glyph_index);
+	fontp->renderGlyph(requested_glyph_type, glyph_index);
+
+	EFontGlyphType bitmap_glyph_type = EFontGlyphType::Unspecified;
+	switch (fontp->mFTFace->glyph->bitmap.pixel_mode)
+	{
+		case FT_PIXEL_MODE_MONO:
+		case FT_PIXEL_MODE_GRAY:
+			bitmap_glyph_type = EFontGlyphType::Grayscale;
+			break;
+		case FT_PIXEL_MODE_BGRA:
+			bitmap_glyph_type = EFontGlyphType::Color;
+			break;
+		default:
+			llassert_always(true);
+			break;
+	}
 	S32 width = fontp->mFTFace->glyph->bitmap.width;
 	S32 height = fontp->mFTFace->glyph->bitmap.rows;
 
 	S32 pos_x, pos_y;
-	S32 bitmap_num;
-	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_num);
+	U32 bitmap_num;
+	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
 	mAddGlyphCount++;
 
-	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index);
+	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
 	gi->mXBitmapOffset = pos_x;
 	gi->mYBitmapOffset = pos_y;
-	gi->mBitmapNum = bitmap_num;
+	gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, bitmap_num);
 	gi->mWidth = width;
 	gi->mHeight = height;
 	gi->mXBearing = fontp->mFTFace->glyph->bitmap_left;
@@ -484,8 +546,12 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l
 
 	insertGlyphInfo(wch, gi);
 
-	llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO
-	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
+	if (requested_glyph_type != bitmap_glyph_type)
+	{
+		LLFontGlyphInfo* gi_temp = new LLFontGlyphInfo(*gi);
+		gi_temp->mGlyphType = bitmap_glyph_type;
+		insertGlyphInfo(wch, gi_temp);
+	}
 
 	if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO
 	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
@@ -520,78 +586,95 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l
 			buffer_row_stride = width;
 		}
 
-		switch (mFontBitmapCachep->getNumComponents())
-		{
-		case 1:
-			mFontBitmapCachep->getImageRaw(bitmap_num)->setSubImage(pos_x,
-																	pos_y,
-																	width,
-																	height,
-																	buffer_data,
-																	buffer_row_stride,
-																	TRUE);
-			break;
-		case 2:
-			setSubImageLuminanceAlpha(pos_x,	
-									  pos_y,
-									  bitmap_num,
-									  width,
-									  height,
-									  buffer_data,
-									  buffer_row_stride);
-			break;
-		default:
-			break;
-		}
+		setSubImageLuminanceAlpha(pos_x,
+									pos_y,
+									bitmap_num,
+									width,
+									height,
+									buffer_data,
+									buffer_row_stride);
 
 		if (tmp_graydata)
 			delete[] tmp_graydata;
+	}
+	else if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
+	{
+		setSubImageBGRA(pos_x,
+		                pos_y,
+		                bitmap_num,
+		                fontp->mFTFace->glyph->bitmap.width,
+		                fontp->mFTFace->glyph->bitmap.rows,
+		                fontp->mFTFace->glyph->bitmap.buffer,
+		                llabs(fontp->mFTFace->glyph->bitmap.pitch));
 	} else {
-		// we don't know how to handle this pixel format from FreeType;
-		// omit it from the font-image.
+		llassert(false);
 	}
 	
-	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_num);
-	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num);
+	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num);
+	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num);
 	image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight());
 
 	return gi;
 }
 
-LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch) const
+LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const
 {
-	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
-	if (iter != mCharGlyphInfoMap.end())
+	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
+
+	char_glyph_info_map_t::iterator iter = (EFontGlyphType::Unspecified != glyph_type)
+		? std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; })
+		: range_it.first;
+	if (iter != range_it.second)
 	{
 		return iter->second;
 	}
 	else
 	{
 		// this glyph doesn't yet exist, so render it and return the result
-		return addGlyph(wch);
+		return addGlyph(wch, (EFontGlyphType::Unspecified != glyph_type) ? glyph_type : EFontGlyphType::Grayscale);
 	}
 }
 
 void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const
 {
-	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
-	if (iter != mCharGlyphInfoMap.end())
+	llassert(gi->mGlyphType < EFontGlyphType::Count);
+	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
+
+	char_glyph_info_map_t::iterator iter =
+		std::find_if(range_it.first, range_it.second, [&gi](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == gi->mGlyphType; });
+	if (iter != range_it.second)
 	{
 		delete iter->second;
 		iter->second = gi;
 	}
 	else
 	{
-		mCharGlyphInfoMap[wch] = gi;
+		mCharGlyphInfoMap.insert(std::make_pair(wch, gi));
 	}
 }
 
-void LLFontFreetype::renderGlyph(U32 glyph_index) const
+void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const
 {
 	if (mFTFace == NULL)
 		return;
 
-	llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_FORCE_AUTOHINT) );
+	FT_Int32 load_flags = FT_LOAD_FORCE_AUTOHINT;
+	if (EFontGlyphType::Color == bitmap_type)
+	{
+		// We may not actually get a color render so our caller should always examine mFTFace->glyph->bitmap.pixel_mode
+		load_flags |= FT_LOAD_COLOR;
+	}
+
+	FT_Error error = FT_Load_Glyph(mFTFace, glyph_index, load_flags);
+	if (FT_Err_Ok != error)
+	{
+		std::string message = llformat(
+			"Error %d (%s) loading glyph %u: bitmap_type=%u, load_flags=%d",
+			error, FT_Error_String(error), glyph_index, bitmap_type, load_flags);
+		LL_WARNS_ONCE() << message << LL_ENDL;
+		error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR);
+		llassert_always_msg(FT_Err_Ok == error, message.c_str());
+	}
 
 	llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) );
 
@@ -601,7 +684,7 @@ void LLFontFreetype::renderGlyph(U32 glyph_index) const
 void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)
 {
 	resetBitmapCache(); 
-	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mFontBitmapCachep->getNumComponents(), mIsFallback);
+	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mIsFallback, 0);
 	if (!mIsFallback)
 	{
 		// This is the head of the list - need to rebuild ourself and all fallbacks.
@@ -611,11 +694,9 @@ void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)
 		}
 		else
 		{
-			for(font_vector_t::iterator it = mFallbackFonts.begin();
-				it != mFallbackFonts.end();
-				++it)
+			for (fallback_font_vector_t::iterator it = mFallbackFonts.begin(); it != mFallbackFonts.end(); ++it)
 			{
-				(*it)->reset(vert_dpi, horz_dpi);
+				it->first->reset(vert_dpi, horz_dpi);
 			}
 		}
 	}
@@ -637,7 +718,7 @@ void LLFontFreetype::resetBitmapCache()
 	if(!mIsFallback)
 	{
 		// Add the empty glyph
-		addGlyphFromFont(this, 0, 0);
+		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);
 	}
 }
 
@@ -651,6 +732,34 @@ const std::string &LLFontFreetype::getName() const
 	return mName;
 }
 
+static void dumpFontBitmap(const LLImageRaw* image_raw, const std::string& file_name)
+{
+	LLPointer<LLImagePNG> tmpImage = new LLImagePNG();
+	if ( (tmpImage->encode(image_raw, 0.0f)) && (tmpImage->save(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name))) )
+	{
+		LL_INFOS("Font") << "Successfully saved " << file_name << LL_ENDL;
+	}
+	else
+	{
+		LL_WARNS("Font") << "Failed to save " << file_name << LL_ENDL;
+	}
+}
+
+void LLFontFreetype::dumpFontBitmaps() const
+{
+	// Dump all the regular bitmaps (if any)
+	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Grayscale); idx < cnt; idx++)
+	{
+		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, idx), llformat("%s_%d_%d_%d.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx));
+	}
+
+	// Dump all the color bitmaps (if any)
+	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Color); idx < cnt; idx++)
+	{
+		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, idx), llformat("%s_%d_%d_%d_clr.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx));
+	}
+}
+
 const LLFontBitmapCache* LLFontFreetype::getFontBitmapCache() const
 {
 	return mFontBitmapCachep;
@@ -666,17 +775,46 @@ U8 LLFontFreetype::getStyle() const
 	return mStyle;
 }
 
+bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const
+{
+	LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num);
+	llassert(!mIsFallback);
+	llassert(image_raw && (image_raw->getComponents() == 4));
+
+	// NOTE: inspired by LLImageRaw::setSubImage()
+	U32* image_data = (U32*)image_raw->getData();
+	if (!image_data)
+	{
+		return false;
+	}
+
+	for (U32 idxRow = 0; idxRow < height; idxRow++)
+	{
+		const U32 nSrcRow = height - 1 - idxRow;
+		const U32 nSrcOffset = nSrcRow * width * image_raw->getComponents();
+		const U32 nDstOffset = (y + idxRow) * image_raw->getWidth() + x;
+
+		for (U32 idxCol = 0; idxCol < width; idxCol++)
+		{
+			U32 nTemp = nSrcOffset + idxCol * 4;
+			image_data[nDstOffset + idxCol] = data[nTemp + 3] << 24 | data[nTemp] << 16 | data[nTemp + 1] << 8 | data[nTemp + 2];
+		}
+	}
+
+	return true;
+}
+
 void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const
 {
-	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num);
+	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num);
 
 	llassert(!mIsFallback);
 	llassert(image_raw && (image_raw->getComponents() == 2));
-
 	
 	U8 *target = image_raw->getData();
+    llassert(target);
 
-	if (!data)
+	if (!data || !target)
 	{
 		return;
 	}
diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h
index f61f169987b2a980ed99f2d39c2e7ab63aaa9a4c..b036d337bad59c859753fb888728a2c51e785943 100644
--- a/indra/llrender/llfontfreetype.h
+++ b/indra/llrender/llfontfreetype.h
@@ -56,9 +56,11 @@ class LLFontManager
 
 struct LLFontGlyphInfo
 {
-	LLFontGlyphInfo(U32 index);
+	LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type);
+	LLFontGlyphInfo(const LLFontGlyphInfo& fgi);
 
 	U32 mGlyphIndex;
+	EFontGlyphType mGlyphType;
 
 	// Metrics
 	S32 mWidth;			// In pixels
@@ -71,7 +73,7 @@ struct LLFontGlyphInfo
 	S32 mYBitmapOffset; // Offset to the origin in the bitmap
 	S32 mXBearing;	// Distance from baseline to left in pixels
 	S32 mYBearing;	// Distance from baseline to top in pixels
-	S32 mBitmapNum; // Which bitmap in the bitmap cache contains this glyph
+	std::pair<EFontGlyphType, S32> mBitmapEntry; // Which bitmap in the bitmap cache contains this glyph
 };
 
 extern LLFontManager *gFontManagerp;
@@ -84,7 +86,7 @@ class LLFontFreetype : public LLRefCount
 
 	// is_fallback should be true for fallback fonts that aren't used
 	// to render directly (Unicode backup, primarily)
-	BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n = 0);
+	BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n);
 
 	S32 getNumFaces(const std::string& filename);
 
@@ -93,10 +95,8 @@ class LLFontFreetype : public LLRefCount
 	void clearFontStreams();
 #endif
 
-	typedef std::vector<LLPointer<LLFontFreetype> > font_vector_t;
-
-	void setFallbackFonts(const font_vector_t &font);
-	const font_vector_t &getFallbackFonts() const;
+	typedef std::function<bool(llwchar)> char_functor_t;
+	void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);
 
 	// Global font metrics - in units of pixels
 	F32 getLineHeight() const;
@@ -135,7 +135,7 @@ class LLFontFreetype : public LLRefCount
 	F32 getXKerning(llwchar char_left, llwchar char_right) const; // Get the kerning between the two characters
 	F32 getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const; // Get the kerning between the two characters
 
-	LLFontGlyphInfo* getGlyphInfo(llwchar wch) const;
+	LLFontGlyphInfo* getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const;
 
 	void reset(F32 vert_dpi, F32 horz_dpi);
 
@@ -143,6 +143,7 @@ class LLFontFreetype : public LLRefCount
 
 	const std::string& getName() const;
 
+	void       dumpFontBitmaps() const;
 	const LLFontBitmapCache* getFontBitmapCache() const;
 
 	void setStyle(U8 style);
@@ -151,10 +152,11 @@ class LLFontFreetype : public LLRefCount
 private:
 	void resetBitmapCache();
 	void setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride = 0) const;
+	bool setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;
 	BOOL hasGlyph(llwchar wch) const;		// Has a glyph for this character
-	LLFontGlyphInfo* addGlyph(llwchar wch) const;		// Add a new character to the font if necessary
-	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found)
-	void renderGlyph(U32 glyph_index) const;
+	LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const;		// Add a new character to the font if necessary
+	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType bitmap_type) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found)
+	void renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const;
 	void insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const;
 
 	std::string mName;
@@ -174,9 +176,12 @@ class LLFontFreetype : public LLRefCount
 #endif
 
 	BOOL mIsFallback;
-	font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars)
+	typedef std::pair<LLPointer<LLFontFreetype>, char_functor_t> fallback_font_t;
+	typedef std::vector<fallback_font_t> fallback_font_vector_t;
+	fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars)
 
-	typedef boost::unordered_map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
+	// *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique)
+	typedef boost::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
 	mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap
 
 	mutable LLFontBitmapCache* mFontBitmapCachep;
diff --git a/indra/llrender/llfontfreetypesvg.cpp b/indra/llrender/llfontfreetypesvg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..19d327a4c9c216ff70ef1afee804836aa6dac705
--- /dev/null
+++ b/indra/llrender/llfontfreetypesvg.cpp
@@ -0,0 +1,205 @@
+/**
+ * @file llfontfreetypesvg.cpp
+ * @brief Freetype font library SVG glyph rendering
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llfontfreetypesvg.h"
+
+#if LL_WINDOWS
+#pragma warning (push)
+#pragma warning (disable : 4702)
+#endif
+
+#define NANOSVG_IMPLEMENTATION
+#include <nanosvg/nanosvg.h>
+#define NANOSVGRAST_IMPLEMENTATION
+#include <nanosvg/nanosvgrast.h>
+
+#if LL_WINDOWS
+#pragma warning (pop)
+#endif
+
+struct LLSvgRenderData
+{
+	FT_UInt    GlyphIndex = 0;
+	FT_Error   Error = FT_Err_Ok; // FreeType currently (@2.12.1) ignores the error value returned by the preset glyph slot callback so we return it at render time
+	// (See https://github.com/freetype/freetype/blob/5faa1df8b93ebecf0f8fd5fe8fda7b9082eddced/src/base/ftobjs.c#L1170)
+	NSVGimage* pNSvgImage = nullptr;
+	float      Scale = 0.f;
+};
+
+// static
+FT_Error LLFontFreeTypeSvgRenderer::OnInit(FT_Pointer* state)
+{
+	// The SVG driver hook state is shared across all callback invocations; since our state is lightweight
+	// we store it in the glyph instead.
+	*state = nullptr;
+
+	return FT_Err_Ok;
+}
+
+// static
+void LLFontFreeTypeSvgRenderer::OnFree(FT_Pointer* state)
+{
+}
+
+// static
+void LLFontFreeTypeSvgRenderer::OnDataFinalizer(void* objectp)
+{
+	FT_GlyphSlot glyph_slot = static_cast<FT_GlyphSlot>(objectp);
+
+	LLSvgRenderData* pData = static_cast<LLSvgRenderData*>(glyph_slot->generic.data);
+	glyph_slot->generic.data = nullptr;
+	glyph_slot->generic.finalizer = nullptr;
+	delete(pData);
+}
+
+//static
+FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer*)
+{
+	FT_SVG_Document document = static_cast<FT_SVG_Document>(glyph_slot->other);
+
+	llassert(!glyph_slot->generic.data || !cache || glyph_slot->glyph_index == ((LLSvgRenderData*)glyph_slot->generic.data)->GlyphIndex);
+	if (!glyph_slot->generic.data)
+	{
+		glyph_slot->generic.data = new LLSvgRenderData();
+		glyph_slot->generic.finalizer = LLFontFreeTypeSvgRenderer::OnDataFinalizer;
+	}
+	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data);
+	if (!cache)
+	{
+		datap->GlyphIndex = glyph_slot->glyph_index;
+		datap->Error = FT_Err_Ok;
+	}
+
+	// NOTE: nsvgParse modifies the input string so we need a temporary copy
+	llassert(!datap->pNSvgImage || cache);
+	if (!datap->pNSvgImage)
+	{
+		char* document_buffer = new char[document->svg_document_length + 1];
+		memcpy(document_buffer, document->svg_document, document->svg_document_length);
+		document_buffer[document->svg_document_length] = '\0';
+
+		datap->pNSvgImage = nsvgParse(document_buffer, "px", 0.);
+
+		delete[] document_buffer;
+	}
+
+	if (!datap->pNSvgImage)
+	{
+		datap->Error = FT_Err_Invalid_SVG_Document;
+		return FT_Err_Invalid_SVG_Document;
+	}
+
+	// We don't (currently) support transformations so test for an identity rotation matrix + zero translation
+	if (document->transform.xx != 1 << 16 || document->transform.yx != 0 ||
+		document->transform.xy != 0 || document->transform.yy != 1 << 16 ||
+		document->delta.x > 0 || document->delta.y > 0)
+	{
+		datap->Error = FT_Err_Unimplemented_Feature;
+		return FT_Err_Unimplemented_Feature;
+	}
+
+	float svg_width = datap->pNSvgImage->width;
+	float svg_height = datap->pNSvgImage->height;
+	if (svg_width == 0.f || svg_height == 0.f)
+	{
+		svg_width = document->units_per_EM;
+		svg_height = document->units_per_EM;
+	}
+
+	float svg_x_scale = (float)document->metrics.x_ppem / floorf(svg_width);
+	float svg_y_scale = (float)document->metrics.y_ppem / floorf(svg_height);
+	float svg_scale = llmin(svg_x_scale, svg_y_scale);
+	datap->Scale = svg_scale;
+
+	glyph_slot->bitmap.width = floorf(svg_width) * svg_scale;
+	glyph_slot->bitmap.rows = floorf(svg_height) * svg_scale;
+	glyph_slot->bitmap_left = (document->metrics.x_ppem - glyph_slot->bitmap.width) / 2;
+	glyph_slot->bitmap_top = glyph_slot->face->size->metrics.ascender / 64.f;
+	glyph_slot->bitmap.pitch = glyph_slot->bitmap.width * 4;
+	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+
+	/* Copied as-is from fcft (MIT license) */
+
+	// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.
+	float horiBearingX = 0.;
+	float horiBearingY = -glyph_slot->bitmap_top;
+
+	// XXX parentheses correct?
+	float vertBearingX = glyph_slot->metrics.horiBearingX / 64.0f - glyph_slot->metrics.horiAdvance / 64.0f / 2;
+	float vertBearingY = (glyph_slot->metrics.vertAdvance / 64.0f - glyph_slot->metrics.height / 64.0f) / 2;
+
+	// Do conversion in two steps to avoid 'bad function cast' warning
+	glyph_slot->metrics.width = glyph_slot->bitmap.width * 64;
+	glyph_slot->metrics.height = glyph_slot->bitmap.rows * 64;
+	glyph_slot->metrics.horiBearingX = horiBearingX * 64;
+	glyph_slot->metrics.horiBearingY = horiBearingY * 64;
+	glyph_slot->metrics.vertBearingX = vertBearingX * 64;
+	glyph_slot->metrics.vertBearingY = vertBearingY * 64;
+	if (glyph_slot->metrics.vertAdvance == 0)
+	{
+		glyph_slot->metrics.vertAdvance = glyph_slot->bitmap.rows * 1.2f * 64;
+	}
+
+	return FT_Err_Ok;
+}
+
+// static
+FT_Error LLFontFreeTypeSvgRenderer::OnRender(FT_GlyphSlot glyph_slot, FT_Pointer*)
+{
+	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data);
+	llassert(FT_Err_Ok == datap->Error);
+	if (FT_Err_Ok != datap->Error)
+	{
+		return datap->Error;
+	}
+
+	// Render to glyph bitmap
+	NSVGrasterizer* nsvgRasterizer = nsvgCreateRasterizer();
+	nsvgRasterize(nsvgRasterizer, datap->pNSvgImage, 0, 0, datap->Scale, glyph_slot->bitmap.buffer, glyph_slot->bitmap.width, glyph_slot->bitmap.rows, glyph_slot->bitmap.pitch);
+	nsvgDeleteRasterizer(nsvgRasterizer);
+	nsvgDelete(datap->pNSvgImage);
+	datap->pNSvgImage = nullptr;
+
+	// Convert from RGBA to BGRA
+	U32* pixel_buffer = (U32*)glyph_slot->bitmap.buffer; U8* byte_buffer = glyph_slot->bitmap.buffer;
+	for (size_t y = 0, h = glyph_slot->bitmap.rows; y < h; y++)
+	{
+		for (size_t x = 0, w = glyph_slot->bitmap.pitch / 4; x < w; x++)
+		{
+			size_t pixel_idx = y * w + x;
+			size_t byte_idx = pixel_idx * 4;
+			U8 alpha = byte_buffer[byte_idx + 3];
+			// Store as ARGB (*TODO - do we still have to care about endianness?)
+			pixel_buffer[y * w + x] = alpha << 24 | (byte_buffer[byte_idx] * alpha / 0xFF) << 16 | (byte_buffer[byte_idx + 1] * alpha / 0xFF) << 8 | (byte_buffer[byte_idx + 2] * alpha / 0xFF);
+		}
+	}
+
+	glyph_slot->format = FT_GLYPH_FORMAT_BITMAP;
+	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+	return FT_Err_Ok;
+}
diff --git a/indra/llrender/llfontfreetypesvg.h b/indra/llrender/llfontfreetypesvg.h
new file mode 100644
index 0000000000000000000000000000000000000000..b5f541991a0c363219ebe10e8c5a46af20490d83
--- /dev/null
+++ b/indra/llrender/llfontfreetypesvg.h
@@ -0,0 +1,54 @@
+/**
+ * @file llfontfreetypesvg.h
+ * @brief Freetype font library SVG glyph rendering
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include <ft2build.h>
+#include FT_TYPES_H
+#include FT_MODULE_H
+#include FT_OTSVG_H
+
+ // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html
+class LLFontFreeTypeSvgRenderer
+{
+public:
+	// Called when the very first OT-SVG glyph is rendered (across the entire lifetime of our FT_Library object)
+	static FT_Error OnInit(FT_Pointer* state);
+
+	// Called when the ot-svg module is being freed (but only called if the init hook was called previously)
+	static void     OnFree(FT_Pointer* state);
+
+	// Called to preset the glyph slot, twice per glyph:
+	//   - when FT_Load_Glyph needs to preset the glyph slot (with cache == false)
+	//   - right before the svg module calls the render callback hook. (with cache == true)
+	static FT_Error OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer* state);
+
+	// Called to render an OT-SVG glyph (right after the preset hook OnPresetGlypthSlot was called with cache set to TRUE)
+	static FT_Error OnRender(FT_GlyphSlot glyph_slot, FT_Pointer* state);
+
+	// Called to deallocate our per glyph slot data
+	static void OnDataFinalizer(void* objectp);
+};
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index 15fddbc99f298de701f00676f3478312489b95ce..53661b6a633801df9e9cdb6f210ede3655a33e22 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -89,14 +89,14 @@ void LLFontGL::destroyGL()
 	mFontFreetype->destroyGL();
 }
 
-BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n)
+BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n)
 {
 	if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))
 	{
 		mFontFreetype = new LLFontFreetype;
 	}
 
-	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback, face_n);
+	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, is_fallback, face_n);
 }
 
 S32 LLFontGL::getNumFaces(const std::string& filename)
@@ -110,14 +110,14 @@ S32 LLFontGL::getNumFaces(const std::string& filename)
 }
 
 S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,
-    ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const
+    ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses, BOOL use_color) const
 {
     LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom);
-    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses);
+    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color);
 }
 
 S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, 
-					 ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const
+					 ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses, BOOL use_color) const
 {
 	F32 x = rect.mLeft;
 	F32 y = 0.f;
@@ -138,12 +138,12 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rec
 		y = rect.mBottom;
 		break;
 	}
-	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses);
+	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses, use_color);
 }
 
 
 S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, 
-					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const
+					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
 
@@ -193,7 +193,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 
 	if (-1 == max_chars)
 	{
-		length = (S32)wstr.length() - begin_offset;
+		max_chars = length = (S32)wstr.length() - begin_offset;
 	}
 	else
 	{
@@ -254,7 +254,6 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 
 	const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL;
 
-
 	BOOL draw_ellipses = FALSE;
 	if (use_ellipses)
 	{
@@ -278,7 +277,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 
 	LLColor4U text_color(color);
 
-	S32 bitmap_num = -1;
+	std::pair<EFontGlyphType, S32> bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1);
 	S32 glyph_count = 0;
 	for (i = begin_offset; i < begin_offset + length; i++)
 	{
@@ -288,7 +287,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 		next_glyph = NULL;
 		if(!fgi)
 		{
-			fgi = mFontFreetype->getGlyphInfo(wch);
+			fgi = mFontFreetype->getGlyphInfo(wch, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);
 		}
 		if (!fgi)
 		{
@@ -296,8 +295,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 			break;
 		}
 		// Per-glyph bitmap texture.
-		S32 next_bitmap_num = fgi->mBitmapNum;
-		if (next_bitmap_num != bitmap_num)
+		std::pair<EFontGlyphType, S32> next_bitmap_entry = fgi->mBitmapEntry;
+		if (next_bitmap_entry != bitmap_entry)
 		{
 			// Actually draw the queued glyphs before switching their texture;
 			// otherwise the queued glyphs will be taken from wrong textures.
@@ -311,8 +310,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 				glyph_count = 0;
 			}
 
-			bitmap_num = next_bitmap_num;
-			LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num);
+			bitmap_entry = next_bitmap_entry;
+			LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);
 			gGL.getTexUnit(0)->bind(font_image);
 		}
 	
@@ -345,7 +344,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 			glyph_count = 0;
 		}
 
-		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength);
+		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, (bitmap_entry.first == EFontGlyphType::Grayscale) ? text_color : LLColor4U::white, style_to_add, shadow, drop_shadow_strength);
 
 		chars_drawn++;
 		cur_x += fgi->mXAdvance;
@@ -355,7 +354,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 		if (next_char && (next_char < LAST_CHARACTER))
 		{
 			// Kern this puppy.
-			next_glyph = mFontFreetype->getGlyphInfo(next_char);
+			next_glyph = mFontFreetype->getGlyphInfo(next_char, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);
 			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
 		}
 
@@ -409,7 +408,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 				shadow,
 				S32_MAX, max_pixels,
 				right_x,
-				FALSE); 
+				FALSE,
+				use_color); 
 		gGL.popUIMatrix();
 	}
 
@@ -420,22 +420,22 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
 
 S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const
 {
-	return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+	return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);
 }
 
-S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses) const
+S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const
 {
-	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses);
+	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);
 }
 
 S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const
 {
-	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);
 }
 
 S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const
 {
-	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE);
+	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow);
 }
 
 // font metrics - override for LLFontFreetype that returns units of virtual pixels
@@ -488,7 +488,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars) const
 	return getWidthF32(wchars, 0, S32_MAX);
 }
 
-F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars ) const
+F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars) const
 {
 	LLWString wtext = utf8str_to_wstring(utf8text);
 	return getWidthF32(wtext.c_str(), begin_offset, max_chars);
@@ -512,7 +512,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars
 		next_glyph = NULL;
 		if(!fgi)
 		{
-			fgi = mFontFreetype->getGlyphInfo(wch);
+			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
 		}
 
 		F32 advance = mFontFreetype->getXAdvance(fgi);
@@ -535,7 +535,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars
 			&& (next_char < LAST_CHARACTER))
 		{
 			// Kern this puppy.
-			next_glyph = mFontFreetype->getGlyphInfo(next_char);
+			next_glyph = mFontFreetype->getGlyphInfo(next_char, EFontGlyphType::Unspecified);
 			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
 		}
 		// Round after kerning.
@@ -556,7 +556,7 @@ void LLFontGL::generateASCIIglyphs()
     LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
     for (U32 i = 32; (i < 127); i++)
     {
-        mFontFreetype->getGlyphInfo(i);
+        mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale);
     }
 }
 
@@ -630,7 +630,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch
 		next_glyph = NULL;
 		if(!fgi)
 		{
-			fgi = mFontFreetype->getGlyphInfo(wch);
+			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
 
 			if (NULL == fgi)
 			{
@@ -655,7 +655,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch
 		if (((i+1) < max_chars) && wchars[i+1])
 		{
 			// Kern this puppy.
-			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]);
+			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1], EFontGlyphType::Unspecified);
 			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
 		}
 
@@ -702,7 +702,7 @@ S32	LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_
 	{
 		llwchar wch = wchars[i];
 
-		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch);
+		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
 
 		// last character uses character width, since the whole character needs to be visible
 		// other characters just use advance
@@ -777,7 +777,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t
 		next_glyph = NULL;
 		if(!glyph)
 		{
-			glyph = mFontFreetype->getGlyphInfo(wch);
+			glyph = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
 		}
 		
 		F32 char_width = mFontFreetype->getXAdvance(glyph);
@@ -807,7 +807,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t
 			&& (wchars[(pos + 1)]))
 		{
 			// Kern this puppy.
-			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]);
+			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1], EFontGlyphType::Unspecified);
 			cur_x += mFontFreetype->getXKerning(glyph, next_glyph);
 		}
 
@@ -847,6 +847,26 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st
 	LLFontGL::loadDefaultFonts();
 }
 
+void LLFontGL::dumpTextures()
+{
+	if (mFontFreetype.notNull())
+	{
+		mFontFreetype->dumpFontBitmaps();
+	}
+}
+
+// static
+void LLFontGL::dumpFonts()
+{
+	sFontRegistry->dump();
+}
+
+// static
+void LLFontGL::dumpFontTextures()
+{
+	sFontRegistry->dumpTextures();
+}
+
 // Force standard fonts to get generated up front.
 // This is primarily for error detection purposes.
 // Don't do this during initClass because it can be slow and we want to get
@@ -1009,6 +1029,20 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name)
 	return gl_vfont_align;
 }
 
+//static
+LLFontGL* LLFontGL::getFontEmoji()
+{
+	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0));
+	return fontp;;
+}
+
+//static
+LLFontGL* LLFontGL::getFontEmojiHuge()
+{
+	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Huge", 0));
+	return fontp;;
+}
+
 //static
 LLFontGL* LLFontGL::getFontMonospace()
 {
diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h
index 9bbf9ce0c4770abf6f6f8731b5fe3e63805fdd70..f6ec416c8b23d8b770767268659847164249671c 100644
--- a/indra/llrender/llfontgl.h
+++ b/indra/llrender/llfontgl.h
@@ -87,7 +87,7 @@ class LLFontGL
 
 	void destroyGL();
 
-	BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback, S32 face_n = 0);
+	BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);
 
 	S32 getNumFaces(const std::string& filename);
 
@@ -98,7 +98,8 @@ class LLFontGL
 				U8 style = NORMAL, ShadowType shadow = NO_SHADOW, 
 				S32 max_chars = S32_MAX,
 				F32* right_x=NULL, 
-				BOOL use_ellipses = FALSE) const;
+				BOOL use_ellipses = FALSE,
+				BOOL use_color = TRUE) const;
 
 	S32 render(const LLWString &text, S32 begin_offset, 
 				const LLRectf& rect, 
@@ -107,7 +108,8 @@ class LLFontGL
 				U8 style = NORMAL, ShadowType shadow = NO_SHADOW, 
 				S32 max_chars = S32_MAX,
 				F32* right_x=NULL, 
-				BOOL use_ellipses = FALSE) const;
+				BOOL use_ellipses = FALSE,
+				BOOL use_color = TRUE) const;
 
 	S32 render(const LLWString &text, S32 begin_offset, 
 				F32 x, F32 y, 
@@ -116,12 +118,13 @@ class LLFontGL
 				U8 style = NORMAL, ShadowType shadow = NO_SHADOW, 
 				S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, 
 				F32* right_x=NULL, 
-				BOOL use_ellipses = FALSE) const;
+				BOOL use_ellipses = FALSE,
+				BOOL use_color = TRUE) const;
 
 	S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;
 
 	// renderUTF8 does a conversion, so is slower!
-	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses) const;
+	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,  F32* right_x = NULL, BOOL use_ellipses = FALSE, BOOL use_color = TRUE) const;
 	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const;
 	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const;
 
@@ -132,12 +135,12 @@ class LLFontGL
 
 	S32 getWidth(const std::string& utf8text) const;
 	S32 getWidth(const llwchar* wchars) const;
-	S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars ) const;
+	S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars) const;
 	S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars) const;
 
 	F32 getWidthF32(const std::string& utf8text) const;
 	F32 getWidthF32(const llwchar* wchars) const;
-	F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const;
+	F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars) const;
 	F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars, bool no_padding = false) const;
 
 	// The following are called often, frequently with large buffers, so do not use a string interface
@@ -165,6 +168,10 @@ class LLFontGL
 
 	static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true);
 
+	       void dumpTextures();
+	static void dumpFonts();
+	static void dumpFontTextures();
+
 	// Load sans-serif, sans-serif-small, etc.
 	// Slow, requires multiple seconds to load fonts.
 	static bool loadDefaultFonts();
@@ -187,6 +194,8 @@ class LLFontGL
 
 	static void setFontDisplay(BOOL flag) { sDisplayFont = flag; }
 		
+	static LLFontGL* getFontEmoji();
+	static LLFontGL* getFontEmojiHuge();
 	static LLFontGL* getFontMonospace();
 	static LLFontGL* getFontSansSerifSmall();
     static LLFontGL* getFontSansSerifSmallBold();
diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp
index 9750bd4fa1e8e30173cdaa37fd47486ec7ae1c61..d2c7e466e6b4f13fcc62e7c03c6f6bfa0a0db65b 100644
--- a/indra/llrender/llfontregistry.cpp
+++ b/indra/llrender/llfontregistry.cpp
@@ -47,6 +47,10 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node);
 const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/";
 const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/";
 
+LLFontDescriptor::char_functor_map_t LLFontDescriptor::mCharFunctors({
+	{ "is_emoji", LLStringOps::isEmoji }
+});
+
 LLFontDescriptor::LLFontDescriptor():
 	mStyle(0)
 {
@@ -55,22 +59,22 @@ LLFontDescriptor::LLFontDescriptor():
 LLFontDescriptor::LLFontDescriptor(const std::string& name,
 								   const std::string& size, 
 								   const U8 style,
-								   const string_vec_t& file_names):
+								   const font_file_info_vec_t& font_files):
 	mName(name),
 	mSize(size),
 	mStyle(style),
-	mFileNames(file_names)
+	mFontFiles(font_files)
 {
 }
 
 LLFontDescriptor::LLFontDescriptor(const std::string& name,
 	const std::string& size,
 	const U8 style,
-	const string_vec_t& file_names,
-	const string_vec_t& ft_collection_listections) :
-	LLFontDescriptor(name, size, style, file_names)
+	const font_file_info_vec_t& font_list,
+	const font_file_info_vec_t& font_collection_files) :
+	LLFontDescriptor(name, size, style, font_list)
 {
-	mFontCollectionsList = ft_collection_listections;
+	mFontCollectionFiles = font_collection_files;
 }
 
 LLFontDescriptor::LLFontDescriptor(const std::string& name,
@@ -82,7 +86,6 @@ LLFontDescriptor::LLFontDescriptor(const std::string& name,
 {
 }
 
-
 bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const
 {
 	if (mName < b.mName)
@@ -175,7 +178,19 @@ LLFontDescriptor LLFontDescriptor::normalize() const
 	if (removeSubString(new_name,"Italic"))
 		new_style |= LLFontGL::ITALIC;
 
-	return LLFontDescriptor(new_name,new_size,new_style,getFileNames(),getFontCollectionsList());
+	return LLFontDescriptor(new_name,new_size,new_style, getFontFiles(), getFontCollectionFiles());
+}
+
+void LLFontDescriptor::addFontFile(const std::string& file_name, const std::string& char_functor)
+{
+	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor);
+	mFontFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));
+}
+
+void LLFontDescriptor::addFontCollectionFile(const std::string& file_name, const std::string& char_functor)
+{
+	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor);
+	mFontCollectionFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));
 }
 
 LLFontRegistry::LLFontRegistry(bool create_gl_textures)
@@ -273,17 +288,24 @@ bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc)
 		if (child->hasName("file"))
 		{
 			std::string font_file_name = child->getTextContents();
-			desc.getFileNames().push_back(font_file_name);
-			
+			std::string char_functor;
+
+			if (child->hasAttribute("functor"))
+			{
+				child->getAttributeString("functor", char_functor);
+			}
+
 			if (child->hasAttribute("load_collection"))
 			{
 				BOOL col = FALSE;
 				child->getAttributeBOOL("load_collection", col);
 				if (col)
 				{
-					desc.getFontCollectionsList().push_back(font_file_name);
+					desc.addFontCollectionFile(font_file_name, char_functor);
 				}
 			}
+
+			desc.addFontFile(font_file_name, char_functor);
 		}
 		else if (child->hasName("os"))
 		{
@@ -326,19 +348,19 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node)
 					// A little roundabout because the map key is const,
 					// so we have to fetch it, make a new map key, and
 					// replace the old entry.
-					string_vec_t match_file_names = match_desc->getFileNames();
-					match_file_names.insert(match_file_names.begin(),
-											desc.getFileNames().begin(),
-											desc.getFileNames().end());
+					font_file_info_vec_t font_files = match_desc->getFontFiles();
+					font_files.insert(font_files.begin(),
+									  desc.getFontFiles().begin(),
+									  desc.getFontFiles().end());
 
-					string_vec_t collections_list = match_desc->getFontCollectionsList();
-					collections_list.insert(collections_list.begin(),
-						desc.getFontCollectionsList().begin(),
-						desc.getFontCollectionsList().end());
+					font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();
+					font_collection_files.insert(font_collection_files.begin(),
+						desc.getFontCollectionFiles().begin(),
+						desc.getFontCollectionFiles().end());
 
 					LLFontDescriptor new_desc = *match_desc;
-					new_desc.getFileNames() = match_file_names;
-					new_desc.getFontCollectionsList() = collections_list;
+					new_desc.setFontFiles(font_files);
+					new_desc.setFontCollectionFiles(font_collection_files);
 					registry->mFontMap.erase(*match_desc);
 					registry->mFontMap[new_desc] = NULL;
 				}
@@ -423,82 +445,80 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)
 
 	// Build list of font names to look for.
 	// Files specified for this font come first, followed by those from the default descriptor.
-	string_vec_t file_names = match_desc->getFileNames();
-	string_vec_t ft_collection_list = match_desc->getFontCollectionsList();
-	string_vec_t default_file_names;
+	font_file_info_vec_t font_files = match_desc->getFontFiles();
+	font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();
 	LLFontDescriptor default_desc("default",s_template_string,0);
 	const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);
 	if (match_default_desc)
 	{
-		file_names.insert(file_names.end(),
-						  match_default_desc->getFileNames().begin(),
-						  match_default_desc->getFileNames().end());
-		ft_collection_list.insert(ft_collection_list.end(),
-			match_default_desc->getFontCollectionsList().begin(),
-			match_default_desc->getFontCollectionsList().end());
+		font_files.insert(font_files.end(),
+						  match_default_desc->getFontFiles().begin(),
+						  match_default_desc->getFontFiles().end());
+		font_collection_files.insert(font_collection_files.end(),
+			match_default_desc->getFontCollectionFiles().begin(),
+			match_default_desc->getFontCollectionFiles().end());
 	}
 
 	// Add ultimate fallback list - generated dynamically on linux,
 	// null elsewhere.
-	file_names.insert(file_names.end(),
-					  getUltimateFallbackList().begin(),
-					  getUltimateFallbackList().end());
+	std::transform(getUltimateFallbackList().begin(), getUltimateFallbackList().end(), std::back_inserter(font_files),
+	               [](const std::string& file_name) { return LLFontFileInfo(file_name); });
 
 	// Load fonts based on names.
-	if (file_names.empty())
+	if (font_files.empty())
 	{
 		LL_WARNS() << "createFont failed, no file names specified" << LL_ENDL;
 		return NULL;
 	}
 
-	LLFontFreetype::font_vector_t fontlist;
 	LLFontGL *result = NULL;
 
-	// Snarf all fonts we can into fontlist.  First will get pulled
-	// off the list and become the "head" font, set to non-fallback.
+	// The first font will get pulled will be the "head" font, set to non-fallback.
 	// Rest will consitute the fallback list.
 	BOOL is_first_found = TRUE;
 	
-	std::string local_path = LLFontGL::getFontPathLocal();
-	std::string sys_path = LLFontGL::getFontPathSystem();
-	
+	string_vec_t font_search_paths;
+	font_search_paths.push_back(LLFontGL::getFontPathLocal());
+	font_search_paths.push_back(LLFontGL::getFontPathSystem());
+#if LL_DARWIN
+	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY);
+	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL);
+	font_search_paths.push_back(LLFontGL::getFontPathSystem() + MACOSX_FONT_SUPPLEMENTAL);
+#endif
+
 	// The fontname string may contain multiple font file names separated by semicolons.
 	// Break it apart and try loading each one, in order.
-	for(string_vec_t::iterator file_name_it = file_names.begin();
-		file_name_it != file_names.end(); 
-		++file_name_it)
+	for(font_file_info_vec_t::iterator font_file_it = font_files.begin();
+		font_file_it != font_files.end();
+		++font_file_it)
 	{
 		LLFontGL *fontp = NULL;
-		string_vec_t font_paths;
-		font_paths.push_back(local_path + *file_name_it);
-		font_paths.push_back(sys_path + *file_name_it);
-#if LL_DARWIN
-		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + *file_name_it);
-		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL + *file_name_it);
-		font_paths.push_back(sys_path +  MACOSX_FONT_SUPPLEMENTAL + *file_name_it);
-#endif
-		
-		bool is_ft_collection = (std::find(ft_collection_list.begin(), ft_collection_list.end(), *file_name_it) != ft_collection_list.end());
+
+		bool is_ft_collection = (std::find_if(font_collection_files.begin(), font_collection_files.end(),
+		                                      [&font_file_it](const LLFontFileInfo& ffi) { return font_file_it->FileName == ffi.FileName; }) != font_collection_files.end());
+
 		// *HACK: Fallback fonts don't render, so we can use that to suppress
 		// creation of OpenGL textures for test apps. JC
 		BOOL is_fallback = !is_first_found || !mCreateGLTextures;
 		F32 extra_scale = (is_fallback)?fallback_scale:1.0;
 		F32 point_size_scale = extra_scale * point_size;
 		bool is_font_loaded = false;
-		for(string_vec_t::iterator font_paths_it = font_paths.begin();
-			font_paths_it != font_paths.end();
-			++font_paths_it)
+		for(string_vec_t::iterator font_search_path_it = font_search_paths.begin();
+			font_search_path_it != font_search_paths.end();
+			++font_search_path_it)
 		{
+			const std::string font_path = *font_search_path_it + font_file_it->FileName;
+
 			fontp = new LLFontGL;
-			S32 num_faces = is_ft_collection ? fontp->getNumFaces(*font_paths_it) : 1;
+			S32 num_faces = is_ft_collection ? fontp->getNumFaces(font_path) : 1;
 			for (S32 i = 0; i < num_faces; i++)
 			{
 				if (fontp == NULL)
 				{
 					fontp = new LLFontGL;
 				}
-				if (fontp->loadFace(*font_paths_it, point_size_scale,
-								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback, i))
+				if (fontp->loadFace(font_path, point_size_scale,
+								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, is_fallback, i))
 				{
 					is_font_loaded = true;
 					if (is_first_found)
@@ -508,7 +528,8 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)
 					}
 					else
 					{
-						fontlist.push_back(fontp->mFontFreetype);
+						result->mFontFreetype->addFallbackFont(fontp->mFontFreetype, font_file_it->CharFunctor);
+
 						delete fontp;
 						fontp = NULL;
 					}
@@ -523,17 +544,12 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)
 		}
 		if(!is_font_loaded)
 		{
-			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it <<  LL_ENDL;
+			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << font_file_it->FileName <<  LL_ENDL;
 			delete fontp;
 			fontp = NULL;
 		}
 	}
 
-	if (result && !fontlist.empty())
-	{
-		result->mFontFreetype->setFallbackFonts(fontlist);
-	}
-
 	if (result)
 	{
 		result->mFontDescriptor = desc;
@@ -720,11 +736,22 @@ void LLFontRegistry::dump()
 				<< " size=[" << desc.getSize() << "]"
 				<< " fileNames="
 				<< LL_ENDL;
-		for (string_vec_t::const_iterator file_it=desc.getFileNames().begin();
-			 file_it != desc.getFileNames().end();
+		for (font_file_info_vec_t::const_iterator file_it=desc.getFontFiles().begin();
+			 file_it != desc.getFontFiles().end();
 			 ++file_it)
 		{
-			LL_INFOS() << "  file: " << *file_it <<LL_ENDL;
+			LL_INFOS() << "  file: " << file_it->FileName << LL_ENDL;
+		}
+	}
+}
+
+void LLFontRegistry::dumpTextures()
+{
+	for (const auto& fontEntry : mFontMap)
+	{
+		if (fontEntry.second)
+		{
+			fontEntry.second->dumpTextures();
 		}
 	}
 }
diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h
index e30c81c63018593ed561b6e9b51f40d9c31e9af8..b0ef72c5ded4ca4112f71f655b78b7506ae2624d 100644
--- a/indra/llrender/llfontregistry.h
+++ b/indra/llrender/llfontregistry.h
@@ -34,13 +34,32 @@ class LLFontGL;
 
 typedef std::vector<std::string> string_vec_t;
 
+struct LLFontFileInfo
+{
+	LLFontFileInfo(const std::string& file_name, const std::function<bool(llwchar)>& char_functor = nullptr)
+		: FileName(file_name)
+		, CharFunctor(char_functor)
+	{
+	}
+
+	LLFontFileInfo(const LLFontFileInfo& ffi)
+		: FileName(ffi.FileName)
+		, CharFunctor(ffi.CharFunctor)
+	{
+	}
+
+	std::string FileName;
+	std::function<bool(llwchar)> CharFunctor;
+};
+typedef std::vector<LLFontFileInfo> font_file_info_vec_t;
+
 class LLFontDescriptor
 {
 public:
 	LLFontDescriptor();
 	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style);
-	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names);
-	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names, const string_vec_t& font_collections);
+	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list);
+	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list, const font_file_info_vec_t& font_collection_list);
 	LLFontDescriptor normalize() const;
 
 	bool operator<(const LLFontDescriptor& b) const;
@@ -51,19 +70,26 @@ class LLFontDescriptor
 	void setName(const std::string& name) { mName = name; }
 	const std::string& getSize() const { return mSize; }
 	void setSize(const std::string& size) { mSize = size; }
-	const std::vector<std::string>& getFileNames() const { return mFileNames; }
-	std::vector<std::string>& getFileNames() { return mFileNames; }
-	const std::vector<std::string>& getFontCollectionsList() const { return mFontCollectionsList; }
-	std::vector<std::string>& getFontCollectionsList() { return mFontCollectionsList; }
+
+	void addFontFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null);
+	const font_file_info_vec_t & getFontFiles() const { return mFontFiles; }
+	void setFontFiles(const font_file_info_vec_t& font_files) { mFontFiles = font_files; }
+	void addFontCollectionFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null);
+	const font_file_info_vec_t& getFontCollectionFiles() const { return mFontCollectionFiles; }
+	void setFontCollectionFiles(const font_file_info_vec_t& font_collection_files) { mFontCollectionFiles = font_collection_files; }
+
 	const U8 getStyle() const { return mStyle; }
 	void setStyle(U8 style) { mStyle = style; }
 
 private:
 	std::string mName;
 	std::string mSize;
-	string_vec_t mFileNames;
-	string_vec_t mFontCollectionsList;
+	font_file_info_vec_t mFontFiles;
+	font_file_info_vec_t mFontCollectionFiles;
 	U8 mStyle;
+
+	typedef std::map<std::string, std::function<bool(llwchar)>> char_functor_map_t;
+	static char_functor_map_t mCharFunctors;
 };
 
 class LLFontRegistry
@@ -94,6 +120,7 @@ class LLFontRegistry
 	bool nameToSize(const std::string& size_name, F32& size);
 
 	void dump();
+	void dumpTextures();
 	
 	const string_vec_t& getUltimateFallbackList() const;
 
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 4c7c8e6f5c91cff3563983267e58dc89c76db1ca..9d3abf32bb4191fbe916a852ea56f43b32225fd1 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -2890,7 +2890,7 @@ void LLGLSyncFence::wait()
 	if (mSync)
 	{
 		while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
-		{
+		{ //track the number of times we've waited here
 		}
 	}
 }
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index c6fd824c4eed881c7aa05ff0f391a44267d60237..56a12b07b1dfc32f7801b502088e7d73458666b8 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1353,6 +1353,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
                 scratch = new(std::nothrow) U32[width * height];
                 if (!scratch)
                 {
+                    LLError::LLUserWarningMsg::showOutOfMemory();
                     LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
                               << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
                 }
@@ -1378,6 +1379,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
                 scratch = new(std::nothrow) U32[width * height];
                 if (!scratch)
                 {
+                    LLError::LLUserWarningMsg::showOutOfMemory();
                     LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
                         << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
                 }
@@ -1406,6 +1408,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
                 scratch = new(std::nothrow) U32[width * height];
                 if (!scratch)
                 {
+                    LLError::LLUserWarningMsg::showOutOfMemory();
                     LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
                         << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
                 }
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index ee66122774d0487d1ed9fe3a85aa7de615a801d2..4d64dc9e104ab729f488091a4afd6b2dc1fe9b02 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -857,7 +857,7 @@ LLRender::~LLRender()
 	shutdown();
 }
 
-void LLRender::init(bool needs_vertex_buffer)
+bool LLRender::init(bool needs_vertex_buffer)
 {
 #if LL_WINDOWS
     if (gGLManager.mHasDebugOutput && gDebugGL)
@@ -879,6 +879,13 @@ void LLRender::init(bool needs_vertex_buffer)
     // necessary for reflection maps
     glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
 
+#if LL_WINDOWS
+    if (glGenVertexArrays == nullptr)
+    {
+        return false;
+    }
+#endif
+
     { //bind a dummy vertex array object so we're core profile compliant
         U32 ret;
         glGenVertexArrays(1, &ret);
@@ -889,6 +896,7 @@ void LLRender::init(bool needs_vertex_buffer)
     {
         initVertexBuffer();
     }
+    return true;
 }
 
 void LLRender::initVertexBuffer()
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index fd922affbabe219dfda2ca9713abce09e89b0407..716b52354dc4fb0c2061e17e05af358f1f1b1e10 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -375,7 +375,7 @@ class LLRender
 
 	LLRender();
 	~LLRender();
-    void init(bool needs_vertex_buffer);
+    bool init(bool needs_vertex_buffer);
     void initVertexBuffer();
     void resetVertexBuffer();
 	void shutdown();
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 9108c6143ca1cf9d0a57e59a58685ec28842f1eb..a0314cb5f2b0e0d7cbceeccc65bf19f9cee176ec 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -29,6 +29,8 @@ set(llui_SOURCE_FILES
     lldockcontrol.cpp
     lldraghandle.cpp
     lleditmenuhandler.cpp
+    llemojidictionary.cpp
+    llemojihelper.cpp
     llf32uictrl.cpp
     llfiltereditor.cpp
     llflashtimer.cpp
@@ -139,6 +141,8 @@ set(llui_HEADER_FILES
     lldockablefloater.h
     lldockcontrol.h
     lleditmenuhandler.h
+    llemojidictionary.h
+    llemojihelper.h
     llf32uictrl.h
     llfiltereditor.h 
     llflashtimer.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 49d275997a62b848e25999cb358ae9904dd84e73..9ef019840abc74905ab0358f3941a3e48a76fa89 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -68,6 +68,7 @@ LLButton::Params::Params()
 	label_shadow("label_shadow", true),
 	auto_resize("auto_resize", false),
 	use_ellipses("use_ellipses", false),
+	use_font_color("use_font_color", true),
 	image_unselected("image_unselected"),
 	image_selected("image_selected"),
 	image_hover_selected("image_hover_selected"),
@@ -160,6 +161,7 @@ LLButton::LLButton(const LLButton::Params& p)
 	mDropShadowedText(p.label_shadow),
 	mAutoResize(p.auto_resize),
 	mUseEllipses( p.use_ellipses ),
+	mUseFontColor( p.use_font_color),
 	mHAlign(p.font_halign),
 	mLeftHPad(p.pad_left),
 	mRightHPad(p.pad_right),
@@ -961,7 +963,7 @@ void LLButton::draw()
 			LLFontGL::NORMAL,
 			mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
 			S32_MAX, text_width,
-			NULL, mUseEllipses);
+			NULL, mUseEllipses, mUseFontColor);
 	}
 
 	LLUICtrl::draw();
@@ -1021,6 +1023,16 @@ BOOL LLButton::toggleState()
 	return flipped; 
 }
 
+void LLButton::setLabel( const std::string& label )
+{
+	mUnselectedLabel = mSelectedLabel = label;
+}
+
+void LLButton::setLabel( const LLUIString& label )
+{
+	mUnselectedLabel = mSelectedLabel = label;
+}
+
 void LLButton::setLabel( const LLStringExplicit& label )
 {
 	setLabelUnselected(label);
@@ -1052,14 +1064,7 @@ bool LLButton::labelIsTruncated() const
 
 const LLUIString& LLButton::getCurrentLabel() const
 {
-	if( getToggleState() )
-	{
-		return mSelectedLabel;
-	}
-	else
-	{
-		return mUnselectedLabel;
-	}
+	return getToggleState() ? mSelectedLabel : mUnselectedLabel;
 }
 
 void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index ccd31e90c0309ecbd4b83915d2d4fc88dc3996da..257159f64fd8090e0309d6f04e6b42ea15babf58 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -73,6 +73,7 @@ class LLButton
 		Optional<bool>			label_shadow;
 		Optional<bool>			auto_resize;
 		Optional<bool>			use_ellipses;
+		Optional<bool>			use_font_color;
 
 		// images
 		Optional<LLUIImage*>	image_unselected,
@@ -174,6 +175,7 @@ class LLButton
 	void			setUnselectedLabelColor( const LLColor4& c )		{ mUnselectedLabelColor = c; }
 	void			setSelectedLabelColor( const LLColor4& c )			{ mSelectedLabelColor = c; }
 	void			setUseEllipses( BOOL use_ellipses )					{ mUseEllipses = use_ellipses; }
+	void			setUseFontColor( BOOL use_font_color)				{ mUseFontColor = use_font_color; }
 
 
 	boost::signals2::connection setClickedCallback(const CommitCallbackParam& cb);
@@ -238,6 +240,8 @@ class LLButton
 	
 	void            autoResize();	// resize with label of current btn state 
 	void            resize(LLUIString label); // resize with label input
+	void			setLabel(const std::string& label);
+	void			setLabel(const LLUIString& label);
 	void			setLabel( const LLStringExplicit& label);
 	virtual BOOL	setLabelArg( const std::string& key, const LLStringExplicit& text );
 	void			setLabelUnselected(const LLStringExplicit& label);
@@ -353,6 +357,7 @@ class LLButton
 	bool						mDropShadowedText;
 	bool						mAutoResize;
 	bool						mUseEllipses;
+	bool						mUseFontColor;
 	bool						mBorderEnabled;
 	bool						mFlashing;
 
diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f16c38a11ae6fccaa17e5086fa24cb236115fafc
--- /dev/null
+++ b/indra/llui/llemojidictionary.cpp
@@ -0,0 +1,469 @@
+/**
+* @file llemojidictionary.cpp
+* @brief Implementation of LLEmojiDictionary
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "linden_common.h"
+
+#include "lldir.h"
+#include "llemojidictionary.h"
+#include "llsdserialize.h"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/range/adaptor/filtered.hpp>
+#include <boost/range/algorithm/transform.hpp>
+
+// ============================================================================
+// Constants
+//
+
+static const std::string SKINNED_EMOJI_FILENAME("emoji_characters.xml");
+static const std::string SKINNED_CATEGORY_FILENAME("emoji_categories.xml");
+static const std::string COMMON_GROUP_FILENAME("emoji_groups.xml");
+static const std::string GROUP_NAME_SKIP("skip");
+// https://www.compart.com/en/unicode/U+1F302
+static const S32 GROUP_OTHERS_IMAGE_INDEX = 0x1F302;
+
+// ============================================================================
+// Helper functions
+//
+
+template<class T>
+std::list<T> llsd_array_to_list(const LLSD& sd, std::function<void(T&)> mutator = {});
+
+template<>
+std::list<std::string> llsd_array_to_list(const LLSD& sd, std::function<void(std::string&)> mutator)
+{
+    std::list<std::string> result;
+    for (LLSD::array_const_iterator it = sd.beginArray(), end = sd.endArray(); it != end; ++it)
+    {
+        const LLSD& entry = *it;
+        if (!entry.isString())
+            continue;
+
+        result.push_back(entry.asStringRef());
+        if (mutator)
+        {
+            mutator(result.back());
+        }
+    }
+    return result;
+}
+
+struct emoji_filter_base
+{
+    emoji_filter_base(const std::string& needle)
+    {
+        // Search without the colon (if present) so the user can type ':food' and see all emojis in the 'Food' category
+        mNeedle = (boost::starts_with(needle, ":")) ? needle.substr(1) : needle;
+        LLStringUtil::toLower(mNeedle);
+    }
+
+protected:
+    std::string mNeedle;
+};
+
+struct emoji_filter_shortcode_or_category_contains : public emoji_filter_base
+{
+    emoji_filter_shortcode_or_category_contains(const std::string& needle) : emoji_filter_base(needle) {}
+
+    bool operator()(const LLEmojiDescriptor& descr) const
+    {
+        for (const auto& short_code : descr.ShortCodes)
+        {
+            if (boost::icontains(short_code, mNeedle))
+                return true;
+        }
+
+        if (boost::icontains(descr.Category, mNeedle))
+            return true;
+
+        return false;
+    }
+};
+
+std::string LLEmojiDescriptor::getShortCodes() const
+{
+    std::string result;
+    for (const std::string& shortCode : ShortCodes)
+    {
+        if (!result.empty())
+        {
+            result += ", ";
+        }
+        result += shortCode;
+    }
+    return result;
+}
+
+// ============================================================================
+// LLEmojiDictionary class
+//
+
+LLEmojiDictionary::LLEmojiDictionary()
+{
+}
+
+// static
+void LLEmojiDictionary::initClass()
+{
+    LLEmojiDictionary* pThis = &LLEmojiDictionary::initParamSingleton();
+
+    pThis->loadTranslations();
+    pThis->loadGroups();
+    pThis->loadEmojis();
+}
+
+LLWString LLEmojiDictionary::findMatchingEmojis(const std::string& needle) const
+{
+    LLWString result;
+    boost::transform(mEmojis | boost::adaptors::filtered(emoji_filter_shortcode_or_category_contains(needle)),
+                     std::back_inserter(result), [](const auto& descr) { return descr.Character; });
+    return result;
+}
+
+// static
+bool LLEmojiDictionary::searchInShortCode(std::size_t& begin, std::size_t& end, const std::string& shortCode, const std::string& needle)
+{
+    begin = 0;
+    end = 1;
+    std::size_t index = 1;
+    // Search for begin
+    char d = tolower(needle[index++]);
+    while (end < shortCode.size())
+    {
+        char s = tolower(shortCode[end++]);
+        if (s == d)
+        {
+            begin = end - 1;
+            break;
+        }
+    }
+    if (!begin)
+        return false;
+    // Search for end
+    d = tolower(needle[index++]);
+    if (!d)
+        return true;
+    while (end < shortCode.size() && index <= needle.size())
+    {
+        char s = tolower(shortCode[end++]);
+        if (s == d)
+        {
+            if (index == needle.size())
+                return true;
+            d = tolower(needle[index++]);
+            continue;
+        }
+        switch (s)
+        {
+        case L'-':
+        case L'_':
+        case L'+':
+            continue;
+        }
+        break;
+    }
+    return false;
+}
+
+void LLEmojiDictionary::findByShortCode(
+    std::vector<LLEmojiSearchResult>& result,
+    const std::string& needle
+) const
+{
+    result.clear();
+
+    if (needle.empty() || needle.front() != ':')
+        return;
+
+    std::map<llwchar, std::vector<LLEmojiSearchResult>> results;
+
+    for (const LLEmojiDescriptor& d : mEmojis)
+    {
+        if (!d.ShortCodes.empty())
+        {
+            const std::string& shortCode = d.ShortCodes.front();
+            if (shortCode.size() >= needle.size() && shortCode.front() == needle.front())
+            {
+                std::size_t begin, end;
+                if (searchInShortCode(begin, end, shortCode, needle))
+                {
+                    results[begin].emplace_back(d.Character, shortCode, begin, end);
+                }
+            }
+        }
+    }
+
+    for (const auto& it : results)
+    {
+#ifdef __cpp_lib_containers_ranges
+        result.append_range(it.second);
+#else
+        result.insert(result.end(), it.second.cbegin(), it.second.cend());
+#endif
+    }
+}
+
+const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromEmoji(llwchar emoji) const
+{
+    const auto it = mEmoji2Descr.find(emoji);
+    return (mEmoji2Descr.end() != it) ? it->second : nullptr;
+}
+
+const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromShortCode(const std::string& short_code) const
+{
+    const auto it = mShortCode2Descr.find(short_code);
+    return (mShortCode2Descr.end() != it) ? it->second : nullptr;
+}
+
+std::string LLEmojiDictionary::getNameFromEmoji(llwchar ch) const
+{
+    const auto it = mEmoji2Descr.find(ch);
+    return (mEmoji2Descr.end() != it) ? it->second->ShortCodes.front() : LLStringUtil::null;
+}
+
+bool LLEmojiDictionary::isEmoji(llwchar ch) const
+{
+    // Currently used codes: A9,AE,203C,2049,2122,...,2B55,3030,303D,3297,3299,1F004,...,1FAF6
+    if (ch == 0xA9 || ch == 0xAE || (ch >= 0x2000 && ch < 0x3300) || (ch >= 0x1F000 && ch < 0x20000))
+    {
+        return mEmoji2Descr.find(ch) != mEmoji2Descr.end();
+    }
+
+    return false;
+}
+
+void LLEmojiDictionary::loadTranslations()
+{
+    std::vector<std::string> filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_CATEGORY_FILENAME, LLDir::CURRENT_SKIN);
+    if (filenames.empty())
+    {
+        LL_WARNS() << "Emoji file categories not found" << LL_ENDL;
+        return;
+    }
+
+    const std::string filename = filenames.back();
+    llifstream file(filename.c_str());
+    if (!file.is_open())
+    {
+        LL_WARNS() << "Emoji file categories failed to open" << LL_ENDL;
+        return;
+    }
+
+    LL_DEBUGS() << "Loading emoji categories file at " << filename << LL_ENDL;
+
+    LLSD data;
+    LLSDSerialize::fromXML(data, file);
+    if (data.isUndefined())
+    {
+        LL_WARNS() << "Emoji file categories missing or ill-formed" << LL_ENDL;
+        return;
+    }
+
+    // Register translations for all categories
+    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it)
+    {
+        const LLSD& sd = *it;
+        const std::string& name = sd["Name"].asStringRef();
+        const std::string& category = sd["Category"].asStringRef();
+        if (!name.empty() && !category.empty())
+        {
+            mTranslations[name] = category;
+        }
+        else
+        {
+            LL_WARNS() << "Skipping invalid emoji category '" << name << "' => '" << category << "'" << LL_ENDL;
+        }
+    }
+}
+
+void LLEmojiDictionary::loadGroups()
+{
+    const std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, COMMON_GROUP_FILENAME);
+    llifstream file(filename.c_str());
+    if (!file.is_open())
+    {
+        LL_WARNS() << "Emoji file groups failed to open" << LL_ENDL;
+        return;
+    }
+
+    LL_DEBUGS() << "Loading emoji groups file at " << filename << LL_ENDL;
+
+    LLSD data;
+    LLSDSerialize::fromXML(data, file);
+    if (data.isUndefined())
+    {
+        LL_WARNS() << "Emoji file groups missing or ill-formed" << LL_ENDL;
+        return;
+    }
+
+    mGroups.clear();
+
+    // Register all groups
+    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it)
+    {
+        const LLSD& sd = *it;
+        const std::string& name = sd["Name"].asStringRef();
+        if (name == GROUP_NAME_SKIP)
+        {
+            mSkipCategories = loadCategories(sd);
+            translateCategories(mSkipCategories);
+        }
+        else
+        {
+            // Add new group
+            mGroups.emplace_back();
+            LLEmojiGroup& group = mGroups.back();
+            group.Character = loadIcon(sd);
+            group.Categories = loadCategories(sd);
+            translateCategories(group.Categories);
+
+            for (const std::string& category : group.Categories)
+            {
+                mCategory2Group.insert(std::make_pair(category, &group));
+            }
+        }
+    }
+
+    // Add group "others"
+    mGroups.emplace_back();
+    mGroups.back().Character = GROUP_OTHERS_IMAGE_INDEX;
+}
+
+void LLEmojiDictionary::loadEmojis()
+{
+    std::vector<std::string> filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_EMOJI_FILENAME, LLDir::CURRENT_SKIN);
+    if (filenames.empty())
+    {
+        LL_WARNS() << "Emoji file characters not found" << LL_ENDL;
+        return;
+    }
+
+    const std::string filename = filenames.back();
+    llifstream file(filename.c_str());
+    if (!file.is_open())
+    {
+        LL_WARNS() << "Emoji file characters failed to open" << LL_ENDL;
+        return;
+    }
+
+    LL_DEBUGS() << "Loading emoji characters file at " << filename << LL_ENDL;
+
+    LLSD data;
+    LLSDSerialize::fromXML(data, file);
+    if (data.isUndefined())
+    {
+        LL_WARNS() << "Emoji file characters missing or ill-formed" << LL_ENDL;
+        return;
+    }
+
+    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it)
+    {
+        const LLSD& sd = *it;
+
+        llwchar icon = loadIcon(sd);
+        if (!icon)
+        {
+            LL_WARNS() << "Skipping invalid emoji descriptor (no icon)" << LL_ENDL;
+            continue;
+        }
+
+        std::list<std::string> categories = loadCategories(sd);
+        if (categories.empty())
+        {
+            LL_WARNS() << "Skipping invalid emoji descriptor (no categories)" << LL_ENDL;
+            continue;
+        }
+
+        std::string category = categories.front();
+
+        if (std::find(mSkipCategories.begin(), mSkipCategories.end(), category) != mSkipCategories.end())
+        {
+            // This category is listed for skip
+            continue;
+        }
+
+        std::list<std::string> shortCodes = loadShortCodes(sd);
+        if (shortCodes.empty())
+        {
+            LL_WARNS() << "Skipping invalid emoji descriptor (no shortCodes)" << LL_ENDL;
+            continue;
+        }
+
+        if (mCategory2Group.find(category) == mCategory2Group.end())
+        {
+            // Add unknown category to "others" group
+            mGroups.back().Categories.push_back(category);
+            mCategory2Group.insert(std::make_pair(category, &mGroups.back()));
+        }
+
+        mEmojis.emplace_back();
+        LLEmojiDescriptor& emoji = mEmojis.back();
+        emoji.Character = icon;
+        emoji.Category = category;
+        emoji.ShortCodes = std::move(shortCodes);
+
+        mEmoji2Descr.insert(std::make_pair(icon, &emoji));
+        mCategory2Descrs[category].push_back(&emoji);
+        for (const std::string& shortCode : emoji.ShortCodes)
+        {
+            mShortCode2Descr.insert(std::make_pair(shortCode, &emoji));
+        }
+    }
+}
+
+llwchar LLEmojiDictionary::loadIcon(const LLSD& sd)
+{
+    // We don't currently support character composition
+    const LLWString icon = utf8str_to_wstring(sd["Character"].asString());
+    return (1 == icon.size()) ? icon[0] : L'\0';
+}
+
+std::list<std::string> LLEmojiDictionary::loadCategories(const LLSD& sd)
+{
+    static const std::string key("Categories");
+    return llsd_array_to_list<std::string>(sd[key]);
+}
+
+std::list<std::string> LLEmojiDictionary::loadShortCodes(const LLSD& sd)
+{
+    static const std::string key("ShortCodes");
+    auto toLower = [](std::string& str) { LLStringUtil::toLower(str); };
+    return llsd_array_to_list<std::string>(sd[key], toLower);
+}
+
+void LLEmojiDictionary::translateCategories(std::list<std::string>& categories)
+{
+    for (std::string& category : categories)
+    {
+        auto it = mTranslations.find(category);
+        if (it != mTranslations.end())
+        {
+            category = it->second;
+        }
+    }
+}
+
+// ============================================================================
diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h
new file mode 100644
index 0000000000000000000000000000000000000000..4af376df64397202095e53319373dd4373839a52
--- /dev/null
+++ b/indra/llui/llemojidictionary.h
@@ -0,0 +1,126 @@
+/**
+* @file llemojidictionary.h
+* @brief Header file for LLEmojiDictionary
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#pragma once
+
+#include "lldictionary.h"
+#include "llinitdestroyclass.h"
+#include "llsingleton.h"
+
+// ============================================================================
+// LLEmojiDescriptor class
+//
+
+struct LLEmojiDescriptor
+{
+    llwchar Character;
+    std::string Category;
+    std::list<std::string> ShortCodes;
+    std::string getShortCodes() const;
+};
+
+// ============================================================================
+// LLEmojiGroup class
+//
+
+struct LLEmojiGroup
+{
+    llwchar Character;
+    std::list<std::string> Categories;
+};
+
+// ============================================================================
+// LLEmojiSearchResult class
+//
+
+struct LLEmojiSearchResult
+{
+    llwchar Character;
+    std::string String;
+    std::size_t Begin, End;
+
+    LLEmojiSearchResult(llwchar character, const std::string& string, std::size_t begin, std::size_t end)
+        : Character(character)
+        , String(string)
+        , Begin(begin)
+        , End(end)
+    {
+    }
+};
+
+// ============================================================================
+// LLEmojiDictionary class
+//
+
+class LLEmojiDictionary : public LLParamSingleton<LLEmojiDictionary>, public LLInitClass<LLEmojiDictionary>
+{
+    LLSINGLETON(LLEmojiDictionary);
+    ~LLEmojiDictionary() override {};
+
+public:
+    typedef std::map<std::string, std::string> cat2cat_map_t;
+    typedef std::map<std::string, const LLEmojiGroup*> cat2group_map_t;
+    typedef std::map<llwchar, const LLEmojiDescriptor*> emoji2descr_map_t;
+    typedef std::map<std::string, const LLEmojiDescriptor*> code2descr_map_t;
+    typedef std::map<std::string, std::vector<const LLEmojiDescriptor*>> cat2descrs_map_t;
+
+    static void initClass();
+    LLWString findMatchingEmojis(const std::string& needle) const;
+    static bool searchInShortCode(std::size_t& begin, std::size_t& end, const std::string& shortCode, const std::string& needle);
+    void findByShortCode(std::vector<LLEmojiSearchResult>& result, const std::string& needle) const;
+    const LLEmojiDescriptor* getDescriptorFromEmoji(llwchar emoji) const;
+    const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const;
+    std::string getNameFromEmoji(llwchar ch) const;
+    bool isEmoji(llwchar ch) const;
+
+    const std::vector<LLEmojiGroup>& getGroups() const { return mGroups; }
+    const emoji2descr_map_t& getEmoji2Descr() const { return mEmoji2Descr; }
+    const cat2descrs_map_t& getCategory2Descrs() const { return mCategory2Descrs; }
+    const code2descr_map_t& getShortCode2Descr() const { return mShortCode2Descr; }
+
+private:
+    void loadTranslations();
+    void loadGroups();
+    void loadEmojis();
+
+    static llwchar loadIcon(const LLSD& sd);
+    static std::list<std::string> loadCategories(const LLSD& sd);
+    static std::list<std::string> loadShortCodes(const LLSD& sd);
+    void translateCategories(std::list<std::string>& categories);
+
+private:
+    std::vector<LLEmojiGroup> mGroups;
+    std::list<LLEmojiDescriptor> mEmojis;
+    std::list<std::string> mSkipCategories;
+
+    cat2cat_map_t mTranslations;
+    cat2group_map_t mCategory2Group;
+    emoji2descr_map_t mEmoji2Descr;
+    cat2descrs_map_t mCategory2Descrs;
+    code2descr_map_t mShortCode2Descr;
+};
+
+// ============================================================================
diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..89e6ddf987e41aada01a6d97f4df66be4c17a144
--- /dev/null
+++ b/indra/llui/llemojihelper.cpp
@@ -0,0 +1,169 @@
+/**
+* @file llemojihelper.h
+* @brief Header file for LLEmojiHelper
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "linden_common.h"
+
+#include "llemojidictionary.h"
+#include "llemojihelper.h"
+#include "llfloater.h"
+#include "llfloaterreg.h"
+#include "lluictrl.h"
+
+// ============================================================================
+// Constants
+//
+
+constexpr char DEFAULT_EMOJI_HELPER_FLOATER[] = "emoji_picker";
+constexpr S32 HELPER_FLOATER_OFFSET_X = 0;
+constexpr S32 HELPER_FLOATER_OFFSET_Y = 0;
+
+// ============================================================================
+// LLEmojiHelper
+//
+
+std::string LLEmojiHelper::getToolTip(llwchar ch) const
+{
+	return LLEmojiDictionary::instance().getNameFromEmoji(ch);
+}
+
+bool LLEmojiHelper::isActive(const LLUICtrl* ctrl_p) const
+{
+	return mHostHandle.get() == ctrl_p;
+}
+
+// static
+bool LLEmojiHelper::isCursorInEmojiCode(const LLWString& wtext, S32 cursorPos, S32* pShortCodePos)
+{
+	// If the cursor is currently on a colon start the check one character further back
+	S32 shortCodePos = (cursorPos == 0 || L':' != wtext[cursorPos - 1]) ? cursorPos : cursorPos - 1;
+
+	auto isPartOfShortcode = [](llwchar ch) {
+		switch (ch)
+		{
+			case L'-':
+			case L'_':
+			case L'+':
+				return true;
+			default:
+				return LLStringOps::isAlnum(ch);
+		}
+	};
+	while (shortCodePos > 1 && isPartOfShortcode(wtext[shortCodePos - 1]))
+	{
+		shortCodePos--;
+	}
+
+	bool isShortCode = (L':' == wtext[shortCodePos - 1]) && (cursorPos - shortCodePos >= 2);
+	if (pShortCodePos)
+		*pShortCodePos = (isShortCode) ? shortCodePos - 1 : -1;
+	return isShortCode;
+}
+
+void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> cb)
+{
+	// Commit immediately if the user already typed a full shortcode
+	if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code))
+	{
+		cb(emojiDescrp->Character);
+		hideHelper();
+		return;
+	}
+
+	if (mHelperHandle.isDead())
+	{
+		LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER);
+		mHelperHandle = pHelperFloater->getHandle();
+		mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())[0]); }, std::placeholders::_2));
+	}
+	setHostCtrl(hostctrl_p);
+	mEmojiCommitCb = cb;
+
+	S32 floater_x, floater_y;
+	if (!hostctrl_p->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView))
+	{
+		LL_ERRS() << "Cannot show emoji helper for non-floater controls." << LL_ENDL;
+		return;
+	}
+
+	LLFloater* pHelperFloater = mHelperHandle.get();
+	LLRect rect = pHelperFloater->getRect();
+	S32 left = floater_x - HELPER_FLOATER_OFFSET_X;
+	S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight();
+	rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
+	pHelperFloater->setRect(rect);
+	pHelperFloater->openFloater(LLSD().with("hint", short_code));
+}
+
+void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict)
+{
+	mIsHideDisabled &= !strict;
+	if (mIsHideDisabled || (ctrl_p && !isActive(ctrl_p)))
+	{
+		return;
+	}
+
+	setHostCtrl(nullptr);
+}
+
+bool LLEmojiHelper::handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask)
+{
+	if (mHelperHandle.isDead() || !isActive(ctrl_p))
+	{
+		return false;
+	}
+
+	return mHelperHandle.get()->handleKey(key, mask, true);
+}
+
+void LLEmojiHelper::onCommitEmoji(llwchar emoji)
+{
+	if (!mHostHandle.isDead() && mEmojiCommitCb)
+	{
+		mEmojiCommitCb(emoji);
+	}
+}
+
+void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p)
+{
+	const LLUICtrl* pCurHostCtrl = mHostHandle.get();
+	if (pCurHostCtrl != hostctrl_p)
+	{
+		mHostCtrlFocusLostConn.disconnect();
+		mHostHandle.markDead();
+		mEmojiCommitCb = {};
+
+		if (!mHelperHandle.isDead())
+		{
+			mHelperHandle.get()->closeFloater();
+		}
+
+		if (hostctrl_p)
+		{
+			mHostHandle = hostctrl_p->getHandle();
+			mHostCtrlFocusLostConn = hostctrl_p->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); }));
+		}
+	}
+}
diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..e826ff93e620aea2ad408358c07c03d18ba6073f
--- /dev/null
+++ b/indra/llui/llemojihelper.h
@@ -0,0 +1,66 @@
+/**
+* @file llemojihelper.h
+* @brief Header file for LLEmojiHelper
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#pragma once
+
+#include "llhandle.h"
+#include "llsingleton.h"
+
+#include <boost/signals2.hpp>
+
+class LLFloater;
+class LLUICtrl;
+
+class LLEmojiHelper : public LLSingleton<LLEmojiHelper>
+{
+	LLSINGLETON(LLEmojiHelper) {}
+	~LLEmojiHelper() override {}
+
+public:
+	// General
+	std::string getToolTip(llwchar ch) const;
+	bool        isActive(const LLUICtrl* ctrl_p) const;
+	static bool isCursorInEmojiCode(const LLWString& wtext, S32 cursor_pos, S32* short_code_pos_p = nullptr);
+	void        showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> commit_cb);
+	void        hideHelper(const LLUICtrl* ctrl_p = nullptr, bool strict = false);
+	void        setIsHideDisabled(bool disabled) { mIsHideDisabled = disabled; };
+
+	// Eventing
+	bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask);
+	void onCommitEmoji(llwchar emoji);
+
+protected:
+	LLUICtrl* getHostCtrl() const { return mHostHandle.get(); }
+	void      setHostCtrl(LLUICtrl* hostctrl_p);
+
+private:
+	LLHandle<LLUICtrl>  mHostHandle;
+	LLHandle<LLFloater> mHelperHandle;
+	boost::signals2::connection mHostCtrlFocusLostConn;
+	boost::signals2::connection mHelperCommitConn;
+	std::function<void(llwchar)> mEmojiCommitCb;
+	bool mIsHideDisabled;
+};
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 2303cd24b7c8e5bf1ad35caded2ab11316c90ac4..de3de53569a80db0a3f198c7c4c70ba43ecfca5b 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -182,6 +182,7 @@ LLFloater::Params::Params()
 	save_visibility("save_visibility", false),
 	can_dock("can_dock", false),
 	show_title("show_title", true),
+	auto_close("auto_close", false),
 	positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),
 	header_height("header_height", 0),
 	legacy_header_height("legacy_header_height", 0),
@@ -254,6 +255,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
 	mCanClose(p.can_close),
 	mDragOnLeft(p.can_drag_on_left),
 	mResizable(p.can_resize),
+	mAutoClose(p.auto_close),
 	mPositioning(p.positioning),
 	mMinWidth(p.min_width),
 	mMinHeight(p.min_height),
@@ -504,6 +506,7 @@ void LLFloater::enableResizeCtrls(bool enable, bool width, bool height)
 
 void LLFloater::destroy()
 {
+	gFloaterView->onDestroyFloater(this);
 	// LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before
 	// it was deleted via LLMortician::updateClass(). See EXT-8458.
 	LLFloaterReg::removeInstance(mInstanceName, mKey);
@@ -681,7 +684,7 @@ void LLFloater::openFloater(const LLSD& key)
 	if (getHost() != NULL)
 	{
 		getHost()->setMinimized(FALSE);
-		getHost()->setVisibleAndFrontmost(mAutoFocus);
+		getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome());
 		getHost()->showFloater(this);
 	}
 	else
@@ -693,7 +696,7 @@ void LLFloater::openFloater(const LLSD& key)
 		}
 		applyControlsAndPosition(floater_to_stack);
 		setMinimized(FALSE);
-		setVisibleAndFrontmost(mAutoFocus);
+		setVisibleAndFrontmost(mAutoFocus && !getIsChrome());
 	}
 
 	mOpenSignal(this, key);
@@ -829,6 +832,24 @@ void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
 	LLPanel::reshape(width, height, called_from_parent);
 }
 
+// virtual
+void LLFloater::translate(S32 x, S32 y)
+{
+    LLView::translate(x, y);
+
+    if (!mTranslateWithDependents || mDependents.empty())
+        return;
+
+    for (const LLHandle<LLFloater>& handle : mDependents)
+    {
+        LLFloater* floater = handle.get();
+        if (floater && floater->getSnapTarget() == getHandle())
+        {
+            floater->LLView::translate(x, y);
+        }
+    }
+}
+
 void LLFloater::releaseFocus()
 {
 	LLUI::getInstance()->removePopup(this);
@@ -1117,9 +1138,9 @@ BOOL LLFloater::canSnapTo(const LLView* other_view)
 
 	if (other_view != getParent())
 	{
-		const LLFloater* other_floaterp = dynamic_cast<const LLFloater*>(other_view);		
-		if (other_floaterp 
-			&& other_floaterp->getSnapTarget() == getHandle() 
+		const LLFloater* other_floaterp = dynamic_cast<const LLFloater*>(other_view);
+		if (other_floaterp
+			&& other_floaterp->getSnapTarget() == getHandle()
 			&& mDependents.find(other_floaterp->getHandle()) != mDependents.end())
 		{
 			// this is a dependent that is already snapped to us, so don't snap back to it
@@ -1509,30 +1530,40 @@ BOOL LLFloater::isFrontmost()
 				&& floater_view->getFrontmost() == this);
 }
 
-void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
+void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition, BOOL resize)
 {
 	mDependents.insert(floaterp->getHandle());
 	floaterp->mDependeeHandle = getHandle();
 
 	if (reposition)
 	{
-		floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
+		LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp);
+		if (resize)
+		{
+			const LLRect& base = getRect();
+			if (rect.mTop == base.mTop)
+				rect.mBottom = base.mBottom;
+			else if (rect.mLeft == base.mLeft)
+				rect.mRight = base.mRight;
+			floaterp->reshape(rect.getWidth(), rect.getHeight(), FALSE);
+		}
+		floaterp->setRect(rect);
 		floaterp->setSnapTarget(getHandle());
 	}
 	gFloaterView->adjustToFitScreen(floaterp, FALSE, TRUE);
 	if (floaterp->isFrontmost())
 	{
 		// make sure to bring self and sibling floaters to front
-		gFloaterView->bringToFront(floaterp);
+		gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome());
 	}
 }
 
-void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, BOOL reposition)
+void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, BOOL reposition, BOOL resize)
 {
 	LLFloater* dependent_floaterp = dependent.get();
 	if(dependent_floaterp)
 	{
-		addDependentFloater(dependent_floaterp, reposition);
+		addDependentFloater(dependent_floaterp, reposition, resize);
 	}
 }
 
@@ -1542,6 +1573,44 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp)
 	floaterp->mDependeeHandle = LLHandle<LLFloater>();
 }
 
+void LLFloater::fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels)
+{
+    LLRect total_rect = getRect();
+
+    for (const LLHandle<LLFloater>& handle : mDependents)
+    {
+        LLFloater* floater = handle.get();
+        if (floater && floater->getSnapTarget() == getHandle())
+        {
+            total_rect.unionWith(floater->getRect());
+        }
+    }
+
+	S32 delta_left = left.notEmpty() ? left.mRight - total_rect.mRight : 0;
+	S32 delta_bottom = bottom.notEmpty() ? bottom.mTop - total_rect.mTop : 0;
+	S32 delta_right = right.notEmpty() ? right.mLeft - total_rect.mLeft : 0;
+
+	// move floater with dependings fully onscreen
+    mTranslateWithDependents = true;
+    if (translateRectIntoRect(total_rect, constraint, min_overlap_pixels))
+    {
+        clearSnapTarget();
+    }
+    else if (delta_left > 0 && total_rect.mTop < left.mTop && total_rect.mBottom > left.mBottom)
+    {
+        translate(delta_left, 0);
+    }
+    else if (delta_bottom > 0 && total_rect.mLeft > bottom.mLeft && total_rect.mRight < bottom.mRight)
+    {
+        translate(0, delta_bottom);
+    }
+    else if (delta_right < 0 && total_rect.mTop < right.mTop    && total_rect.mBottom > right.mBottom)
+    {
+        translate(delta_right, 0);
+    }
+    mTranslateWithDependents = false;
+}
+
 BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index)
 {
 	if( mButtonsEnabled[index] )
@@ -1630,6 +1699,7 @@ BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
 	return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
 }
 
+// virtual
 void LLFloater::bringToFront( S32 x, S32 y )
 {
 	if (getVisible() && pointInView(x, y))
@@ -1644,12 +1714,20 @@ void LLFloater::bringToFront( S32 x, S32 y )
 			LLFloaterView* parent = dynamic_cast<LLFloaterView*>( getParent() );
 			if (parent)
 			{
-				parent->bringToFront( this );
+				parent->bringToFront(this, !getIsChrome());
 			}
 		}
 	}
 }
 
+// virtual
+void LLFloater::goneFromFront()
+{
+    if (mAutoClose)
+    {
+        closeFloater();
+    }
+}
 
 // virtual
 void LLFloater::setVisibleAndFrontmost(BOOL take_focus,const LLSD& key)
@@ -2488,13 +2566,18 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore
 
 	if (mFrontChild == child)
 	{
-		if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
+		if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child))
 		{
 			child->setFocus(TRUE);
 		}
 		return;
 	}
 
+	if (mFrontChild)
+	{
+		mFrontChild->goneFromFront();
+	}
+
 	mFrontChild = child;
 
 	// *TODO: make this respect floater's mAutoFocus value, instead of
@@ -2852,10 +2935,17 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
 		// floater is hosted elsewhere, so ignore
 		return;
 	}
+
+	if (floater->getDependee() &&
+		floater->getDependee() == floater->getSnapTarget().get())
+	{
+		// floater depends on other and snaps to it, so ignore
+		return;
+	}
+
 	LLRect::tCoordType screen_width = getSnapRect().getWidth();
 	LLRect::tCoordType screen_height = getSnapRect().getHeight();
 
-	
 	// only automatically resize non-minimized, resizable floaters
 	if( floater->isResizable() && !floater->isMinimized() )
 	{
@@ -2897,29 +2987,10 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
 		}
 	}
 
-	const LLRect& floater_rect = floater->getRect();
-
-	S32 delta_left = mToolbarLeftRect.notEmpty() ? mToolbarLeftRect.mRight - floater_rect.mRight : 0;
-	S32 delta_bottom = mToolbarBottomRect.notEmpty() ? mToolbarBottomRect.mTop - floater_rect.mTop : 0;
-	S32 delta_right = mToolbarRightRect.notEmpty() ? mToolbarRightRect.mLeft - floater_rect.mLeft : 0;
+    const LLRect& constraint = snap_in_toolbars ? getSnapRect() : gFloaterView->getRect();
+    S32 min_overlap_pixels = allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX;
 
-	// move window fully onscreen
-	if (floater->translateIntoRect( snap_in_toolbars ? getSnapRect() : gFloaterView->getRect(), allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX ))
-	{
-		floater->clearSnapTarget();
-	}
-	else if (delta_left > 0 && floater_rect.mTop < mToolbarLeftRect.mTop && floater_rect.mBottom > mToolbarLeftRect.mBottom)
-	{
-		floater->translate(delta_left, 0);
-	}
-	else if (delta_bottom > 0 && floater_rect.mLeft > mToolbarBottomRect.mLeft && floater_rect.mRight < mToolbarBottomRect.mRight)
-	{
-		floater->translate(0, delta_bottom);
-	}
-	else if (delta_right < 0 && floater_rect.mTop < mToolbarRightRect.mTop	&& floater_rect.mBottom > mToolbarRightRect.mBottom)
-	{
-		floater->translate(delta_right, 0);
-	}
+	floater->fitWithDependentsOnScreen(mToolbarLeftRect, mToolbarBottomRect, mToolbarRightRect, constraint, min_overlap_pixels);
 }
 
 void LLFloaterView::draw()
@@ -3006,6 +3077,9 @@ LLFloater *LLFloaterView::getBackmost() const
 
 void LLFloaterView::syncFloaterTabOrder()
 {
+	if (mFrontChild && !mFrontChild->isDead() && mFrontChild->getIsChrome())
+		return;
+
 	// look for a visible modal dialog, starting from first
 	LLModalDialog* modal_dialog = NULL;
 	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
@@ -3041,7 +3115,34 @@ void LLFloaterView::syncFloaterTabOrder()
 			LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);
 			if (gFocusMgr.childHasKeyboardFocus(floaterp))
 			{
-				bringToFront(floaterp, FALSE);
+                if (mFrontChild != floaterp)
+                {
+                    // Grab a list of the top floaters that want to stay on top of the focused floater
+					std::list<LLFloater*> listTop;
+					if (mFrontChild && !mFrontChild->canFocusStealFrontmost())
+                    {
+                        for (LLView* childp : *getChildList())
+                        {
+							LLFloater* child_floaterp = static_cast<LLFloater*>(childp);
+                            if (child_floaterp->canFocusStealFrontmost())
+                                break;
+							listTop.push_back(child_floaterp);
+                        }
+                    }
+
+                    bringToFront(floaterp, FALSE);
+
+                    // Restore top floaters
+					if (!listTop.empty())
+					{
+						for (LLView* childp : listTop)
+						{
+							sendChildToFront(childp);
+						}
+						mFrontChild = listTop.back();
+					}
+                }
+
 				break;
 			}
 		}
@@ -3134,6 +3235,14 @@ void LLFloaterView::setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LL
 	}
 }
 
+void LLFloaterView::onDestroyFloater(LLFloater* floater)
+{
+    if (mFrontChild == floater)
+    {
+        mFrontChild = nullptr;
+    }
+}
+
 void LLFloater::setInstanceName(const std::string& name)
 {
 	if (name != mInstanceName)
@@ -3226,6 +3335,7 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
 	mDefaultRelativeY = p.rel_y;
 
 	mPositioning = p.positioning;
+	mAutoClose = p.auto_close;
 
 	mSaveRect = p.save_rect;
 	if (p.save_visibility)
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 3d157082956b9ef5c4fb8473e1f593f010f8f766..88f9e7777716ae992e33af9faaa5a5de803888d3 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -113,8 +113,6 @@ struct LLCoordFloater : LLCoord<LL_COORD_FLOATER>
 	bool operator!=(const LLCoordFloater& other) const { return !(*this == other); }
 
 	void setFloater(LLFloater& floater);
-
-	
 };
 
 class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
@@ -165,7 +163,8 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 								save_visibility,
 								save_dock_state,
 								can_dock,
-								show_title;
+								show_title,
+								auto_close;
 		
 		Optional<LLFloaterEnums::EOpenPositioning>	positioning;
 		
@@ -238,6 +237,7 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	virtual void	closeHostedFloater();
 
 	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+	/*virtual*/ void translate(S32 x, S32 y);
 	
 	// Release keyboard and mouse focus
 	void			releaseFocus();
@@ -256,10 +256,11 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	std::string		getShortTitle() const;
 	virtual void	setMinimized(BOOL b);
 	void			moveResizeHandlesToFront();
-	void			addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE);
-	void			addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE);
+	void			addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE, BOOL resize = FALSE);
+	void			addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE, BOOL resize = FALSE);
 	LLFloater*		getDependee() { return (LLFloater*)mDependeeHandle.get(); }
-	void		removeDependentFloater(LLFloater* dependent);
+	void			removeDependentFloater(LLFloater* dependent);
+	void			fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels);
 	BOOL			isMinimized() const				{ return mMinimized; }
 	/// isShown() differs from getVisible() in that isShown() also considers
 	/// isMinimized(). isShown() is true only if visible and not minimized.
@@ -314,6 +315,9 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	/*virtual*/ void setVisible(BOOL visible); // do not override
 	/*virtual*/ void onVisibilityChange ( BOOL new_visibility ); // do not override
 	
+	bool            canFocusStealFrontmost() const { return mFocusStealsFrontmost; }
+	void            setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; }
+
 	void			setFrontmost(BOOL take_focus = TRUE, BOOL restore = TRUE);
      virtual void	setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD());
 	
@@ -387,6 +391,7 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	void		 	setInstanceName(const std::string& name);
 	
 	virtual void	bringToFront(S32 x, S32 y);
+	virtual void	goneFromFront();
 	
 	void			setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
 	const LLRect&	getExpandedRect() const { return mExpandedRect; }
@@ -482,8 +487,10 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	BOOL			mCanTearOff;
 	BOOL			mCanMinimize;
 	BOOL			mCanClose;
+    bool            mFocusStealsFrontmost = true;	// FALSE if we don't want the currently focused floater to cover this floater without user interaction
 	BOOL			mDragOnLeft;
 	BOOL			mResizable;
+	BOOL			mAutoClose;
 
 	LLFloaterEnums::EOpenPositioning	mPositioning;
 	LLCoordFloater	mPosition;
@@ -503,6 +510,7 @@ class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
 	typedef std::set<LLHandle<LLFloater> > handle_set_t;
 	typedef std::set<LLHandle<LLFloater> >::iterator handle_set_iter_t;
 	handle_set_t	mDependents;
+	bool			mTranslateWithDependents { false };
 
 	bool			mButtonsEnabled[BUTTON_COUNT];
 	F32				mButtonScale;
@@ -599,6 +607,7 @@ class LLFloaterView : public LLUICtrl
 	LLFloater* getFrontmostClosableFloater(); 
 
 	void setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect);
+	void onDestroyFloater(LLFloater* floater);
 
 private:
 	void hiddenFloaterClosed(LLFloater* floater);
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 0dc66bf37a789193a7091b05da725c6a2d6efd52..bc9469cfadf296c8f1843858a1994d4b37d7f8bb 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -890,7 +890,7 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y
     //
     font->renderUTF8(mLabel, 0, x, y, color,
         LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
-        S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, TRUE);
+        S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/TRUE);
 }
 
 void LLFolderViewItem::draw()
@@ -999,7 +999,7 @@ void LLFolderViewItem::draw()
 	{
         suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,
 						  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
-						  S32_MAX, S32_MAX, &right_x, FALSE );
+						  S32_MAX, S32_MAX, &right_x);
 	}
 
 	//--------------------------------------------------------------------------------//
@@ -1011,9 +1011,9 @@ void LLFolderViewItem::draw()
         {
             F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length);
             F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
-            font->renderUTF8( combined_string, filter_offset, match_string_left, yy,
-            sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
-            filter_string_length, S32_MAX, &right_x, FALSE );
+            font->renderUTF8(combined_string, filter_offset, match_string_left, yy,
+                sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+                filter_string_length, S32_MAX, &right_x);
         }
         else
         {
@@ -1022,8 +1022,9 @@ void LLFolderViewItem::draw()
             {
                 F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length);
                 F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
-                font->renderUTF8( mLabel, filter_offset, match_string_left, yy,
- sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, FALSE );
+                font->renderUTF8(mLabel, filter_offset, match_string_left, yy,
+                    sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+                    label_filter_length, S32_MAX, &right_x);
             }
             
             S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
@@ -1032,7 +1033,9 @@ void LLFolderViewItem::draw()
                 S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
                 F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length);
                 F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
-                suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, FALSE );
+                suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor,
+                    LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+                    suffix_filter_length, S32_MAX, &right_x);
             }
         }
 
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 60dbfd68c67783384aaa7c5828862b005982f595..453fa29e7c612bf951b1082e18ae77d60bbe3242 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -89,6 +89,7 @@ LLLineEditor::Params::Params()
 	background_image_disabled("background_image_disabled"),
 	background_image_focused("background_image_focused"),
 	bg_image_always_focused("bg_image_always_focused", false),
+	show_label_focused("show_label_focused", false),
 	select_on_focus("select_on_focus", false),
 	revert_on_esc("revert_on_esc", true),
 	spellcheck("spellcheck", false),
@@ -152,6 +153,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 	mBgImageDisabled( p.background_image_disabled ),
 	mBgImageFocused( p.background_image_focused ),
 	mShowImageFocused( p.bg_image_always_focused ),
+	mShowLabelFocused( p.show_label_focused ),
 	mUseBgColor(p.use_bg_color),
 	mHaveHistory(FALSE),
 	mReplaceNewlinesWithSpaces( TRUE ),
@@ -1737,6 +1739,20 @@ void LLLineEditor::drawBackground()
 	}
 }
 
+//virtual 
+const std::string LLLineEditor::getToolTip() const
+{
+    if (sDebugUnicode)
+    {
+        std::string text = getText();
+        std::string tooltip = utf8str_showBytesUTF8(text);
+        return tooltip;
+    }
+
+    return LLUICtrl::getToolTip();
+}
+
+//virtual 
 void LLLineEditor::draw()
 {
 	F32 alpha = getDrawContext().mAlpha;
@@ -2069,7 +2085,7 @@ void LLLineEditor::draw()
 		//draw label if no text is provided
 		//but we should draw it in a different color
 		//to give indication that it is not text you typed in
-		if (0 == mText.length() && mReadOnly)
+		if (0 == mText.length() && (mReadOnly || mShowLabelFocused))
 		{
 			mGLFont->render(mLabel.getWString(), 0,
 							mTextLeftEdge, (F32)text_bottom,
@@ -2105,7 +2121,7 @@ void LLLineEditor::draw()
 							LLFontGL::NO_SHADOW,
 							S32_MAX,
 							mTextRightEdge - ll_round(rendered_pixels_right),
-							&rendered_pixels_right, FALSE);
+							&rendered_pixels_right);
 		}
 		// Draw children (border)
 		LLView::draw();
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index f983828d2b4e37fc4ff682922ddbfec9c3e152b6..5794b3c35abb17e4ea2d91037680918e28ac4132 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -91,6 +91,7 @@ class LLLineEditor
 										commit_on_focus_lost,
 										ignore_tab,
 										bg_image_always_focused,
+										show_label_focused,
 										is_password,
 										use_bg_color;
 
@@ -118,54 +119,55 @@ class LLLineEditor
 	friend class LLUICtrlFactory;
 	friend class LLFloaterEditUI;
 	void showContextMenu(S32 x, S32 y);
+
 public:
 	virtual ~LLLineEditor();
 
 	// mousehandler overrides
-	/*virtual*/ BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL	handleHover(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL	handleDoubleClick(S32 x,S32 y,MASK mask);
-	/*virtual*/ BOOL	handleMiddleMouseDown(S32 x,S32 y,MASK mask);
-	/*virtual*/ BOOL	handleRightMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL	handleKeyHere(KEY key, MASK mask );
-	/*virtual*/ BOOL	handleUnicodeCharHere(llwchar uni_char);
-	/*virtual*/ void	onMouseCaptureLost();
+	/*virtual*/ BOOL	handleMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL	handleMouseUp(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL	handleHover(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL	handleDoubleClick(S32 x,S32 y,MASK mask) override;
+	/*virtual*/ BOOL	handleMiddleMouseDown(S32 x,S32 y,MASK mask) override;
+	/*virtual*/ BOOL	handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL	handleKeyHere(KEY key, MASK mask) override;
+	/*virtual*/ BOOL	handleUnicodeCharHere(llwchar uni_char) override;
+	/*virtual*/ void	onMouseCaptureLost() override;
 
 	// LLEditMenuHandler overrides
-	virtual void	cut();
-	virtual BOOL	canCut() const;
-	virtual void	copy();
-	virtual BOOL	canCopy() const;
-	virtual void	paste();
-	virtual BOOL	canPaste() const;
+	/*virtual*/ void	cut() override;
+	/*virtual*/ BOOL	canCut() const override;
+	/*virtual*/ void	copy() override;
+	/*virtual*/ BOOL	canCopy() const override;
+	/*virtual*/ void	paste() override;
+	/*virtual*/ BOOL	canPaste() const override;
 
 	virtual void	updatePrimary();
 	virtual void	copyPrimary();
  	virtual void	pastePrimary();
 	virtual BOOL	canPastePrimary() const;
 
-	virtual void	doDelete();
-	virtual BOOL	canDoDelete() const;
+	/*virtual*/ void	doDelete() override;
+	/*virtual*/ BOOL	canDoDelete() const override;
 
-	virtual void	selectAll();
-	virtual BOOL	canSelectAll() const;
+	/*virtual*/ void	selectAll() override;
+	/*virtual*/ BOOL	canSelectAll() const override;
 
-	virtual void	deselect();
-	virtual BOOL	canDeselect() const;
+	/*virtual*/ void	deselect() override;
+	/*virtual*/ BOOL	canDeselect() const override;
 
 	// LLSpellCheckMenuHandler overrides
-	/*virtual*/ bool	getSpellCheck() const;
+	/*virtual*/ bool	getSpellCheck() const override;
 
-	/*virtual*/ const std::string& getSuggestion(U32 index) const;
-	/*virtual*/ U32		getSuggestionCount() const;
-	/*virtual*/ void	replaceWithSuggestion(U32 index);
+	/*virtual*/ const std::string& getSuggestion(U32 index) const override;
+	/*virtual*/ U32		getSuggestionCount() const override;
+	/*virtual*/ void	replaceWithSuggestion(U32 index) override;
 
-	/*virtual*/ void	addToDictionary();
-	/*virtual*/ bool	canAddToDictionary() const;
+	/*virtual*/ void	addToDictionary() override;
+	/*virtual*/ bool	canAddToDictionary() const override;
 
-	/*virtual*/ void	addToIgnore();
-	/*virtual*/ bool	canAddToIgnore() const;
+	/*virtual*/ void	addToIgnore() override;
+	/*virtual*/ bool	canAddToIgnore() const override;
 
 	// Spell checking helper functions
 	std::string			getMisspelledWord(U32 pos) const;
@@ -173,27 +175,28 @@ class LLLineEditor
 	void				onSpellCheckSettingsChange();
 
 	// view overrides
-	virtual void	draw();
-	virtual void	reshape(S32 width,S32 height,BOOL called_from_parent=TRUE);
-	virtual void	onFocusReceived();
-	virtual void	onFocusLost();
-	virtual void	setEnabled(BOOL enabled);
+	/*virtual*/ const std::string getToolTip() const override;
+	/*virtual*/ void	draw() override;
+	/*virtual*/ void	reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override;
+	/*virtual*/ void	onFocusReceived() override;
+	/*virtual*/ void	onFocusLost() override;
+	/*virtual*/ void	setEnabled(BOOL enabled) override;
 
 	// UI control overrides
-	virtual void	clear();
-	virtual void	onTabInto();
-	virtual void	setFocus( BOOL b );
-	virtual void 	setRect(const LLRect& rect);
-	virtual BOOL	acceptsTextInput() const;
-	virtual void	onCommit();
-	virtual BOOL	isDirty() const;	// Returns TRUE if user changed value at all
-	virtual void	resetDirty();		// Clear dirty state
+	/*virtual*/ void	clear() override;
+	/*virtual*/ void	onTabInto() override;
+	/*virtual*/ void	setFocus(BOOL b) override;
+	/*virtual*/ void 	setRect(const LLRect& rect) override;
+	/*virtual*/ BOOL	acceptsTextInput() const override;
+	/*virtual*/ void	onCommit() override;
+	/*virtual*/ BOOL	isDirty() const override;	// Returns TRUE if user changed value at all
+	/*virtual*/ void	resetDirty() override;		// Clear dirty state
 
 	// assumes UTF8 text
-	virtual void	setValue(const LLSD& value );
-	virtual LLSD	getValue() const;
-	virtual BOOL	setTextArg( const std::string& key, const LLStringExplicit& text );
-	virtual BOOL	setLabelArg( const std::string& key, const LLStringExplicit& text );
+	/*virtual*/ void	setValue(const LLSD& value) override;
+	/*virtual*/ LLSD	getValue() const override;
+	/*virtual*/ BOOL	setTextArg(const std::string& key, const LLStringExplicit& text) override;
+	/*virtual*/ BOOL	setLabelArg(const std::string& key, const LLStringExplicit& text) override;
 
 	void			setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }
 	const std::string& 	getLabel()	{ return mLabel.getString(); }
@@ -215,7 +218,7 @@ class LLLineEditor
 
 	// Selects characters 'start' to 'end'.
 	void			setSelection(S32 start, S32 end);
-	virtual void	getSelectionRange(S32 *position, S32 *length) const;
+	/*virtual*/ void	getSelectionRange(S32 *position, S32 *length) const override;
 	
 	void			setCommitOnFocusLost( BOOL b )	{ mCommitOnFocusLost = b; }
 	void			setRevertOnEsc( BOOL b )		{ mRevertOnEsc = b; }
@@ -314,14 +317,14 @@ class LLLineEditor
 	void			updateAllowingLanguageInput();
 	BOOL			hasPreeditString() const;
 	// Implementation (overrides) of LLPreeditor
-	virtual void	resetPreedit();
-	virtual void	updatePreedit(const LLWString &preedit_string,
-						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position);
-	virtual void	markAsPreedit(S32 position, S32 length);
-	virtual void	getPreeditRange(S32 *position, S32 *length) const;
-	virtual BOOL	getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
-	virtual S32		getPreeditFontSize() const;
-	virtual LLWString getPreeditString() const { return getWText(); }
+	/*virtual*/ void	resetPreedit() override;
+	/*virtual*/ void	updatePreedit(const LLWString &preedit_string,
+						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) override;
+	/*virtual*/ void	markAsPreedit(S32 position, S32 length) override;
+	/*virtual*/ void	getPreeditRange(S32 *position, S32 *length) const override;
+	/*virtual*/ BOOL	getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const override;
+	/*virtual*/ S32		getPreeditFontSize() const override;
+	/*virtual*/ LLWString getPreeditString() const override { return getWText(); }
 
     void			setText(const LLStringExplicit &new_text, bool use_size_limit);
 
@@ -398,6 +401,7 @@ class LLLineEditor
 	BOOL		mReadOnly;
 
 	BOOL 		mShowImageFocused;
+	BOOL 		mShowLabelFocused;
 
 	bool		mUseBgColor;
 
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index cebca70b59fdda198e72e3710386f39a94c8dc86..fececa15cfd29f1f506b9af68d0a7fcde8803375 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -573,6 +573,11 @@ void LLMenuItemGL::onVisibilityChange(BOOL new_visibility)
 //
 // This class represents a separator.
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLMenuItemSeparatorGL::Params::Params()
+    : on_visible("on_visible")
+{
+}
+
 LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
 	LLMenuItemGL( p )
 {
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 87e3f18ebc3018dcb64aa82ecc1a09325e8af29c..5c51cf8465b2cac43d5b239573fb390da7f45948 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -235,10 +235,10 @@ class LLMenuItemSeparatorGL : public LLMenuItemGL
 	struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
 	{
         Optional<EnableCallbackParam > on_visible;
-        Params() : on_visible("on_visible")
-        {}
+        Params();
 	};
-	LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params());
+
+    LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params());
 
 	/*virtual*/ void draw( void );
 	/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index d736aa6634879ca33fc27023000f539c59db6a8f..2fe9ee18e3077b681ea060a3fa57aaa39479c8f1 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -1553,6 +1553,7 @@ bool LLNotifications::loadTemplates()
 
 	if (!success || root.isNull() || !root->hasName( "notifications" ))
 	{
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
 		LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
 		return false;
 	}
@@ -1563,6 +1564,7 @@ bool LLNotifications::loadTemplates()
 
 	if(!params.validateBlock())
 	{
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
 		LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
 		return false;
 	}
@@ -1629,6 +1631,7 @@ bool LLNotifications::loadVisibilityRules()
 
 	if(!params.validateBlock())
 	{
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
 		LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
 		return false;
 	}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 921398a69325f8f942795d256751df762540aad2..4d9a33f1d7ad7bce5b7613f1e401da97dc90b083 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -913,7 +913,7 @@ class LLNotifications :
 	/* virtual */ LLNotificationPtr add(const std::string& name, 
 						const LLSD& substitutions, 
 						const LLSD& payload, 
-						LLNotificationFunctorRegistry::ResponseFunctor functor);
+						LLNotificationFunctorRegistry::ResponseFunctor functor) override;
 	LLNotificationPtr add(const LLNotification::Params& p);
 
 	void add(const LLNotificationPtr pNotif);
@@ -964,8 +964,8 @@ class LLNotifications :
 	bool isVisibleByRules(LLNotificationPtr pNotification);
 	
 private:
-	/*virtual*/ void initSingleton();
-	/*virtual*/ void cleanupSingleton();
+	/*virtual*/ void initSingleton() override;
+	/*virtual*/ void cleanupSingleton() override;
 	
 	void loadPersistentNotifications();
 
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index 735e2d529ecf081140a417631ce2fed9dda8c2a9..b2be9fb1e179dc6d54dc56169d11ff38c1314000 100644
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -475,13 +475,15 @@ void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent)
 	{
 		up_button->reshape(up_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));
 		down_button->reshape(down_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));
-		up_button->setOrigin(up_button->getRect().mLeft, getRect().getHeight() - up_button->getRect().getHeight());
+		up_button->setOrigin(0, getRect().getHeight() - up_button->getRect().getHeight());
+		down_button->setOrigin(0, 0);
 	}
 	else
 	{
 		up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight());
 		down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight());
-		down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), down_button->getRect().mBottom);
+		up_button->setOrigin(0, 0);
+		down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), 0);
 	}
 	updateThumbRect();
 }
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index ad32f7186c3a2481694342d4bf5841bdce6cfb53..22d27b1f2ad4ff209a15eebc7b24b42309db65f2 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -70,6 +70,7 @@ LLScrollContainer::Params::Params()
 	bg_color("color"),
 	border_visible("border_visible"),
 	hide_scrollbar("hide_scrollbar"),
+	ignore_arrow_keys("ignore_arrow_keys"),
 	min_auto_scroll_rate("min_auto_scroll_rate", 100),
 	max_auto_scroll_rate("max_auto_scroll_rate", 1000),
 	max_auto_scroll_zone("max_auto_scroll_zone", 16),
@@ -86,6 +87,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
 	mBackgroundColor(p.bg_color()),
 	mIsOpaque(p.is_opaque),
 	mHideScrollbar(p.hide_scrollbar),
+	mIgnoreArrowKeys(p.ignore_arrow_keys),
 	mReserveScrollCorner(p.reserve_scroll_corner),
 	mMinAutoScrollRate(p.min_auto_scroll_rate),
 	mMaxAutoScrollRate(p.max_auto_scroll_rate),
@@ -204,10 +206,29 @@ void LLScrollContainer::reshape(S32 width, S32 height,
 	}
 }
 
+// virtual
 BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
 {
+    if (mIgnoreArrowKeys)
+    {
+        switch(key)
+        {
+        case KEY_LEFT:
+        case KEY_RIGHT:
+        case KEY_UP:
+        case KEY_DOWN:
+        case KEY_PAGE_UP:
+        case KEY_PAGE_DOWN:
+        case KEY_HOME:
+        case KEY_END:
+            return FALSE;
+        default:
+            break;
+        }
+    }
+
 	// allow scrolled view to handle keystrokes in case it delegated keyboard focus
-	// to the scroll container.  
+	// to the scroll container.
 	// NOTE: this should not recurse indefinitely as handleKeyHere
 	// should not propagate to parent controls, so mScrolledView should *not*
 	// call LLScrollContainer::handleKeyHere in turn
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index dacea2a987c51e0a3b618d331314cc31b2f08ad1..79dc70cac9e1a93423deb1e33f16d15f982dbb50 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -63,7 +63,8 @@ class LLScrollContainer : public LLUICtrl
 		Optional<bool>		is_opaque,
 							reserve_scroll_corner,
 							border_visible,
-							hide_scrollbar;
+							hide_scrollbar,
+							ignore_arrow_keys;
 		Optional<F32>		min_auto_scroll_rate,
 							max_auto_scroll_rate;
 		Optional<U32>		max_auto_scroll_zone;
@@ -149,6 +150,7 @@ class LLScrollContainer : public LLUICtrl
 	F32			mMaxAutoScrollRate;
 	U32			mMaxAutoScrollZone;
 	bool		mHideScrollbar;
+	bool		mIgnoreArrowKeys;
 };
 
 
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp
index b6f2eb8ba2ff48e243a6274c0666b65b6adce299..3a819e7d06ad2ac7975c342ffdcfe733b97a5802 100644
--- a/indra/llui/llscrollingpanellist.cpp
+++ b/indra/llui/llscrollingpanellist.cpp
@@ -37,53 +37,44 @@ static LLDefaultChildRegistry::Register<LLScrollingPanelList> r("scrolling_panel
 
 // This could probably be integrated with LLScrollContainer -SJB
 
+LLScrollingPanelList::Params::Params()
+	: is_horizontal("is_horizontal")
+	, padding("padding")
+	, spacing("spacing")
+{
+}
+
+LLScrollingPanelList::LLScrollingPanelList(const Params& p)
+	: LLUICtrl(p)
+	, mIsHorizontal(p.is_horizontal)
+	, mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING)
+	, mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING)
+{
+}
+
 void LLScrollingPanelList::clearPanels()
 {
 	deleteAllChildren();
 	mPanelList.clear();
-
-	LLRect rc = getRect();
-	rc.setLeftTopAndSize(rc.mLeft, rc.mTop, 1, 1);
-	setRect(rc);
-
-	notifySizeChanged(rc.getHeight());
+	rearrange();
 }
 
-S32 LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
+S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back)
 {
-	addChildInBack( panel );
-	mPanelList.push_front( panel );
-
-	// Resize this view
-	S32 total_height = 0;
-	S32 max_width = 0;
-	S32 cur_gap = 0;
-	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
-		 iter != mPanelList.end(); ++iter)
+	if (back)
 	{
-		LLScrollingPanel *childp = *iter;
-		total_height += childp->getRect().getHeight() + cur_gap;
-		max_width = llmax( max_width, childp->getRect().getWidth() );
-		cur_gap = GAP_BETWEEN_PANELS;
+		addChild(panel);
+		mPanelList.push_back(panel);
 	}
- 	LLRect rc = getRect();
- 	rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height);
- 	setRect(rc);
-
-	notifySizeChanged(rc.getHeight());
-
-	// Reposition each of the child views
-	S32 cur_y = total_height;
-	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
-		 iter != mPanelList.end(); ++iter)
+	else
 	{
-		LLScrollingPanel *childp = *iter;
-		cur_y -= childp->getRect().getHeight();
-		childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom);
-		cur_y -= GAP_BETWEEN_PANELS;
+		addChildInBack(panel);
+		mPanelList.push_front(panel);
 	}
 
-	return total_height;
+	rearrange();
+
+	return mIsHorizontal ? getRect().getWidth() : getRect().getHeight();
 }
 
 void LLScrollingPanelList::removePanel(LLScrollingPanel* panel) 
@@ -100,7 +91,7 @@ void LLScrollingPanelList::removePanel(LLScrollingPanel* panel)
 				break;
 			}
 		}
-		if(iter != mPanelList.end())
+		if (iter != mPanelList.end())
 		{
 			removePanel(index);
 		}
@@ -120,62 +111,104 @@ void LLScrollingPanelList::removePanel( U32 panel_index )
 		mPanelList.erase( mPanelList.begin() + panel_index );
 	}
 
-	const S32 GAP_BETWEEN_PANELS = 6;
+	rearrange();
+}
 
-	// Resize this view
-	S32 total_height = 0;
-	S32 max_width = 0;
-	S32 cur_gap = 0;
-	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+void LLScrollingPanelList::updatePanels(BOOL allow_modify)
+{
+    for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
 		 iter != mPanelList.end(); ++iter)
-	{
+    {
 		LLScrollingPanel *childp = *iter;
-		total_height += childp->getRect().getHeight() + cur_gap;
-		max_width = llmax( max_width, childp->getRect().getWidth() );
-		cur_gap = GAP_BETWEEN_PANELS;
+		childp->updatePanel(allow_modify);
+    }
+}
+
+void LLScrollingPanelList::rearrange()
+{
+	// Resize this view
+	S32 new_width, new_height;
+	if (!mPanelList.empty())
+	{
+		new_width = new_height = mPadding * 2;
+		for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+			iter != mPanelList.end(); ++iter)
+		{
+			LLScrollingPanel* childp = *iter;
+			const LLRect& rect = childp->getRect();
+			if (mIsHorizontal)
+			{
+				new_width += rect.getWidth() + mSpacing;
+				new_height = llmax(new_height, rect.getHeight());
+			}
+			else
+			{
+				new_height += rect.getHeight() + mSpacing;
+				new_width = llmax(new_width, rect.getWidth());
+			}
+		}
+
+		if (mIsHorizontal)
+		{
+			new_width -= mSpacing;
+		}
+		else
+		{
+			new_height -= mSpacing;
+		}
 	}
+	else
+	{
+		new_width = new_height = 1;
+	}
+
 	LLRect rc = getRect();
-	rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height);
-	setRect(rc);
+	if (mIsHorizontal || !followsRight())
+	{
+		rc.mRight = rc.mLeft + new_width;
+	}
+	if (!mIsHorizontal || !followsBottom())
+	{
+		rc.mBottom = rc.mTop - new_height;
+	}
 
-	notifySizeChanged(rc.getHeight());
+	if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom)
+	{
+		setRect(rc);
+		notifySizeChanged();
+	}
 
 	// Reposition each of the child views
-	S32 cur_y = total_height;
+	S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding;
 	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
-		 iter != mPanelList.end(); ++iter)
+		iter != mPanelList.end(); ++iter)
 	{
-		LLScrollingPanel *childp = *iter;
-		cur_y -= childp->getRect().getHeight();
-		childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom);
-		cur_y -= GAP_BETWEEN_PANELS;
+		LLScrollingPanel* childp = *iter;
+		const LLRect& rect = childp->getRect();
+		if (mIsHorizontal)
+		{
+			childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop);
+			pos += rect.getWidth() + mSpacing;
+		}
+		else
+		{
+			childp->translate(mPadding - rect.mLeft, pos - rect.mTop);
+			pos -= rect.getHeight() + mSpacing;
+		}
 	}
 }
 
-void LLScrollingPanelList::updatePanels(BOOL allow_modify)
-{
-    for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
-		 iter != mPanelList.end(); ++iter)
-    {
-		LLScrollingPanel *childp = *iter;
-		childp->updatePanel(allow_modify);
-    }
-}
-
 void LLScrollingPanelList::updatePanelVisiblilty()
 {
 	// Determine visibility of children.
-	S32 BORDER_WIDTH = 2;  // HACK
 
-	LLRect parent_local_rect = getParent()->getRect();
-	parent_local_rect.stretch( -BORDER_WIDTH );
-	
 	LLRect parent_screen_rect;
-	getParent()->localPointToScreen( 
-		BORDER_WIDTH, 0, 
+	getParent()->localPointToScreen(
+		mPadding, mPadding,
 		&parent_screen_rect.mLeft, &parent_screen_rect.mBottom );
-	getParent()->localPointToScreen( 
-		parent_local_rect.getWidth() - BORDER_WIDTH, parent_local_rect.getHeight() - BORDER_WIDTH,
+	getParent()->localPointToScreen(
+		getParent()->getRect().getWidth() - mPadding,
+		getParent()->getRect().getHeight() - mPadding,
 		&parent_screen_rect.mRight, &parent_screen_rect.mTop );
 
 	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
@@ -207,11 +240,12 @@ void LLScrollingPanelList::draw()
 	LLUICtrl::draw();
 }
 
-void LLScrollingPanelList::notifySizeChanged(S32 height)
+void LLScrollingPanelList::notifySizeChanged()
 {
 	LLSD info;
 	info["action"] = "size_changes";
-	info["height"] = height;
+	info["height"] = getRect().getHeight();
+	info["width"] = getRect().getWidth();
 	notifyParent(info);
 }
 
diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h
index e8df176ec380595668057e6981ce0ceca832945d..d62503942735a87e2fb8a324cfe4c973516bf43c 100644
--- a/indra/llui/llscrollingpanellist.h
+++ b/indra/llui/llscrollingpanellist.h
@@ -45,18 +45,24 @@ class LLScrollingPanel : public LLPanel
 
 
 /*
- * A set of panels that are displayed in a vertical sequence inside a scroll container.
+ * A set of panels that are displayed in a sequence inside a scroll container.
  */
 class LLScrollingPanelList : public LLUICtrl
 {
 public:
 	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
-	{};
-	LLScrollingPanelList(const Params& p)
-	:	LLUICtrl(p) 
-	{}
+	{
+		Optional<bool> is_horizontal;
+		Optional<S32> padding;
+		Optional<S32> spacing;
+
+		Params();
+	};
+
+	LLScrollingPanelList(const Params& p);
 	
-	static const S32 GAP_BETWEEN_PANELS = 6;
+	static const S32 DEFAULT_SPACING = 6;
+	static const S32 DEFAULT_PADDING = 2;
 
 	typedef std::deque<LLScrollingPanel*>	panel_list_t;
 
@@ -65,11 +71,18 @@ class LLScrollingPanelList : public LLUICtrl
 	virtual void		draw();
 
 	void				clearPanels();
-	S32					addPanel( LLScrollingPanel* panel );
-	void				removePanel( LLScrollingPanel* panel );
-	void				removePanel( U32 panel_index );
+	S32					addPanel(LLScrollingPanel* panel, bool back = false);
+	void				removePanel(LLScrollingPanel* panel);
+	void				removePanel(U32 panel_index);
 	void				updatePanels(BOOL allow_modify);
-	const panel_list_t&	getPanelList() { return mPanelList; }
+	void				rearrange();
+
+	const panel_list_t&	getPanelList() const { return mPanelList; }
+	bool				getIsHorizontal() const { return mIsHorizontal; }
+	void				setPadding(S32 padding) { mPadding = padding; rearrange(); }
+	void				setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); }
+	S32					getPadding() const { return mPadding; }
+	S32					getSpacing() const { return mSpacing; }
 
 private:
 	void				updatePanelVisiblilty();
@@ -77,7 +90,11 @@ class LLScrollingPanelList : public LLUICtrl
 	/**
 	 * Notify parent about size change, makes sense when used inside accordion
 	 */
-	void				notifySizeChanged(S32 height);
+	void				notifySizeChanged();
+
+	bool				mIsHorizontal;
+	S32					mPadding;
+	S32					mSpacing;
 
 	panel_list_t		mPanelList;
 };
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 219667f7667741e36ad6fcb79cab15295d0aa9a9..f982dc99e894a68269d0b0711bc3843c402f8685 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -411,7 +411,7 @@ void LLScrollListCtrl::clearRows()
 LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
 {
 	item_list::const_iterator iter;
-	for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+	for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
 	{
 		LLScrollListItem* item  = *iter;
 		if (item->getSelected())
@@ -1269,7 +1269,7 @@ BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sen
 	LLScrollListItem* item = getItemByLabel(label, case_sensitive, column);
 
 	bool found = NULL != item;
-	if(found)
+	if (found)
 	{
 		selectItem(item, -1);
 	}
@@ -2747,7 +2747,7 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending)
 S32	LLScrollListCtrl::getLinesPerPage()
 {
 	//if mPageLines is NOT provided display all item
-	if(mPageLines)
+	if (mPageLines)
 	{
 		return mPageLines;
 	}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 73b4fb036ab717df66a4c624313e55e331535bfb..326589a3295f10b02148684affd4b2f4548cb280 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -253,7 +253,7 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	S32				getItemIndex( LLScrollListItem* item ) const;
 	S32				getItemIndex( const LLUUID& item_id ) const;
 
-	void setCommentText( const std::string& comment_text);
+	void			setCommentText( const std::string& comment_text);
 	LLScrollListItem* addSeparator(EAddPosition pos);
 
 	// "Simple" interface: use this when you're creating a list that contains only unique strings, only
@@ -263,7 +263,7 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	BOOL			selectItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 );		// FALSE if item not found
 	BOOL			selectItemByPrefix(const std::string& target, BOOL case_sensitive = TRUE, S32 column = -1);
 	BOOL			selectItemByPrefix(const LLWString& target, BOOL case_sensitive = TRUE, S32 column = -1);
-	LLScrollListItem*  getItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 );
+	LLScrollListItem*	getItemByLabel(const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0);
 	const std::string	getSelectedItemLabel(S32 column = 0) const;
 	LLSD			getSelectedValue();
 
@@ -322,7 +322,7 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 
 	virtual S32		getScrollPos() const;
 	virtual void	setScrollPos( S32 pos );
-	S32 getSearchColumn();
+	S32				getSearchColumn();
 	void			setSearchColumn(S32 column) { mSearchColumn = column; }
 	S32				getColumnIndexFromOffset(S32 x);
 	S32				getColumnOffsetFromIndex(S32 index);
@@ -371,13 +371,13 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	// Used "internally" by the scroll bar.
 	void			onScrollChange( S32 new_pos, LLScrollbar* src );
 
-	static void onClickColumn(void *userdata);
+	static void		onClickColumn(void *userdata);
 
-	virtual void updateColumns(bool force_update = false);
-	S32 calcMaxContentWidth();
-	bool updateColumnWidths();
+	virtual void	updateColumns(bool force_update = false);
+	S32				calcMaxContentWidth();
+	bool			updateColumnWidths();
 
-	void setHeadingHeight(S32 heading_height);
+	void			setHeadingHeight(S32 heading_height);
 	/**
 	 * Sets  max visible  lines without scroolbar, if this value equals to 0,
 	 * then display all items.
@@ -398,18 +398,20 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	virtual void	deselect();
 	virtual BOOL	canDeselect() const;
 
-	void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; }
-	void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
-	S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
+	void			setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; }
+	void			updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
+	S32				getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
 
 	std::string     getSortColumnName();
 	BOOL			getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
 	BOOL			hasSortOrder() const;
 	void			clearSortOrder();
 
-	void			setAlternateSort() { mAlternateSort = true; }
+	void			setAlternateSort() { mAlternateSort = TRUE; }
 
-	S32		selectMultiple( uuid_vec_t ids );
+	void			selectPrevItem(BOOL extend_selection = FALSE);
+	void			selectNextItem(BOOL extend_selection = FALSE);
+	S32				selectMultiple(uuid_vec_t ids);
 	// conceptually const, but mutates mItemList
 	void			updateSort() const;
 	// sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
@@ -454,8 +456,6 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	void			updateLineHeight();
 
 private:
-	void			selectPrevItem(BOOL extend_selection);
-	void			selectNextItem(BOOL extend_selection);
 	void			drawItems();
 	
 	void            updateLineHeightInsert(LLScrollListItem* item);
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index bafeef41fb512172c0e5221774104589722d7d71..cfaf08ec0a26704df4db9bae7e86c38af25d92a1 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -178,6 +178,10 @@ void LLSearchEditor::setFocus( BOOL b )
 void LLSearchEditor::onClearButtonClick(const LLSD& data)
 {
 	setText(LLStringUtil::null);
+	if (mTextChangedCallback)
+	{
+		mTextChangedCallback(this, getValue());
+	}
 	mSearchEditor->onCommit(); // force keystroke callback
 }
 
diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h
index 3da5e30955795bc5f11e7904b5226f504d0bf9c3..14f9b44fe45b3c7bc8330dfd9cd3cb6d6dcb7187 100644
--- a/indra/llui/llspellcheck.h
+++ b/indra/llui/llspellcheck.h
@@ -47,7 +47,7 @@ class LLSpellChecker : public LLSingleton<LLSpellChecker>
 protected:
 	void addToDictFile(const std::string& dict_path, const std::string& word);
 	void initHunspell(const std::string& dict_language);
-	void initSingleton();
+	void initSingleton() override;
 
 public:
 	typedef std::list<std::string> dict_list_t;
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index e0697cb454a6d852b8ed18352120cddc0cf038a0..7ccf025a191f8c54d097b5939f4bd7ef33c7a506 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -29,6 +29,8 @@
 
 #include "lltextbase.h"
 
+#include "llemojidictionary.h"
+#include "llemojihelper.h"
 #include "lllocalcliprect.h"
 #include "llmenugl.h"
 #include "llscrollcontainer.h"
@@ -161,10 +163,12 @@ LLTextBase::Params::Params()
 	line_spacing("line_spacing"),
 	max_text_length("max_length", 255),
 	font_shadow("font_shadow"),
+	text_valign("text_valign"),
 	wrap("wrap"),
 	trusted_content("trusted_content", true),
 	always_show_icons("always_show_icons", false),
 	use_ellipses("use_ellipses", false),
+	use_color("use_color", true),
 	parse_urls("parse_urls", false),
 	force_urls_external("force_urls_external", false),
 	parse_highlights("parse_highlights", false)
@@ -208,6 +212,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
 	mVPad(p.v_pad),
 	mHAlign(p.font_halign),
 	mVAlign(p.font_valign),
+	mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()),
 	mLineSpacingMult(p.line_spacing.multiple),
 	mLineSpacingPixels(p.line_spacing.pixels),
 	mClip(p.clip),
@@ -222,6 +227,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
 	mPlainText ( p.plain_text ),
 	mWordWrap(p.wrap),
 	mUseEllipses( p.use_ellipses ),
+	mUseColor(p.use_color),
 	mParseHTML(p.parse_urls),
 	mForceUrlsExternal(p.force_urls_external),
 	mParseHighlights(p.parse_highlights),
@@ -582,7 +588,7 @@ void LLTextBase::drawCursor()
 				fontp = segmentp->getStyle()->getFont();
 				fontp->render(text, mCursorPos, cursor_rect, 
 					LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha),
-					LLFontGL::LEFT, mVAlign,
+					LLFontGL::LEFT, mTextVAlign,
 					LLFontGL::NORMAL,
 					LLFontGL::NO_SHADOW,
 					1);
@@ -896,6 +902,28 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
 		}
 	}
 
+	// Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us)
+	{
+		LLStyleSP emoji_style;
+		LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
+		for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++)
+		{
+			llwchar code = wstr[text_kitty];
+			bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code);
+			if (isEmoji)
+			{
+				if (!emoji_style)
+				{
+					emoji_style = new LLStyle(getStyleParams());
+					emoji_style->setFont(LLFontGL::getFontEmoji());
+				}
+
+				S32 new_seg_start = pos + text_kitty;
+				insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this));
+			}
+		}
+	}
+
 	getViewModel()->getEditableDisplay().insert(pos, wstr);
 
 	if ( truncate() )
@@ -1079,6 +1107,7 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
 	needsReflow(reflow_start_index);
 }
 
+//virtual 
 BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	// handle triple click
@@ -1133,6 +1162,7 @@ BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleMouseDown(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1152,6 +1182,7 @@ BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleMouseUp(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1163,6 +1194,7 @@ BOOL LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleMiddleMouseDown(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1174,6 +1206,7 @@ BOOL LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleMiddleMouseUp(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1185,6 +1218,7 @@ BOOL LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleRightMouseDown(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1196,6 +1230,7 @@ BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleRightMouseUp(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)
 {
 	//Don't start triple click timer if user have clicked on scrollbar
@@ -1215,6 +1250,7 @@ BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleDoubleClick(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleHover(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1226,6 +1262,7 @@ BOOL LLTextBase::handleHover(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleHover(x, y, mask);
 }
 
+//virtual 
 BOOL LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1237,6 +1274,7 @@ BOOL LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)
 	return LLUICtrl::handleScrollWheel(x, y, clicks);
 }
 
+//virtual 
 BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
 {
 	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
@@ -1248,7 +1286,20 @@ BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
 	return LLUICtrl::handleToolTip(x, y, mask);
 }
 
+//virtual 
+const std::string LLTextBase::getToolTip() const
+{
+    if (sDebugUnicode)
+    {
+        std::string text = getText();
+        std::string tooltip = utf8str_showBytesUTF8(text);
+        return tooltip;
+    }
+
+    return LLUICtrl::getToolTip();
+}
 
+//virtual 
 void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
 {
 	if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape)
@@ -1275,6 +1326,7 @@ void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
 	}
 }
 
+//virtual 
 void LLTextBase::draw()
 {
 	// reflow if needed, on demand
@@ -1985,21 +2037,8 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getEditableSegIterContaini
 
 LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)
 {
-
 	static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
 
-	S32 text_len = 0;
-	if (!useLabel())
-	{
-		text_len = getLength();
-	}
-	else
-	{
-		text_len = mLabel.getWString().length();
-	}
-
-	if (index > text_len) { return mSegments.end(); }
-
 	// when there are no segments, we return the end iterator, which must be checked by caller
 	if (mSegments.size() <= 1) { return mSegments.begin(); }
 
@@ -2013,18 +2052,6 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i
 {
 	static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
 
-	S32 text_len = 0;
-	if (!useLabel())
-	{
-		text_len = getLength();
-	}
-	else
-	{
-		text_len = mLabel.getWString().length();
-	}
-
-	if (index > text_len) { return mSegments.end(); }
-
 	// when there are no segments, we return the end iterator, which must be checked by caller
 	if (mSegments.size() <= 1) { return mSegments.begin(); }
 
@@ -2188,8 +2215,8 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
 		S32 start=0,end=0;
 		LLUrlMatch match;
 		std::string text = new_text;
-		while ( LLUrlRegistry::instance().findUrl(text, match,
-				boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted() || mAlwaysShowIcons))
+		while (LLUrlRegistry::instance().findUrl(text, match,
+				boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3), isContentTrusted() || mAlwaysShowIcons))
 		{
 			start = match.getStart();
 			end = match.getEnd()+1;
@@ -2437,18 +2464,18 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig
 			LLStyle::Params normal_style_params(style_params);
 			normal_style_params.font.style("NORMAL");
 			LLStyleConstSP normal_sp(new LLStyle(normal_style_params));
-			segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this ));
+			segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this));
 		}
 		else
 		{
-		segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this ));
+			segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this));
 		}
 
 		insertStringNoUndo(getLength(), wide_text, &segments);
 	}
 
 	// Set the cursor and scroll position
-	if( selection_start != selection_end )
+	if (selection_start != selection_end)
 	{
 		mSelectionStart = selection_start;
 		mSelectionEnd = selection_end;
@@ -2456,7 +2483,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig
 		mIsSelecting = was_selecting;
 		setCursorPos(cursor_pos);
 	}
-	else if( cursor_was_at_end )
+	else if (cursor_was_at_end)
 	{
 		setCursorPos(getLength());
 	}
@@ -2468,25 +2495,28 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig
 
 void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
 {
-	if (new_text.empty()) return; 
+	if (new_text.empty())
+	{
+		return; 
+	}
 
 	std::string::size_type start = 0;
-	std::string::size_type pos = new_text.find("\n",start);
+	std::string::size_type pos = new_text.find("\n", start);
 	
-	while(pos!=-1)
+	while (pos != std::string::npos)
 	{
-		if(pos!=start)
+		if (pos != start)
 		{
 			std::string str = std::string(new_text,start,pos-start);
-			appendAndHighlightTextImpl(str,highlight_part, style_params, underline_on_hover_only);
+			appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);
 		}
 		appendLineBreakSegment(style_params);
 		start = pos+1;
-		pos = new_text.find("\n",start);
+		pos = new_text.find("\n", start);
 	}
 
-	std::string str = std::string(new_text,start,new_text.length()-start);
-	appendAndHighlightTextImpl(str,highlight_part, style_params, underline_on_hover_only);
+	std::string str = std::string(new_text, start, new_text.length() - start);
+	appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);
 }
 
 
@@ -3318,12 +3348,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
 		font->render(text, start, 
 				 rect, 
 				 color, 
-				 LLFontGL::LEFT, mEditor.mVAlign, 
+				 LLFontGL::LEFT, mEditor.mTextVAlign,
 				 LLFontGL::NORMAL, 
 				 mStyle->getShadowType(), 
 				 length,
 				 &right_x, 
-				 mEditor.getUseEllipses());
+				 mEditor.getUseEllipses(),
+				 mEditor.getUseColor());
 	}
 	rect.mLeft = right_x;
 	
@@ -3337,12 +3368,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
 		font->render(text, start, 
 				 rect,
 				 mStyle->getSelectedColor().get(),
-				 LLFontGL::LEFT, mEditor.mVAlign, 
+				 LLFontGL::LEFT, mEditor.mTextVAlign,
 				 LLFontGL::NORMAL, 
 				 LLFontGL::NO_SHADOW, 
 				 length,
 				 &right_x, 
-				 mEditor.getUseEllipses());
+				 mEditor.getUseEllipses(),
+				 mEditor.getUseColor());
 	}
 	rect.mLeft = right_x;
 	if( selection_end < seg_end )
@@ -3354,12 +3386,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
 		font->render(text, start, 
 				 rect, 
 				 color, 
-				 LLFontGL::LEFT, mEditor.mVAlign, 
+				 LLFontGL::LEFT, mEditor.mTextVAlign,
 				 LLFontGL::NORMAL, 
 				 mStyle->getShadowType(), 
 				 length,
 				 &right_x, 
-				 mEditor.getUseEllipses());
+				 mEditor.getUseEllipses(),
+				 mEditor.getUseColor());
 	}
     return right_x;
 }
@@ -3590,6 +3623,33 @@ const S32 LLLabelTextSegment::getLength() const
 	return mEditor.getWlabel().length();
 }
 
+//
+// LLEmojiTextSegment
+//
+LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor)
+	: LLNormalTextSegment(style, start, end, editor)
+{
+}
+
+LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible)
+	: LLNormalTextSegment(color, start, end, editor, is_visible)
+{
+}
+
+BOOL LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
+{
+	if (mTooltip.empty())
+	{
+		LLWString emoji = getWText().substr(getStart(), getEnd() - getStart());
+		if (!emoji.empty())
+		{
+			mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]);
+		}
+	}
+
+	return LLNormalTextSegment::handleToolTip(x, y, mask);
+}
+
 //
 // LLOnHoverChangeableTextSegment
 //
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 3611ab04991ed9873bd766c192cfbb2c5db29fda..9d3c54fbeebeecf7fbb74be9c6dba2251cd5d45f 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -178,6 +178,18 @@ class LLLabelTextSegment : public LLNormalTextSegment
 	/*virtual*/	const S32			getLength()	const;
 };
 
+// Text segment that represents a single emoji character that has a different style (=font size) than the rest of
+// the document it belongs to
+class LLEmojiTextSegment : public LLNormalTextSegment
+{
+public:
+	LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor);
+	LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE);
+
+	bool canEdit() const override { return false; }
+	BOOL handleToolTip(S32 x, S32 y, MASK mask) override;
+};
+
 // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)
 class LLOnHoverChangeableTextSegment : public LLNormalTextSegment
 {
@@ -273,7 +285,7 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
 /// as LLTextEditor and LLTextBox. It implements shared functionality
 /// such as Url highlighting and opening.
 ///
-class LLTextBase 
+class LLTextBase
 :	public LLUICtrl,
 	protected LLEditMenuHandler,
 	public LLSpellCheckMenuHandler,
@@ -316,6 +328,7 @@ class LLTextBase
 								plain_text,
 								wrap,
 								use_ellipses,
+								use_color,
 								parse_urls,
 								force_urls_external,
 								parse_highlights,
@@ -335,55 +348,58 @@ class LLTextBase
 
 		Optional<LLFontGL::ShadowType>	font_shadow;
 
+		Optional<LLFontGL::VAlign> text_valign;
+
 		Params();
 	};
 
 	// LLMouseHandler interface
-	/*virtual*/ BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleMiddleMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleMiddleMouseUp(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleRightMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleRightMouseUp(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleHover(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleScrollWheel(S32 x, S32 y, S32 clicks);
-	/*virtual*/ BOOL		handleToolTip(S32 x, S32 y, MASK mask);
+	/*virtual*/ BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleMiddleMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleMiddleMouseUp(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleRightMouseUp(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleHover(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleScrollWheel(S32 x, S32 y, S32 clicks) override;
+	/*virtual*/ BOOL		handleToolTip(S32 x, S32 y, MASK mask) override;
 
 	// LLView interface
-	/*virtual*/ void		reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
-	/*virtual*/ void		draw();
+	/*virtual*/ const std::string getToolTip() const override;
+	/*virtual*/ void		reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override;
+	/*virtual*/ void		draw() override;
 
 	// LLUICtrl interface
-	/*virtual*/ BOOL		acceptsTextInput() const { return !mReadOnly; }
-	/*virtual*/ void		setColor( const LLColor4& c );
+	/*virtual*/ BOOL		acceptsTextInput() const override { return !mReadOnly; }
+	/*virtual*/ void		setColor(const LLColor4& c) override;
 	virtual     void 		setReadOnlyColor(const LLColor4 &c);
-	virtual	    void		onVisibilityChange( BOOL new_visibility );
+	/*virtual*/ void		onVisibilityChange(BOOL new_visibility) override;
 
-	/*virtual*/ void		setValue(const LLSD& value );
-	/*virtual*/ LLTextViewModel* getViewModel() const;
+	/*virtual*/ void		setValue(const LLSD& value) override;
+	/*virtual*/ LLTextViewModel* getViewModel() const override;
 
 	// LLEditMenuHandler interface
-	/*virtual*/ BOOL		canDeselect() const;
-	/*virtual*/ void		deselect();
+	/*virtual*/ BOOL		canDeselect() const override;
+	/*virtual*/ void		deselect() override;
 
-	virtual void	onFocusReceived();
-	virtual void	onFocusLost();
+	virtual void	onFocusReceived() override;
+	virtual void	onFocusLost() override;
 
     void        setParseHTML(bool parse_html) { mParseHTML = parse_html; }
 
 	// LLSpellCheckMenuHandler overrides
-	/*virtual*/ bool		getSpellCheck() const;
+	/*virtual*/ bool		getSpellCheck() const override;
 
-	/*virtual*/ const std::string& getSuggestion(U32 index) const;
-	/*virtual*/ U32			getSuggestionCount() const;
-	/*virtual*/ void		replaceWithSuggestion(U32 index);
+	/*virtual*/ const std::string& getSuggestion(U32 index) const override;
+	/*virtual*/ U32			getSuggestionCount() const override;
+	/*virtual*/ void		replaceWithSuggestion(U32 index) override;
 
-	/*virtual*/ void		addToDictionary();
-	/*virtual*/ bool		canAddToDictionary() const;
+	/*virtual*/ void		addToDictionary() override;
+	/*virtual*/ bool		canAddToDictionary() const override;
 
-	/*virtual*/ void		addToIgnore();
-	/*virtual*/ bool		canAddToIgnore() const;
+	/*virtual*/ void		addToIgnore() override;
+	/*virtual*/ bool		canAddToIgnore() const override;
 
 	// Spell checking helper functions
 	std::string				getMisspelledWord(U32 pos) const;
@@ -394,6 +410,7 @@ class LLTextBase
 	// used by LLTextSegment layout code
 	bool					getWordWrap() { return mWordWrap; }
 	bool					getUseEllipses() { return mUseEllipses; }
+	bool					getUseColor() { return mUseColor; }
 	bool					truncate(); // returns true of truncation occurred
 
 	bool					isContentTrusted() {return mTrustedContent;}
@@ -416,7 +433,7 @@ class LLTextBase
 	void					appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
 
 	void					setLabel(const LLStringExplicit& label);
-	virtual BOOL			setLabelArg(const std::string& key, const LLStringExplicit& text );
+	/*virtual*/ BOOL		setLabelArg(const std::string& key, const LLStringExplicit& text) override;
 
 	const	std::string& 	getLabel()	{ return mLabel.getString(); }
 	const	LLWString&		getWlabel() { return mLabel.getWString();}
@@ -633,7 +650,8 @@ class LLTextBase
 	S32 normalizeUri(std::string& uri);
 	
 protected:
-	virtual std::string _getSearchText() const
+	// virtual
+	std::string _getSearchText() const override
 	{
 		return mLabel.getString() + getToolTip();
 	}
@@ -687,8 +705,9 @@ class LLTextBase
 	// configuration
 	S32							mHPad;				// padding on left of text
 	S32							mVPad;				// padding above text
-	LLFontGL::HAlign			mHAlign;
-	LLFontGL::VAlign			mVAlign;
+	LLFontGL::HAlign			mHAlign;			// horizontal alignment of the document in its entirety
+	LLFontGL::VAlign			mVAlign;			// vertical alignment of the document in its entirety
+	LLFontGL::VAlign			mTextVAlign;		// vertical alignment of a text segment within a single line of text
 	F32							mLineSpacingMult;	// multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding)
 	S32							mLineSpacingPixels;	// padding between lines
 	bool						mBorderVisible;
@@ -697,6 +716,7 @@ class LLTextBase
 	bool						mParseHighlights;	// highlight user-defined keywords
 	bool                		mWordWrap;
 	bool						mUseEllipses;
+	bool						mUseColor;
 	bool						mTrackEnd;			// if true, keeps scroll position at end of document during resize
 	bool						mReadOnly;
 	bool						mBGVisible;			// render background?
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 3d2a4269134facfd3fcaa70ee6cd11d0da12d4c0..a247e8700a58a587931f7af7a9bd7ddb595bd821 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -43,6 +43,7 @@
 #include "llmath.h"
 
 #include "llclipboard.h"
+#include "llemojihelper.h"
 #include "llscrollbar.h"
 #include "llstl.h"
 #include "llstring.h"
@@ -238,6 +239,7 @@ LLTextEditor::Params::Params()
 	default_color("default_color"),
     commit_on_focus_lost("commit_on_focus_lost", false),
 	show_context_menu("show_context_menu"),
+	show_emoji_helper("show_emoji_helper"),
 	enable_tooltip_paste("enable_tooltip_paste")
 {
 	addSynonym(prevalidate_callback, "text_type");
@@ -258,6 +260,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
 	mTabsToNextField(p.ignore_tab),
 	mPrevalidateFunc(p.prevalidate_callback()),
 	mShowContextMenu(p.show_context_menu),
+	mShowEmojiHelper(p.show_emoji_helper),
 	mEnableTooltipPaste(p.enable_tooltip_paste),
 	mPassDelete(FALSE),
 	mKeepSelectionOnReturn(false)
@@ -505,6 +508,16 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,
 	}
 }
 
+void LLTextEditor::setShowEmojiHelper(bool show)
+{
+	if (!mShowEmojiHelper)
+	{
+		LLEmojiHelper::instance().hideHelper(this);
+	}
+
+	mShowEmojiHelper = show;
+}
+
 BOOL LLTextEditor::selectionContainsLineBreaks()
 {
 	if (hasSelection())
@@ -668,6 +681,28 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p
 	endSelection();
 }
 
+void LLTextEditor::insertEmoji(llwchar emoji)
+{
+	LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL;
+	auto styleParams = LLStyle::Params();
+	styleParams.font = LLFontGL::getFontEmoji();
+	auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this);
+	insert(mCursorPos, LLWString(1, emoji), false, segment);
+	setCursorPos(mCursorPos + 1);
+}
+
+void LLTextEditor::handleEmojiCommit(llwchar emoji)
+{
+	S32 shortCodePos;
+	if (LLEmojiHelper::isCursorInEmojiCode(getWText(), mCursorPos, &shortCodePos))
+	{
+		remove(shortCodePos, mCursorPos - shortCodePos, true);
+		setCursorPos(shortCodePos);
+
+		insertEmoji(emoji);
+	}
+}
+
 BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	BOOL	handled = FALSE;
@@ -934,6 +969,12 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
 
 S32 LLTextEditor::execute( TextCmd* cmd )
 {
+	if (!mReadOnly && mShowEmojiHelper)
+	{
+		// Any change to our contents should always hide the helper
+		LLEmojiHelper::instance().hideHelper(this);
+	}
+
 	S32 delta = 0;
 	if( cmd->execute(this, &delta) )
 	{
@@ -983,7 +1024,7 @@ S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
 	// store text segments
 	getSegmentsInRange(segments_to_remove, pos, pos + length, false);
 	
-	if(pos <= end_pos)
+	if (pos <= end_pos)
 	{
 		removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
 	}
@@ -1007,11 +1048,12 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
 // a pseudo-tab (up to for spaces in a row)
 void LLTextEditor::removeCharOrTab()
 {
-	if( !getEnabled() )
+	if (!getEnabled())
 	{
 		return;
 	}
-	if( mCursorPos > 0 )
+
+	if (mCursorPos > 0)
 	{
 		S32 chars_to_remove = 1;
 
@@ -1023,14 +1065,14 @@ void LLTextEditor::removeCharOrTab()
 			if (offset > 0)
 			{
 				chars_to_remove = offset % SPACES_PER_TAB;
-				if( chars_to_remove == 0 )
+				if (chars_to_remove == 0)
 				{
 					chars_to_remove = SPACES_PER_TAB;
 				}
 
-				for( S32 i = 0; i < chars_to_remove; i++ )
+				for (S32 i = 0; i < chars_to_remove; i++)
 				{
-					if (text[ mCursorPos - i - 1] != ' ')
+					if (text[mCursorPos - i - 1] != ' ')
 					{
 						// Fewer than a full tab's worth of spaces, so
 						// just delete a single character.
@@ -1044,8 +1086,10 @@ void LLTextEditor::removeCharOrTab()
 		for (S32 i = 0; i < chars_to_remove; i++)
 		{
 			setCursorPos(mCursorPos - 1);
-			remove( mCursorPos, 1, FALSE );
+			remove(mCursorPos, 1, false);
 		}
+
+		tryToShowEmojiHelper();
 	}
 	else
 	{
@@ -1056,7 +1100,7 @@ void LLTextEditor::removeCharOrTab()
 // Remove a single character from the text
 S32 LLTextEditor::removeChar(S32 pos)
 {
-	return remove( pos, 1, FALSE );
+	return remove(pos, 1, false);
 }
 
 void LLTextEditor::removeChar()
@@ -1065,10 +1109,12 @@ void LLTextEditor::removeChar()
 	{
 		return;
 	}
+
 	if (mCursorPos > 0)
 	{
 		setCursorPos(mCursorPos - 1);
 		removeChar(mCursorPos);
+		tryToShowEmojiHelper();
 	}
 	else
 	{
@@ -1127,6 +1173,7 @@ void LLTextEditor::addChar(llwchar wc)
 	}
 
 	setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+	tryToShowEmojiHelper();
 
 	if (!mReadOnly && mAutoreplaceCallback != NULL)
 	{
@@ -1146,6 +1193,37 @@ void LLTextEditor::addChar(llwchar wc)
 	}
 }
 
+void LLTextEditor::showEmojiHelper()
+{
+    if (mReadOnly || !mShowEmojiHelper)
+        return;
+
+    const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos));
+    auto cb = [this](llwchar emoji) { insertEmoji(emoji); };
+    LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb);
+}
+
+void LLTextEditor::tryToShowEmojiHelper()
+{
+    if (mReadOnly || !mShowEmojiHelper)
+        return;
+
+    S32 shortCodePos;
+    LLWString wtext(getWText());
+    if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos))
+    {
+        const LLRect cursorRect(getLocalRectFromDocIndex(shortCodePos));
+        const LLWString wpart(wtext.substr(shortCodePos, mCursorPos - shortCodePos));
+        const std::string part(wstring_to_utf8str(wpart));
+        auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); };
+        LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb);
+    }
+    else
+    {
+        LLEmojiHelper::instance().hideHelper();
+    }
+}
+
 void LLTextEditor::addLineBreakChar(BOOL group_together)
 {
 	if( !getEnabled() )
@@ -1778,6 +1856,11 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
 	}
 	else 
 	{
+		if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask))
+		{
+			return TRUE;
+		}
+
 		if (mEnableTooltipPaste &&
 			LLToolTipMgr::instance().toolTipVisible() && 
 			KEY_TAB == key)
@@ -1819,6 +1902,12 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
 	{
 		resetCursorBlink();
 		needsScroll();
+
+		if (mShowEmojiHelper)
+		{
+			// Dismiss the helper whenever we handled a key that it didn't
+			LLEmojiHelper::instance().hideHelper(this);
+		}
 	}
 
 	return handled;
@@ -1837,7 +1926,12 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
 	// Handle most keys only if the text editor is writeable.
 	if( !mReadOnly )
 	{
-		if( mAutoIndent && '}' == uni_char )
+        if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE))
+        {
+            return TRUE;
+        }
+
+        if( mAutoIndent && '}' == uni_char )
 		{
 			unindentLineBeforeCloseBrace();
 		}
@@ -2600,6 +2694,7 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
 	char* text = new char[ text_len + 1];
 	if (text == NULL)
 	{
+        LLError::LLUserWarningMsg::showOutOfMemory();
 		LL_ERRS() << "Memory allocation failure." << LL_ENDL;			
 		return FALSE;
 	}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index f3939248c2acf6413ea3241c5009ea0418853359..521405ec251676e811a6c4aecccc65e84a49a4d2 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -60,6 +60,7 @@ class LLTextEditor :
 								ignore_tab,
 								commit_on_focus_lost,
 								show_context_menu,
+								show_emoji_helper,
 								enable_tooltip_paste,
 								auto_indent;
 
@@ -91,6 +92,9 @@ class LLTextEditor :
 
 	static S32		spacesPerTab();
 
+	void    insertEmoji(llwchar emoji);
+	void    handleEmojiCommit(llwchar emoji);
+
 	// mousehandler overrides
 	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
@@ -202,6 +206,10 @@ class LLTextEditor :
 	void			setShowContextMenu(bool show) { mShowContextMenu = show; }
 	bool			getShowContextMenu() const { return mShowContextMenu; }
 
+	void			showEmojiHelper();
+	void			setShowEmojiHelper(bool show);
+	bool			getShowEmojiHelper() const { return mShowEmojiHelper; }
+
 	void			setPassDelete(BOOL b) { mPassDelete = b; }
 
 protected:
@@ -248,6 +256,7 @@ class LLTextEditor :
 	S32				insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment);
 	S32				remove(S32 pos, S32 length, bool group_with_next_op);
 
+	void			tryToShowEmojiHelper();
 	void			focusLostHelper();
 	void			updateAllowingLanguageInput();
 	BOOL			hasPreeditString() const;
@@ -318,6 +327,7 @@ class LLTextEditor :
 
 	BOOL			mAllowEmbeddedItems;
 	bool			mShowContextMenu;
+	bool			mShowEmojiHelper;
 	bool			mEnableTooltipPaste;
 	bool			mPassDelete;
 	bool			mKeepSelectionOnReturn;	// disabling of removing selected text after pressing of Enter
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
index 5da722a72bb8fd1815c5ee3ddcdb4cb58926f765..6c486f29ba94ebae5d824bc89a4ae79a07a5b9f9 100644
--- a/indra/llui/lltransutil.cpp
+++ b/indra/llui/lltransutil.cpp
@@ -44,8 +44,13 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<s
 	bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS);
 	if (!success)
 	{
+        const std::string error_string =
+            "Second Life viewer couldn't access some of the files it needs and will be closed."
+            "\n\nPlease reinstall viewer from  https://secondlife.com/support/downloads/ and "
+            "contact https://support.secondlife.com if issue persists after reinstall.";
+        LLError::LLUserWarningMsg::show(error_string);
 		gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
-		LL_ERRS() << "Couldn't load string table " << xml_filename << ". Please reinstall viewer from  https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
+		LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << LL_ENDL;
 		return false;
 	}
 
@@ -60,6 +65,7 @@ bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
 	
 	if (!success)
 	{
+        LLError::LLUserWarningMsg::showMissingFiles();
 		LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL;
 		return false;
 	}
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 21afcae7c35e1b632060bcb6e7161e055ded17f3..7eb9ae69fba27ab28017793831bbe683bc703500 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -768,25 +768,20 @@ void LLUICtrl::setIsChrome(BOOL is_chrome)
 
 // virtual
 BOOL LLUICtrl::getIsChrome() const
-{ 
+{
+	if (mIsChrome)
+		return TRUE;
+
 	LLView* parent_ctrl = getParent();
-	while(parent_ctrl)
+	while (parent_ctrl)
 	{
-		if(parent_ctrl->isCtrl())
-		{
-			break;	
-		}
+		if (parent_ctrl->isCtrl())
+			return ((LLUICtrl*)parent_ctrl)->getIsChrome();
+
 		parent_ctrl = parent_ctrl->getParent();
 	}
-	
-	if(parent_ctrl)
-	{
-		return mIsChrome || ((LLUICtrl*)parent_ctrl)->getIsChrome();
-	}
-	else
-	{
-		return mIsChrome ; 
-	}
+
+	return FALSE; 
 }
 
 
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index be1c7dd0b68442d9c6fc92607ca513adba89ce27..cffb9933d439033a2cdcc5145d9483b96cd2f79d 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -264,7 +264,7 @@ class LLUICtrl
 	class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>
 	{
 		LLSINGLETON_EMPTY_CTOR(LLTextInputFilter);
-		/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const 
+		/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
 		{
 			return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->acceptsTextInput(), TRUE);
 		}
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index 07e02de6d857acbd31632a6d038d3c28e6e7a097..b1089a3903fa999f1c576428de1650daba4ac380 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -61,6 +61,7 @@ class LLUIString
 	LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {}
 	LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);
 	LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); }
+	LLUIString(const LLWString& instring) : mArgs(NULL) { insert(0, instring); }
 	~LLUIString() { delete mArgs; }
 
 	void assign(const std::string& instring);
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index da7868d80477cbe7432de8f54203d4c5bcd4430d..139eb17efaff0af712617ac9f1e76a4985c719b2 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -60,6 +60,7 @@ static const S32 LINE_HEIGHT = 15;
 
 S32		LLView::sDepth = 0;
 bool	LLView::sDebugRects = false;
+bool	LLView::sDebugUnicode = false;
 bool	LLView::sIsRectDirty = false;
 LLRect	LLView::sDirtyRect;
 bool	LLView::sDebugRectsShowNames = true;
@@ -520,7 +521,7 @@ BOOL LLView::focusNext(LLView::child_list_t & result)
 		{
 			next = result.rbegin();
 		}
-		if((*next)->isCtrl())
+		if ((*next)->isCtrl() && ((LLUICtrl*)*next)->hasTabStop())
 		{
 			LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
 			ctrl->setFocus(TRUE);
@@ -1024,7 +1025,7 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
 			handled = handleUnicodeCharHere(uni_char);
 			if (handled && LLView::sDebugKeys)
 			{
-				LL_INFOS() << "Unicode key handled by " << getName() << LL_ENDL;
+				LL_INFOS() << "Unicode key " << wchar_utf8_preview(uni_char) << " is handled by " << getName() << LL_ENDL;
 			}
 		}
 	}
@@ -1336,8 +1337,7 @@ void LLView::drawDebugRect()
 			std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),
 										debug_rect.getWidth(), debug_rect.getHeight());
 			LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
-												LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
-												S32_MAX, S32_MAX, NULL, FALSE);
+					LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
 		}
 	}
 	LLUI::popMatrix();
@@ -1753,23 +1753,26 @@ LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S3
 	const S32 KEEP_ONSCREEN_PIXELS_WIDTH = llmin(min_overlap_pixels, input.getWidth());
 	const S32 KEEP_ONSCREEN_PIXELS_HEIGHT = llmin(min_overlap_pixels, input.getHeight());
 
-	if( input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft )
-	{
-		delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH);
-	}
-	else if( input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight )
+	if (KEEP_ONSCREEN_PIXELS_WIDTH <= constraint.getWidth() &&
+		KEEP_ONSCREEN_PIXELS_HEIGHT <= constraint.getHeight())
 	{
-		delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH);
-	}
+		if (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft)
+		{
+			delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH);
+		}
+		else if (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight)
+		{
+			delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH);
+		}
 
-	if( input.mTop > constraint.mTop )
-	{
-		delta.mY = constraint.mTop - input.mTop;
-	}
-	else
-	if( input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom )
-	{
-		delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT);
+		if (input.mTop > constraint.mTop)
+		{
+			delta.mY = constraint.mTop - input.mTop;
+		}
+		else if (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom)
+		{
+			delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT);
+		}
 	}
 
 	return delta;
@@ -1780,13 +1783,19 @@ LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S3
 // (Why top and left?  That's where the drag bars are for floaters.)
 BOOL LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels)
 {
-	LLCoordGL translation = getNeededTranslation(getRect(), constraint, min_overlap_pixels);
+    return translateRectIntoRect(getRect(), constraint, min_overlap_pixels);
+}
+
+BOOL LLView::translateRectIntoRect(const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels)
+{
+	LLCoordGL translation = getNeededTranslation(rect, constraint, min_overlap_pixels);
 
 	if (translation.mX != 0 || translation.mY != 0)
 	{
 		translate(translation.mX, translation.mY);
 		return TRUE;
 	}
+
 	return FALSE;
 }
 
@@ -1966,7 +1975,7 @@ class CompareByTabOrder
 class SortByTabOrder : public LLQuerySorter, public LLSingleton<SortByTabOrder>
 {
 	LLSINGLETON_EMPTY_CTOR(SortByTabOrder);
-	/*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const 
+	/*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override
 	{
 		children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));
 	}
@@ -1990,7 +1999,7 @@ const LLViewQuery & LLView::getTabOrderQuery()
 class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const 
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
 	{
 		return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
 	}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 7360fd0fb939b50d1b64facee4fd2b5907ada1e5..6e16d41cba39af8cfcfbe39385a0fc7f89fd3e6e 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -111,7 +111,7 @@ class LLView
 		Alternative<std::string>	string;
 		Alternative<U32>			flags;
 
-        Follows();
+		Follows();
 	};
 
 	struct Params : public LLInitParam::Block<Params>
@@ -369,6 +369,7 @@ class LLView
 	virtual void	translate( S32 x, S32 y );
 	void			setOrigin( S32 x, S32 y )	{ mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); }
 	BOOL			translateIntoRect( const LLRect& constraint, S32 min_overlap_pixels = S32_MAX);
+	BOOL			translateRectIntoRect( const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels = S32_MAX);
 	BOOL			translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX);
 	void			centerWithin(const LLRect& bounds);
 
@@ -658,8 +659,11 @@ class LLView
 	// Draw debug rectangles around widgets to help with alignment and spacing
 	static bool	sDebugRects;
 
-    static bool sIsRectDirty;
-    static LLRect sDirtyRect;
+	// Show hexadecimal byte values of unicode symbols in a tooltip
+	static bool	sDebugUnicode;
+
+	static bool sIsRectDirty;
+	static LLRect sDirtyRect;
 
 	// Draw widget names and sizes when drawing debug rectangles, turning this
 	// off is useful to make the rectangles themselves easier to see.
@@ -702,20 +706,16 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co
 		if (!result)
 		{
 			result = LLUICtrlFactory::getDefaultWidget<T>(name);
-
-			if (result)
+			if (!result)
 			{
-				// *NOTE: You cannot call mFoo = getChild<LLFoo>("bar")
-				// in a floater or panel constructor.  The widgets will not
-				// be ready.  Instead, put it in postBuild().
-				LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL;
-			}
-			else
-			{
-				LL_WARNS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL;
-				return NULL;
+				LL_ERRS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL;
 			}
 
+			// *NOTE: You cannot call mFoo = getChild<LLFoo>("bar")
+			// in a floater or panel constructor.  The widgets will not
+			// be ready.  Instead, put it in postBuild().
+			LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL;
+
 			getDefaultWidgetContainer().addChild(result);
 		}
 	}
diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h
index 21bb1be26f8d5c86a17e19c569446d79ecbebf8d..4bc9c4a08efe393935a3634c154a0375e344ea4c 100644
--- a/indra/llui/llviewquery.h
+++ b/indra/llui/llviewquery.h
@@ -55,37 +55,37 @@ class LLQuerySorter
 class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLLeavesFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
 };
 
 class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLRootsFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
 };
 
 class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLVisibleFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
 };
 
 class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLEnabledFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
 };
 
 class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLTabStopFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
 };
 
 class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter>
 {
 	LLSINGLETON_EMPTY_CTOR(LLCtrlFilter);
-	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
 };
 
 template <class T>
diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm
index 586e00b5e48a8cd9c50f86b072b3ddd47ef76d48..0bd4e506a27ea3b7066b584c14cd35a22361d677 100644
--- a/indra/llwindow/llopenglview-objc.mm
+++ b/indra/llwindow/llopenglview-objc.mm
@@ -733,23 +733,52 @@ attributedStringInfo getSegments(NSAttributedString *str)
 
 - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
 {
-	if (!mHasMarkedText)
+	// SL-19801 Special workaround for system emoji picker
+	if ([aString length] == 2)
 	{
-		for (NSInteger i = 0; i < [aString length]; i++)
-		{
-			callUnicodeCallback([aString characterAtIndex:i], mModifiers);
-		}
-	} else {
-        resetPreedit();
-		// We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text.
-		// But just in case...
-		
-		for (NSInteger i = 0; i < [aString length]; i++)
-		{
-			handleUnicodeCharacter([aString characterAtIndex:i]);
-		}
-		mHasMarkedText = FALSE;
+        @try
+        {
+            uint32_t b0 = [aString characterAtIndex:0];
+            uint32_t b1 = [aString characterAtIndex:1];
+            if (((b0 & 0xF000) == 0xD000) && ((b1 & 0xF000) == 0xD000))
+            {
+                uint32_t b = 0x10000 | ((b0 & 0x3F) << 10) | (b1 & 0x3FF);
+                callUnicodeCallback(b, 0);
+                return;
+            }
+        }
+        @catch(NSException * e)
+        {
+            // One of the characters is an attribute string?
+            NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString);
+            return;
+        }
 	}
+    
+    @try
+    {
+        if (!mHasMarkedText)
+        {
+            for (NSInteger i = 0; i < [aString length]; i++)
+            {
+                callUnicodeCallback([aString characterAtIndex:i], mModifiers);
+            }
+        } else {
+            resetPreedit();
+            // We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text.
+            // But just in case...
+            
+            for (NSInteger i = 0; i < [aString length]; i++)
+            {
+                handleUnicodeCharacter([aString characterAtIndex:i]);
+            }
+            mHasMarkedText = FALSE;
+        }
+    }
+    @catch(NSException * e)
+    {
+        NSLog(@"Failed to process an attributed string. Exception: %@ String: %@", e.name, aString);
+    }
 }
 
 - (void) insertNewline:(id)sender
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 04d3b64d5cea9308eee0b00b4cef87d7154e6b3b..6b15e847a32ad086ebe04ddffed6d80f51f746cb 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -18,6 +18,7 @@ include(DragDrop)
 include(EXPAT)
 include(FMODSTUDIO)
 include(Hunspell)
+include(ICU4C)
 include(JPEGEncoderBasic)
 include(JsonCpp)
 include(LLAppearance)
@@ -207,6 +208,7 @@ set(viewer_SOURCE_FILES
     llfloaterdisplayname.cpp
     llfloatereditenvironmentbase.cpp
     llfloatereditextdaycycle.cpp
+    llfloateremojipicker.cpp
     llfloaterenvironmentadjust.cpp
     llfloaterevent.cpp
     llfloaterexperiencepicker.cpp
@@ -419,6 +421,7 @@ set(viewer_SOURCE_FILES
     llpaneleditsky.cpp
     llpaneleditwater.cpp
     llpaneleditwearable.cpp
+    llpanelemojicomplete.cpp
     llpanelenvironment.cpp
     llpanelexperiencelisteditor.cpp
     llpanelexperiencelog.cpp
@@ -860,6 +863,7 @@ set(viewer_HEADER_FILES
     llfloaterdisplayname.h
     llfloatereditenvironmentbase.h
     llfloatereditextdaycycle.h
+    llfloateremojipicker.h
     llfloaterenvironmentadjust.h
     llfloaterevent.h
     llfloaterexperiencepicker.h
@@ -1064,6 +1068,7 @@ set(viewer_HEADER_FILES
     llpaneleditsky.h
     llpaneleditwater.h
     llpaneleditwearable.h
+    llpanelemojicomplete.h
     llpanelenvironment.h
     llpanelexperiencelisteditor.h
     llpanelexperiencelog.h
@@ -1931,6 +1936,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
         ${LLPHYSICSEXTENSIONS_LIBRARIES}
         ll::bugsplat
         ll::tracy
+        ll::icu4c
         )
 
 if( TARGET ll::intel_memops )
@@ -1944,6 +1950,28 @@ endif()
 set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
     "Path to artwork files.")
 
+message("Copying fonts")
+file(GLOB FONT_FILE_GLOB_LIST
+  "${AUTOBUILD_INSTALL_DIR}/fonts/*"
+)
+file(COPY ${FONT_FILE_GLOB_LIST} DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/fonts")
+
+# Copy over the Emoji/shortcodes mapping XML files (and create dependency
+# if they are changed, CMake will run again and copy over new versions)
+message("Copying Emoji/shortcode mappings")
+set(emoji_mapping_src_folder ${AUTOBUILD_INSTALL_DIR}/xui)
+set(emoji_mapping_dst_folder ${CMAKE_CURRENT_SOURCE_DIR}/skins/default/xui)
+
+# Note Turkey is missing from this set (not available in Emoji package yet)
+set(country_codes "da;de;en;es;fr;it;ja;pl;pt;ru;zh")
+foreach(elem ${country_codes})
+   set(emoji_mapping_src_file
+      "${emoji_mapping_src_folder}/${elem}/emoji_characters.xml")
+   set(emoji_mapping_dst_file
+      "${emoji_mapping_dst_folder}/${elem}/emoji_characters.xml")      
+   configure_file(${emoji_mapping_src_file} ${emoji_mapping_dst_file} COPYONLY)
+endforeach()
+
 if (LINUX)
   set(product SecondLife-${ARCH}-${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION})
 
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 1996c504476f1c0e21b27bf1bcee9c79cf393a9b..b7f8ee41e6934d953db404b5c91f4465bd1850cb 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-7.1.3
+7.1.4
diff --git a/indra/newview/app_settings/emoji_groups.xml b/indra/newview/app_settings/emoji_groups.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b433927f912f7f36ae940ea2616b3593de81e29e
--- /dev/null
+++ b/indra/newview/app_settings/emoji_groups.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>all</string>
+      <key>Character</key>
+      <string>🔍</string>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>😀</string>
+      <key>Categories</key>
+      <array>
+        <string>smileys and emotion</string>
+        <string>people and body</string>
+      </array>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>🥬</string>
+      <key>Categories</key>
+      <array>
+        <string>animals and nature</string>
+      </array>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>🍔</string>
+      <key>Categories</key>
+      <array>
+        <string>food and drink</string>
+      </array>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>🛩</string>
+      <key>Categories</key>
+      <array>
+        <string>travel and places</string>
+      </array>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>🏈</string>
+      <key>Categories</key>
+      <array>
+        <string>activities</string>
+      </array>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>💡</string>
+      <key>Categories</key>
+      <array>
+        <string>objects</string>
+      </array>
+    </map>
+    <map>
+      <key>Character</key>
+      <string>âš </string>
+      <key>Categories</key>
+      <array>
+        <string>symbols</string>
+      </array>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>others</string>
+      <key>Character</key>
+      <string>🌂</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>skip</string>
+      <key>Categories</key>
+      <array>
+        <string>components</string>
+      </array>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e707e1eb5e57824b698f9c86657735b425fbb5d4..9d4f4cf91142f03a9b8ff15ba276e824259ef30a 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11343,7 +11343,7 @@
 			<key>Type</key>
 			<string>String</string>
 			<key>Value</key>
-			<string>https://jira.secondlife.com/secure/CreateIssueDetails!init.jspa?pid=10610&amp;issuetype=1&amp;environment=[ENVIRONMENT]&amp;customfield_10253=[LOCATION]</string>
+			<string>https://feedback.secondlife.com/</string>
 		</map>
 	<key>RevokePermsOnStopAnimation</key>
     <map>
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
index a1da4b1f9ac8c59d5b7c1269ec87bd0065ea0955..8769cc023907155245bb1f9e495ed637926cfff9 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
@@ -98,7 +98,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
     haze_glow = max(haze_glow, .001);  // set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
     haze_glow *= glow.x;
     // higher glow.x gives dimmer glow (because next step is 1 / "angle")
-    haze_glow = pow(haze_glow, glow.z);
+    haze_glow = clamp(pow(haze_glow, glow.z), -10, 10);
     // glow.z should be negative, so we're doing a sort of (1 / "angle") function
 
     // add "minimum anti-solar illumination"
diff --git a/indra/newview/fonts/DejaVu-license.txt b/indra/newview/fonts/DejaVu-license.txt
deleted file mode 100644
index df52c1709bea171104d41bf084313ea434858423..0000000000000000000000000000000000000000
--- a/indra/newview/fonts/DejaVu-license.txt
+++ /dev/null
@@ -1,187 +0,0 @@
-Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
-Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
-
-
-Bitstream Vera Fonts Copyright
-------------------------------
-
-Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
-a trademark of Bitstream, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of the fonts accompanying this license ("Fonts") and associated
-documentation files (the "Font Software"), to reproduce and distribute the
-Font Software, including without limitation the rights to use, copy, merge,
-publish, distribute, and/or sell copies of the Font Software, and to permit
-persons to whom the Font Software is furnished to do so, subject to the
-following conditions:
-
-The above copyright and trademark notices and this permission notice shall
-be included in all copies of one or more of the Font Software typefaces.
-
-The Font Software may be modified, altered, or added to, and in particular
-the designs of glyphs or characters in the Fonts may be modified and
-additional glyphs or characters may be added to the Fonts, only if the fonts
-are renamed to names not containing either the words "Bitstream" or the word
-"Vera".
-
-This License becomes null and void to the extent applicable to Fonts or Font
-Software that has been modified and is distributed under the "Bitstream
-Vera" names.
-
-The Font Software may be sold as part of a larger software package but no
-copy of one or more of the Font Software typefaces may be sold by itself.
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
-TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
-FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
-ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
-THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
-FONT SOFTWARE.
-
-Except as contained in this notice, the names of Gnome, the Gnome
-Foundation, and Bitstream Inc., shall not be used in advertising or
-otherwise to promote the sale, use or other dealings in this Font Software
-without prior written authorization from the Gnome Foundation or Bitstream
-Inc., respectively. For further information, contact: fonts at gnome dot
-org.
-
-Arev Fonts Copyright
-------------------------------
-
-Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the fonts accompanying this license ("Fonts") and
-associated documentation files (the "Font Software"), to reproduce
-and distribute the modifications to the Bitstream Vera Font Software,
-including without limitation the rights to use, copy, merge, publish,
-distribute, and/or sell copies of the Font Software, and to permit
-persons to whom the Font Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright and trademark notices and this permission notice
-shall be included in all copies of one or more of the Font Software
-typefaces.
-
-The Font Software may be modified, altered, or added to, and in
-particular the designs of glyphs or characters in the Fonts may be
-modified and additional glyphs or characters may be added to the
-Fonts, only if the fonts are renamed to names not containing either
-the words "Tavmjong Bah" or the word "Arev".
-
-This License becomes null and void to the extent applicable to Fonts
-or Font Software that has been modified and is distributed under the 
-"Tavmjong Bah Arev" names.
-
-The Font Software may be sold as part of a larger software package but
-no copy of one or more of the Font Software typefaces may be sold by
-itself.
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
-TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
-
-Except as contained in this notice, the name of Tavmjong Bah shall not
-be used in advertising or otherwise to promote the sale, use or other
-dealings in this Font Software without prior written authorization
-from Tavmjong Bah. For further information, contact: tavmjong @ free
-. fr.
-
-TeX Gyre DJV Math
------------------
-Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
-
-Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
-(on behalf of TeX users groups) are in public domain.
-
-Letters imported from Euler Fraktur from AMSfonts are (c) American
-Mathematical Society (see below).
-Bitstream Vera Fonts Copyright
-Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
-is a trademark of Bitstream, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of the fonts accompanying this license (“Fonts”) and associated
-documentation
-files (the “Font Software”), to reproduce and distribute the Font Software,
-including without limitation the rights to use, copy, merge, publish,
-distribute,
-and/or sell copies of the Font Software, and to permit persons  to whom
-the Font Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright and trademark notices and this permission notice
-shall be
-included in all copies of one or more of the Font Software typefaces.
-
-The Font Software may be modified, altered, or added to, and in particular
-the designs of glyphs or characters in the Fonts may be modified and
-additional
-glyphs or characters may be added to the Fonts, only if the fonts are
-renamed
-to names not containing either the words “Bitstream” or the word “Vera”.
-
-This License becomes null and void to the extent applicable to Fonts or
-Font Software
-that has been modified and is distributed under the “Bitstream Vera”
-names.
-
-The Font Software may be sold as part of a larger software package but
-no copy
-of one or more of the Font Software typefaces may be sold by itself.
-
-THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
-TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
-FOUNDATION
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
-SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
-ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
-INABILITY TO USE
-THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
-Except as contained in this notice, the names of GNOME, the GNOME
-Foundation,
-and Bitstream Inc., shall not be used in advertising or otherwise to promote
-the sale, use or other dealings in this Font Software without prior written
-authorization from the GNOME Foundation or Bitstream Inc., respectively.
-For further information, contact: fonts at gnome dot org.
-
-AMSFonts (v. 2.2) copyright
-
-The PostScript Type 1 implementation of the AMSFonts produced by and
-previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
-available for general use. This has been accomplished through the
-cooperation
-of a consortium of scientific publishers with Blue Sky Research and Y&Y.
-Members of this consortium include:
-
-Elsevier Science IBM Corporation Society for Industrial and Applied
-Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
-
-In order to assure the authenticity of these fonts, copyright will be
-held by
-the American Mathematical Society. This is not meant to restrict in any way
-the legitimate use of the fonts, such as (but not limited to) electronic
-distribution of documents containing these fonts, inclusion of these fonts
-into other public domain or commercial font collections or computer
-applications, use of the outline data to create derivative fonts and/or
-faces, etc. However, the AMS does require that the AMS copyright notice be
-removed from any derivative versions of the fonts which have been altered in
-any way. In addition, to ensure the fidelity of TeX documents using Computer
-Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
-has requested that any alterations which yield different font metrics be
-given a different name.
-
-$Id$
diff --git a/indra/newview/fonts/DejaVuSans-Bold.ttf b/indra/newview/fonts/DejaVuSans-Bold.ttf
deleted file mode 100644
index 6d65fa7dc41ae8ffae77a4a843a73ba31ffd78c7..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans-Bold.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf b/indra/newview/fonts/DejaVuSans-BoldOblique.ttf
deleted file mode 100644
index 753f2d80b1f9a13026d641b6fd4cafb8d85dd479..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSans-Oblique.ttf b/indra/newview/fonts/DejaVuSans-Oblique.ttf
deleted file mode 100644
index 999bac7714134bc7d931efac92b962a93c52e094..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans-Oblique.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSans.ttf b/indra/newview/fonts/DejaVuSans.ttf
deleted file mode 100644
index e5f7eecce43be41ff0703ed99e1553029b849f14..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSansMono.ttf b/indra/newview/fonts/DejaVuSansMono.ttf
deleted file mode 100644
index f5786022f18216b4c59c6fb0c634b52c8b6e7990..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSansMono.ttf and /dev/null differ
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index debf93dccd740753599de90fdc3adf635176e3a7..51e259992d25bf238313d653600810eefe815727 100644
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -168,6 +168,7 @@ void LLAppCoreHttp::init()
     }
     else
     {
+        LLError::LLUserWarningMsg::showMissingFiles();
         LL_ERRS("Init") << "Missing CA File; should be at " << ca_file << LL_ENDL;
     }
     
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 28dd2a3c9abf33bb536e2f3dee1b938d6ec64745..eb62fe0c182698d7e71595a1af7da8e85c386b48 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -363,7 +363,6 @@ BOOL gRandomizeFramerate = FALSE;
 BOOL gPeriodicSlowFrame = FALSE;
 
 BOOL gCrashOnStartup = FALSE;
-BOOL gLLErrorActivated = FALSE;
 BOOL gLogoutInProgress = FALSE;
 
 BOOL gSimulateMemLeak = FALSE;
@@ -2254,9 +2253,6 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
         OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK);
 #endif
 
-        //Set the ErrorActivated global so we know to create a marker file
-        gLLErrorActivated = true;
-
         gDebugInfo["FatalMessage"] = error_string;
         // We're not already crashing -- we simply *intend* to crash. Since we
         // haven't actually trashed anything yet, we can afford to write the whole
@@ -2265,6 +2261,14 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
     }
 }
 
+void errorMSG(const std::string& title_string, const std::string& message_string)
+{
+    if (!message_string.empty())
+    {
+        OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
+    }
+}
+
 void LLAppViewer::initLoggingAndGetLastDuration()
 {
     //
@@ -2276,6 +2280,8 @@ void LLAppViewer::initLoggingAndGetLastDuration()
     LLError::addGenericRecorder(&errorCallback);
     //LLError::setTimeFunction(getRuntime);
 
+    LLError::LLUserWarningMsg::setHandler(errorMSG);
+
 
     if (mSecondInstance)
     {
@@ -2413,6 +2419,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
 			{	// failed to load
 				if(file.required)
 				{
+                    LLError::LLUserWarningMsg::showMissingFiles();
 					LL_ERRS() << "Error: Cannot load required settings file from: " << full_settings_path << LL_ENDL;
 					return false;
 				}
@@ -2511,6 +2518,7 @@ bool LLAppViewer::initConfiguration()
 	if (!success)
 	{
         LL_WARNS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
+        LLError::LLUserWarningMsg::showMissingFiles();
         if (gDirUtilp->fileExists(settings_file_list))
         {
             LL_ERRS() << "Cannot load default configuration file settings_files.xml. "
@@ -2534,6 +2542,7 @@ bool LLAppViewer::initConfiguration()
 
 	if (!mSettingsLocationList->validateBlock())
 	{
+        LLError::LLUserWarningMsg::showMissingFiles();
         LL_ERRS() << "Invalid settings file list " << settings_file_list << LL_ENDL;
 	}
 
@@ -2968,6 +2977,8 @@ bool LLAppViewer::initConfiguration()
 		LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key));
 	}
 
+    LLError::LLUserWarningMsg::setOutOfMemoryStrings(LLTrans::getString("MBOutOfMemoryTitle"), LLTrans::getString("MBOutOfMemoryErr"));
+
 	return true; // Config was successful.
 }
 
@@ -3005,6 +3016,7 @@ void LLAppViewer::initStrings()
 
 		// initial check to make sure files are there failed
 		gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
+        LLError::LLUserWarningMsg::showMissingFiles();
 		LL_ERRS() << "Viewer failed to find localization and UI files."
 			<< " Please reinstall viewer from https://secondlife.com/support/downloads"
 			<< " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
@@ -4310,6 +4322,7 @@ void LLAppViewer::loadKeyBindings()
 		key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml");
 		if (!gViewerInput.loadBindingsXML(key_bindings_file))
 		{
+            LLError::LLUserWarningMsg::showMissingFiles();
 			LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL;
 		}
 	}
@@ -5400,6 +5413,14 @@ void LLAppViewer::forceErrorLLError()
    	LL_ERRS() << "This is a deliberate llerror" << LL_ENDL;
 }
 
+void LLAppViewer::forceErrorLLErrorMsg()
+{
+    LLError::LLUserWarningMsg::show("Deliberate error");
+    // Note: under debug this will show a message as well,
+    // but release won't show anything and will quit silently
+    LL_ERRS() << "This is a deliberate llerror with a message" << LL_ENDL;
+}
+
 void LLAppViewer::forceErrorBreakpoint()
 {
    	LL_WARNS() << "Forcing a deliberate breakpoint" << LL_ENDL;
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 6d1496d51709c5383bcb64522bdaad751d730304..77a1cdb48585d40aed4f316e3293406ca964f382 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -152,6 +152,7 @@ class LLAppViewer : public LLApp
     // LLAppViewer testing helpers.
     // *NOTE: These will potentially crash the viewer. Only for debugging.
     virtual void forceErrorLLError();
+    virtual void forceErrorLLErrorMsg();
     virtual void forceErrorBreakpoint();
     virtual void forceErrorBadMemoryAccess();
     virtual void forceErrorInfiniteLoop();
diff --git a/indra/newview/llautoreplace.h b/indra/newview/llautoreplace.h
index 23cc3136468575a3ce1dce365cc4cb525c94c28e..a1eebf9dcbdf8e056938ed5c4c35855c831c994f 100644
--- a/indra/newview/llautoreplace.h
+++ b/indra/newview/llautoreplace.h
@@ -203,7 +203,7 @@ class LLAutoReplace : public LLSingleton<LLAutoReplace>
     void setSettings(const LLAutoReplaceSettings& settings);
 
 private:
-    /*virtual*/ void initSingleton();
+    /*virtual*/ void initSingleton() override;
 
     LLAutoReplaceSettings mSettings; ///< configuration information
 	
diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
index a6c9a41fa46e0bf8d0e85b28b9829b00c4072b36..b95b9718906c57373b3fabdb4fd0c79a73a6ec81 100644
--- a/indra/newview/llavatarrenderinfoaccountant.cpp
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -339,6 +339,7 @@ void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regio
         }
         catch (std::bad_alloc&)
         {
+            LLError::LLUserWarningMsg::showOutOfMemory();
             LL_ERRS() << "LLCoros::launch() allocation failure" << LL_ENDL;
         }
 	}
@@ -370,6 +371,7 @@ void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regi
         }
         catch (std::bad_alloc&)
         {
+            LLError::LLUserWarningMsg::showOutOfMemory();
             LL_ERRS() << "LLCoros::launch() allocation failure" << LL_ENDL;
         }
 	}
diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h
index 8abe350196bd9630de2a4e6de8e3f4591d95f423..22ae595d66b0db015d8982667226e5f5ce204ea6 100644
--- a/indra/newview/llchannelmanager.h
+++ b/indra/newview/llchannelmanager.h
@@ -46,7 +46,7 @@ class LLChannelManager : public LLSingleton<LLChannelManager>
 	LLSINGLETON(LLChannelManager);
 	virtual ~LLChannelManager();
 
-	void cleanupSingleton();
+	void cleanupSingleton() override;
 public:
 
 
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 43dc10ef5f49637c59b6bc882f9eec50cd1c0abd..b9bf43258109ce2e7dd2323090a77d94b38126c9 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -1096,6 +1096,8 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
 	editor_params.enabled = false; // read only
 	editor_params.show_context_menu = "true";
 	editor_params.trusted_content = false;
+	editor_params.text_valign = LLFontGL::VAlign::VCENTER;
+	editor_params.use_color = true;
 	mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);
 	mEditor->setIsFriendCallback(LLAvatarActions::isFriend);
 	mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0));
@@ -1213,9 +1215,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 
 	llassert(mEditor);
 	if (!mEditor)
-	{
 		return;
-	}
 
 	bool from_me = chat.mFromID == gAgent.getID();
 	mEditor->setPlainText(use_plain_text_chat_history);
@@ -1225,26 +1225,16 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 		mUnreadChatSources.insert(chat.mFromName);
 		mMoreChatPanel->setVisible(TRUE);
 		std::string chatters;
-		for (unread_chat_source_t::iterator it = mUnreadChatSources.begin();
-			it != mUnreadChatSources.end();)
+		for (const std::string& source : mUnreadChatSources)
 		{
-			chatters += *it;
-			if (++it != mUnreadChatSources.end())
-			{
-				chatters += ", ";
-			}
+			chatters += chatters.size() ? ", " + source : source;
 		}
 		LLStringUtil::format_map_t args;
 		args["SOURCES"] = chatters;
 
-		if (mUnreadChatSources.size() == 1)
-		{
-			mMoreChatText->setValue(LLTrans::getString("unread_chat_single", args));
-		}
-		else
-		{
-			mMoreChatText->setValue(LLTrans::getString("unread_chat_multiple", args));
-		}
+		std::string xml_desc = mUnreadChatSources.size() == 1 ?
+			"unread_chat_single" : "unread_chat_multiple";
+		mMoreChatText->setValue(LLTrans::getString(xml_desc, args));
 		S32 height = mMoreChatText->getTextPixelHeight() + 5;
 		mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height);
 	}
@@ -1292,11 +1282,11 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 		body_message_params.font.style = "ITALIC";
 	}
 
-	if(chat.mChatType == CHAT_TYPE_WHISPER)
+	if (chat.mChatType == CHAT_TYPE_WHISPER)
 	{
 		body_message_params.font.style = "ITALIC";
 	}
-	else if(chat.mChatType == CHAT_TYPE_SHOUT)
+	else if (chat.mChatType == CHAT_TYPE_SHOUT)
 	{
 		body_message_params.font.style = "BOLD";
 	}
@@ -1343,10 +1333,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 		}
 
 		// names showing
-		if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size() != 0)
+		if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size())
 		{
 			// Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text.
-			if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())
+			if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())
 			{
 				// for object IMs, create a secondlife:///app/objectim SLapp
 				std::string url = LLViewerChat::getSenderSLURL(chat, args);
@@ -1406,36 +1396,27 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 			&& mIsLastMessageFromLog == message_from_log)  //distinguish between current and previous chat session's histories
 		{
 			view = getSeparator();
-			p.top_pad = mTopSeparatorPad;
-			p.bottom_pad = mBottomSeparatorPad;
             if (!view)
             {
                 // Might be wiser to make this LL_ERRS, getSeparator() should work in case of correct instalation.
                 LL_WARNS() << "Failed to create separator from " << mMessageSeparatorFilename << ": can't append to history" << LL_ENDL;
                 return;
             }
+
+			p.top_pad = mTopSeparatorPad;
+			p.bottom_pad = mBottomSeparatorPad;
 		}
 		else
 		{
 			view = getHeader(chat, name_params, args);
-			if (mEditor->getLength() == 0)
-				p.top_pad = 0;
-			else
-				p.top_pad = mTopHeaderPad;
-            if (teleport_separator)
-            {
-                p.bottom_pad = mBottomSeparatorPad;
-            }
-            else
-            {
-                p.bottom_pad = mBottomHeaderPad;
-            }
-            if (!view)
-            {
-                LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL;
-                return;
-            }
+			if (!view)
+			{
+				LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL;
+				return;
+			}
 			
+			p.top_pad = mEditor->getLength() ? mTopHeaderPad : 0;
+			p.bottom_pad = teleport_separator ? mBottomSeparatorPad : mBottomHeaderPad;
 		}
 		p.view = view;
 
@@ -1508,11 +1489,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 		}
 	}
 	// usual messages showing
-	else if(!teleport_separator)
+	else if (!teleport_separator)
 	{
 		std::string message = irc_me ? chat.mText.substr(3) : chat.mText;
 
-
 		//MESSAGE TEXT PROCESSING
 		//*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010)
 		if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull())
diff --git a/indra/newview/llchicletbar.h b/indra/newview/llchicletbar.h
index 6c521dc1d504f176b0f3cfc367ed10ece092af14..c295b999624f71e32fd2bbba5515000627a39fdc 100644
--- a/indra/newview/llchicletbar.h
+++ b/indra/newview/llchicletbar.h
@@ -43,11 +43,11 @@ class LLChicletBar
 
 public:
 
-	BOOL postBuild();
+	BOOL postBuild() override;
 
 	LLChicletPanel*	getChicletPanel() { return mChicletPanel; }
 
-	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent) override;
 
 
 	/**
diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h
index 820a5db49143c77ea49c549b12f7d85416bf7728..54aeedcf9ac13a7787d98f449fca60065b52173a 100644
--- a/indra/newview/llconversationlog.h
+++ b/indra/newview/llconversationlog.h
@@ -125,11 +125,11 @@ class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObse
 	void removeObserver(LLConversationLogObserver* observer);
 
 	// LLIMSessionObserver triggers
-	virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg);
-    virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; // Stub
-	virtual void sessionRemoved(const LLUUID& session_id){}											// Stub
-	virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){};								// Stub
-	virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){};	// Stub
+	virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg) override;
+    virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) override {}; // Stub
+	virtual void sessionRemoved(const LLUUID& session_id) override{}											// Stub
+	virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) override{};								// Stub
+	virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) override{};	// Stub
 
 	void notifyObservers();
 
diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp
index 3395777aab42424db453fa9314140951e6814f46..2d332f75f52cf6a85fc638315c0da312f7bfb963 100644
--- a/indra/newview/llexpandabletextbox.cpp
+++ b/indra/newview/llexpandabletextbox.cpp
@@ -88,7 +88,7 @@ class LLExpanderSegment : public LLTextSegment
 									mStyle->getShadowType(), 
 									end - start, draw_rect.getWidth(), 
 									&right_x, 
-									mEditor.getUseEllipses());
+									mEditor.getUseEllipses(), mEditor.getUseColor());
 		return right_x;
 	}
 	/*virtual*/ bool	canEdit() const { return false; }
diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h
index 651404d8905a270e2ab767e804ab1ad2d5065465..70c6c09e0de2b20cc5cb361946c97a66fc1f15fc 100644
--- a/indra/newview/llfeaturemanager.h
+++ b/indra/newview/llfeaturemanager.h
@@ -101,7 +101,7 @@ class LLFeatureManager : public LLFeatureList, public LLSingleton<LLFeatureManag
 	~LLFeatureManager() {cleanupFeatureTables();}
 
 	// initialize this by loading feature table and gpu table
-	void initSingleton();
+	void initSingleton() override;
 
 public:
 
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
index 2422596f60ae1312bc55bda66cc281b1ae2e2db0..42ef41017af26f14f2ea708d68f2c66e66aa6314 100644
--- a/indra/newview/llfloateravatarpicker.cpp
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -741,7 +741,6 @@ void LLFloaterAvatarPicker::processResponse(const LLUUID& query_id, const LLSD&
 	}
 }
 
-//static
 void LLFloaterAvatarPicker::editKeystroke(LLLineEditor* caller, void* user_data)
 {
 	getChildView("Find")->setEnabled(caller->getText().size() > 0);
diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1578caa39c828dc6f10edb152a9ff077b33b5969
--- /dev/null
+++ b/indra/newview/llfloateremojipicker.cpp
@@ -0,0 +1,1346 @@
+/**
+ * @file llfloateremojipicker.cpp
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateremojipicker.h"
+
+#include "llappviewer.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llemojidictionary.h"
+#include "llemojihelper.h"
+#include "llfloaterreg.h"
+#include "llkeyboard.h"
+#include "llscrollcontainer.h"
+#include "llscrollingpanellist.h"
+#include "llscrolllistctrl.h"
+#include "llscrolllistitem.h"
+#include "llsdserialize.h"
+#include "lltextbox.h" 
+#include "llviewerchat.h" 
+
+namespace {
+// The following variables and constants are used for storing the floater state
+// between different lifecycles of the floater and different sissions of the viewer
+
+// Floater constants
+static const S32 ALL_EMOJIS_GROUP_INDEX = -2;
+// https://www.compart.com/en/unicode/U+1F50D
+static const S32 ALL_EMOJIS_IMAGE_INDEX = 0x1F50D;
+static const S32 USED_EMOJIS_GROUP_INDEX = -1;
+// https://www.compart.com/en/unicode/U+23F2
+static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2;
+// https://www.compart.com/en/unicode/U+1F6D1
+static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1;
+// The following categories should follow the required alphabetic order
+static const std::string RECENTLY_USED_CATEGORY = "1 recently used";
+static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used";
+
+// Floater state related variables
+static std::list<llwchar> sRecentlyUsed;
+static std::list<std::pair<llwchar, U32>> sFrequentlyUsed;
+
+// State file related values
+static std::string sStateFileName;
+static const std::string sKeyRecentlyUsed("RecentlyUsed");
+static const std::string sKeyFrequentlyUsed("FrequentlyUsed");
+}
+
+class LLEmojiGridRow : public LLScrollingPanel
+{
+public:
+    LLEmojiGridRow(const LLPanel::Params& panel_params,
+        const LLScrollingPanelList::Params& list_params)
+        : LLScrollingPanel(panel_params)
+        , mList(new LLScrollingPanelList(list_params))
+    {
+        addChild(mList);
+    }
+
+    virtual void updatePanel(BOOL allow_modify) override {}
+
+public:
+    LLScrollingPanelList* mList;
+};
+
+class LLEmojiGridDivider : public LLScrollingPanel
+{
+public:
+    LLEmojiGridDivider(const LLPanel::Params& panel_params, std::string text)
+        : LLScrollingPanel(panel_params)
+        , mText(utf8string_to_wstring(text))
+    {
+    }
+
+    virtual void draw() override
+    {
+        LLScrollingPanel::draw();
+
+        F32 x = 4; // padding-left
+        F32 y = getRect().getHeight() / 2;
+        LLFontGL::getFontSansSerif()->render(
+            mText,                      // wstr
+            0,                          // begin_offset
+            x,                          // x
+            y,                          // y
+            LLColor4::white,            // color
+            LLFontGL::LEFT,             // halign
+            LLFontGL::VCENTER,          // valign
+            LLFontGL::NORMAL,           // style
+            LLFontGL::DROP_SHADOW_SOFT, // shadow
+            mText.size());              // max_chars
+    }
+
+    virtual void updatePanel(BOOL allow_modify) override {}
+
+private:
+    const LLWString mText;
+};
+
+class LLEmojiGridIcon : public LLScrollingPanel
+{
+public:
+    LLEmojiGridIcon(
+        const LLPanel::Params& panel_params
+        , const LLEmojiSearchResult& emoji)
+        : LLScrollingPanel(panel_params)
+        , mData(emoji)
+        , mText(LLWString(1, emoji.Character))
+    {
+    }
+
+    virtual void draw() override
+    {
+        LLScrollingPanel::draw();
+
+        F32 x = getRect().getWidth() / 2;
+        F32 y = getRect().getHeight() / 2;
+        LLFontGL::getFontEmoji()->render(
+            mText,                      // wstr
+            0,                          // begin_offset
+            x,                          // x
+            y,                          // y
+            LLColor4::white,            // color
+            LLFontGL::HCENTER,          // halign
+            LLFontGL::VCENTER,          // valign
+            LLFontGL::NORMAL,           // style
+            LLFontGL::DROP_SHADOW_SOFT, // shadow
+            1);                         // max_chars
+    }
+
+    virtual void updatePanel(BOOL allow_modify) override {}
+
+    const LLEmojiSearchResult& getData() const { return mData; }
+    LLWString getText() const { return mText; }
+
+private:
+    const LLEmojiSearchResult mData;
+    const LLWString mText;
+};
+
+class LLEmojiPreviewPanel : public LLPanel
+{
+public:
+    LLEmojiPreviewPanel()
+        : LLPanel()
+    {
+    }
+
+    void setIcon(const LLEmojiGridIcon* icon)
+    {
+        if (icon)
+        {
+            setData(icon->getData().Character, icon->getData().String, icon->getData().Begin, icon->getData().End);
+        }
+        else
+        {
+            setData(0, LLStringUtil::null, 0, 0);
+        }
+    }
+
+    void setData(llwchar emoji, std::string title, size_t begin, size_t end)
+    {
+        mWStr = LLWString(1, emoji);
+        mEmoji = emoji;
+        mTitle = title;
+        mBegin = begin;
+        mEnd = end;
+    }
+
+    virtual void draw() override
+    {
+        LLPanel::draw();
+
+        S32 clientHeight = getRect().getHeight();
+        S32 clientWidth = getRect().getWidth();
+        S32 iconWidth = clientHeight;
+
+        F32 centerX = 0.5f * iconWidth;
+        F32 centerY = 0.5f * clientHeight;
+        drawIcon(centerX, centerY - 1, iconWidth);
+
+        static LLColor4 defaultColor(0.75f, 0.75f, 0.75f, 1.0f);
+        LLColor4 textColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", defaultColor);
+        S32 max_pixels = clientWidth - iconWidth;
+        drawName(iconWidth, centerY, max_pixels, textColor);
+    }
+
+protected:
+    void drawIcon(F32 x, F32 y, S32 max_pixels)
+    {
+        LLFontGL::getFontEmojiHuge()->render(
+            mWStr,                      // wstr
+            0,                          // begin_offset
+            x,                          // x
+            y,                          // y
+            LLColor4::white,            // color
+            LLFontGL::HCENTER,          // halign
+            LLFontGL::VCENTER,          // valign
+            LLFontGL::NORMAL,           // style
+            LLFontGL::DROP_SHADOW_SOFT, // shadow
+            1,                          // max_chars
+            max_pixels);                // max_pixels
+    }
+
+    void drawName(F32 x, F32 y, S32 max_pixels, LLColor4& color)
+    {
+        F32 x0 = x;
+        F32 x1 = max_pixels;
+        LLFontGL* font = LLFontGL::getFontEmoji();
+        if (mBegin)
+        {
+            std::string text = mTitle.substr(0, mBegin);
+            font->renderUTF8(
+                text,                       // text
+                0,                          // begin_offset
+                x0,                         // x
+                y,                          // y
+                color,                      // color
+                LLFontGL::LEFT,             // halign
+                LLFontGL::VCENTER,          // valign
+                LLFontGL::NORMAL,           // style
+                LLFontGL::DROP_SHADOW_SOFT, // shadow
+                text.size(),                // max_chars
+                x1);                        // max_pixels
+            F32 dx = font->getWidthF32(text);
+            x0 += dx;
+            x1 -= dx;
+        }
+        if (x1 > 0 && mEnd > mBegin)
+        {
+            std::string text = mTitle.substr(mBegin, mEnd - mBegin);
+            font->renderUTF8(
+                text,                       // text
+                0,                          // begin_offset
+                x0,                         // x
+                y,                          // y
+                LLColor4::yellow6,          // color
+                LLFontGL::LEFT,             // halign
+                LLFontGL::VCENTER,          // valign
+                LLFontGL::NORMAL,           // style
+                LLFontGL::DROP_SHADOW_SOFT, // shadow
+                text.size(),                // max_chars
+                x1);                        // max_pixels
+            F32 dx = font->getWidthF32(text);
+            x0 += dx;
+            x1 -= dx;
+        }
+        if (x1 > 0 && mEnd < mTitle.size())
+        {
+            std::string text = mEnd ? mTitle.substr(mEnd) : mTitle;
+            font->renderUTF8(
+                text,                       // text
+                0,                          // begin_offset
+                x0,                         // x
+                y,                          // y
+                color,                      // color
+                LLFontGL::LEFT,             // halign
+                LLFontGL::VCENTER,          // valign
+                LLFontGL::NORMAL,           // style
+                LLFontGL::DROP_SHADOW_SOFT, // shadow
+                text.size(),                // max_chars
+                x1);                        // max_pixels
+        }
+    }
+
+private:
+    llwchar mEmoji;
+    LLWString mWStr;
+    std::string mTitle;
+    size_t mBegin;
+    size_t mEnd;
+};
+
+LLFloaterEmojiPicker::LLFloaterEmojiPicker(const LLSD& key)
+: super(key)
+{
+    // This floater should hover on top of our dependent (with the dependent having the focus)
+    setFocusStealsFrontmost(FALSE);
+    setBackgroundVisible(FALSE);
+    setAutoFocus(FALSE);
+
+    loadState();
+}
+
+BOOL LLFloaterEmojiPicker::postBuild()
+{
+    mGroups = getChild<LLPanel>("Groups");
+    mBadge = getChild<LLPanel>("Badge");
+    mEmojiScroll = getChild<LLScrollContainer>("EmojiGridContainer");
+    mEmojiGrid = getChild<LLScrollingPanelList>("EmojiGrid");
+    mDummy = getChild<LLTextBox>("Dummy");
+
+    mPreview = new LLEmojiPreviewPanel();
+    mPreview->setVisible(FALSE);
+    addChild(mPreview);
+
+    return LLFloater::postBuild();
+}
+
+void LLFloaterEmojiPicker::onOpen(const LLSD& key)
+{
+    mHint = key["hint"].asString();
+
+    LLEmojiHelper::instance().setIsHideDisabled(mHint.empty());
+    mFilterPattern = mHint;
+
+    initialize();
+
+    gFloaterView->adjustToFitScreen(this, FALSE);
+}
+
+void LLFloaterEmojiPicker::dirtyRect()
+{
+    super::dirtyRect();
+
+    if (!mPreview)
+        return;
+
+    const S32 HPADDING = 4;
+    const S32 VOFFSET = 12;
+    LLRect rect(HPADDING, mDummy->getRect().mTop + 6, getRect().getWidth() - HPADDING, VOFFSET);
+    if (mPreview->getRect() != rect)
+    {
+        mPreview->setRect(rect);
+    }
+
+    if (mEmojiScroll && mEmojiGrid)
+    {
+        S32 outer_width = mEmojiScroll->getRect().getWidth();
+        S32 inner_width = mEmojiGrid->getRect().getWidth();
+        if (outer_width != inner_width)
+        {
+            resizeGroupButtons();
+            fillEmojis(true);
+        }
+    }
+}
+
+void LLFloaterEmojiPicker::initialize()
+{
+    S32 groupIndex = mSelectedGroupIndex && mSelectedGroupIndex <= mFilteredEmojiGroups.size() ?
+        mFilteredEmojiGroups[mSelectedGroupIndex - 1] : ALL_EMOJIS_GROUP_INDEX;
+
+    fillGroups();
+
+    if (mFilteredEmojis.empty())
+    {
+        if (!mHint.empty())
+        {
+            hideFloater();
+            return;
+        }
+
+        mGroups->setVisible(FALSE);
+        mFocusedIconRow = -1;
+        mFocusedIconCol = -1;
+        mFocusedIcon = nullptr;
+        mHoveredIcon = nullptr;
+        mEmojiScroll->goToTop();
+        mEmojiGrid->clearPanels();
+
+        if (mFilterPattern.empty())
+        {
+            showPreview(false);
+        }
+        else
+        {
+            const std::string prompt("No emoji found for ");
+            std::string title(prompt + '"' + mFilterPattern.substr(1) + '"');
+            mPreview->setData(EMPTY_LIST_IMAGE_INDEX, title, prompt.size() + 1, title.size() - 1);
+            showPreview(true);
+        }
+        return;
+    }
+
+    mGroups->setVisible(TRUE);
+    mPreview->setIcon(nullptr);
+    showPreview(true);
+
+    mSelectedGroupIndex = groupIndex == ALL_EMOJIS_GROUP_INDEX ? 0 :
+        (1 + std::distance(mFilteredEmojiGroups.begin(),
+            std::find(mFilteredEmojiGroups.begin(), mFilteredEmojiGroups.end(), groupIndex))) %
+        (1 + mFilteredEmojiGroups.size());
+
+    mGroupButtons[mSelectedGroupIndex]->setToggleState(TRUE);
+    mGroupButtons[mSelectedGroupIndex]->setUseFontColor(TRUE);
+
+    fillEmojis();
+}
+
+void LLFloaterEmojiPicker::fillGroups()
+{
+    // Do not use deleteAllChildren() because mBadge shouldn't be removed
+    for (LLButton* button : mGroupButtons)
+    {
+        mGroups->removeChild(button);
+    }
+    mFilteredEmojiGroups.clear();
+    mFilteredEmojis.clear();
+    mGroupButtons.clear();
+
+    LLButton::Params params;
+    params.font = LLFontGL::getFontEmoji();
+
+    LLRect rect;
+    rect.mTop = mGroups->getRect().getHeight();
+    rect.mBottom = mBadge->getRect().getHeight();
+
+    // Create button for "All categories"
+    createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX);
+
+    // Create group and button for "Recently used" and/or "Frequently used"
+    if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty())
+    {
+        std::map<std::string, std::vector<LLEmojiSearchResult>> cats;
+        fillCategoryRecentlyUsed(cats);
+        fillCategoryFrequentlyUsed(cats);
+
+        if (!cats.empty())
+        {
+            mFilteredEmojiGroups.push_back(USED_EMOJIS_GROUP_INDEX);
+            mFilteredEmojis.emplace_back(cats);
+            createGroupButton(params, rect, USED_EMOJIS_IMAGE_INDEX);
+        }
+    }
+
+    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups();
+
+    // List all categories in the dictionary
+    for (U32 i = 0; i < groups.size(); ++i)
+    {
+        std::map<std::string, std::vector<LLEmojiSearchResult>> cats;
+
+        fillGroupEmojis(cats, i);
+
+        if (!cats.empty())
+        {
+            mFilteredEmojiGroups.push_back(i);
+            mFilteredEmojis.emplace_back(cats);
+            createGroupButton(params, rect, groups[i].Character);
+        }
+    }
+
+    resizeGroupButtons();
+}
+
+void LLFloaterEmojiPicker::fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats)
+{
+    if (sRecentlyUsed.empty())
+        return;
+
+    std::vector<LLEmojiSearchResult> emojis;
+
+    // In case of empty mFilterPattern we'd use sRecentlyUsed directly
+    if (!mFilterPattern.empty())
+    {
+        // List all emojis in "Recently used"
+        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+        std::size_t begin, end;
+        for (llwchar emoji : sRecentlyUsed)
+        {
+            auto e2d = emoji2descr.find(emoji);
+            if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+            {
+                const std::string shortcode(e2d->second->ShortCodes.front());
+                if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
+                {
+                    emojis.emplace_back(emoji, shortcode, begin, end);
+                }
+            }
+        }
+        if (emojis.empty())
+            return;
+    }
+
+    cats.emplace(std::make_pair(RECENTLY_USED_CATEGORY, emojis));
+}
+
+void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats)
+{
+    if (sFrequentlyUsed.empty())
+        return;
+
+    std::vector<LLEmojiSearchResult> emojis;
+
+    // In case of empty mFilterPattern we'd use sFrequentlyUsed directly
+    if (!mFilterPattern.empty())
+    {
+        // List all emojis in "Frequently used"
+        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+        std::size_t begin, end;
+        for (const auto& emoji : sFrequentlyUsed)
+        {
+            auto e2d = emoji2descr.find(emoji.first);
+            if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+            {
+                const std::string shortcode(e2d->second->ShortCodes.front());
+                if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
+                {
+                    emojis.emplace_back(emoji.first, shortcode, begin, end);
+                }
+            }
+        }
+        if (emojis.empty())
+            return;
+    }
+
+    cats.emplace(std::make_pair(FREQUENTLY_USED_CATEGORY, emojis));
+}
+
+void LLFloaterEmojiPicker::fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index)
+{
+    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups();
+    const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs();
+
+    for (const std::string& category : groups[index].Categories)
+    {
+        const LLEmojiDictionary::cat2descrs_map_t::const_iterator& c2d = category2Descr.find(category);
+        if (c2d == category2Descr.end())
+            continue;
+
+        std::vector<LLEmojiSearchResult> emojis;
+
+        // In case of empty mFilterPattern we'd use category2Descr directly
+        if (!mFilterPattern.empty())
+        {
+            // List all emojis in category
+            std::size_t begin, end;
+            for (const LLEmojiDescriptor* descr : c2d->second)
+            {
+                if (!descr->ShortCodes.empty())
+                {
+                    const std::string shortcode(descr->ShortCodes.front());
+                    if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
+                    {
+                        emojis.emplace_back(descr->Character, shortcode, begin, end);
+                    }
+                }
+            }
+            if (emojis.empty())
+                continue;
+        }
+
+        cats.emplace(std::make_pair(category, emojis));
+    }
+}
+
+void LLFloaterEmojiPicker::createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji)
+{
+    LLButton* button = LLUICtrlFactory::create<LLButton>(params);
+    button->setClickedCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonClick(ctrl); });
+    button->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseEnter(ctrl); });
+    button->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseLeave(ctrl); });
+
+    button->setRect(rect);
+    button->setTabStop(FALSE);
+    button->setLabel(LLUIString(LLWString(1, emoji)));
+    button->setUseFontColor(FALSE);
+
+    mGroupButtons.push_back(button);
+    mGroups->addChild(button);
+}
+
+void LLFloaterEmojiPicker::resizeGroupButtons()
+{
+    U32 groupCount = (U32)mGroupButtons.size();
+    if (!groupCount)
+        return;
+
+    S32 totalWidth = mGroups->getRect().getWidth();
+    S32 badgeWidth = totalWidth / groupCount;
+    S32 leftOffset = (totalWidth - badgeWidth * groupCount) / 2;
+
+    for (U32 i = 0; i < groupCount; ++i)
+    {
+        LLRect rect = mGroupButtons[i]->getRect();
+        rect.mLeft = leftOffset + badgeWidth * i;
+        rect.mRight = rect.mLeft + badgeWidth;
+        mGroupButtons[i]->setRect(rect);
+    }
+
+    LLRect rect = mBadge->getRect();
+    rect.mLeft = leftOffset + badgeWidth * mSelectedGroupIndex;
+    rect.mRight = rect.mLeft + badgeWidth;
+    mBadge->setRect(rect);
+}
+
+void LLFloaterEmojiPicker::selectEmojiGroup(U32 index)
+{
+    if (index == mSelectedGroupIndex || index >= mGroupButtons.size())
+        return;
+
+    if (mSelectedGroupIndex < mGroupButtons.size())
+    {
+        mGroupButtons[mSelectedGroupIndex]->setUseFontColor(FALSE);
+        mGroupButtons[mSelectedGroupIndex]->setToggleState(FALSE);
+    }
+
+    mSelectedGroupIndex = index;
+    mGroupButtons[mSelectedGroupIndex]->setToggleState(TRUE);
+    mGroupButtons[mSelectedGroupIndex]->setUseFontColor(TRUE);
+
+    LLButton* button = mGroupButtons[mSelectedGroupIndex];
+    LLRect rect = mBadge->getRect();
+    rect.mLeft = button->getRect().mLeft;
+    rect.mRight = button->getRect().mRight;
+    mBadge->setRect(rect);
+
+    fillEmojis();
+}
+
+void LLFloaterEmojiPicker::fillEmojis(bool fromResize)
+{
+    S32 scrollbar_size = mEmojiScroll->getSize();
+    if (scrollbar_size < 0)
+    {
+        static LLUICachedControl<S32> scrollbar_size_control("UIScrollbarSize", 0);
+        scrollbar_size = scrollbar_size_control;
+    }
+
+    const S32 scroll_width = mEmojiScroll->getRect().getWidth();
+    const S32 client_width = scroll_width - scrollbar_size - mEmojiScroll->getBorderWidth() * 2;
+    const S32 grid_padding = mEmojiGrid->getPadding();
+    const S32 icon_spacing = mEmojiGrid->getSpacing();
+    const S32 row_width = client_width - grid_padding * 2;
+    const S32 icon_size = 28; // icon width and height
+    const S32 max_icons = llmax(1, (row_width + icon_spacing) / (icon_size + icon_spacing));
+
+    // Optimization: don't rearrange for different widths with the same maxIcons
+    if (fromResize && (max_icons == mRecentMaxIcons))
+        return;
+
+    mRecentMaxIcons = max_icons;
+
+    mFocusedIconRow = 0;
+    mFocusedIconCol = 0;
+    mFocusedIcon = nullptr;
+    mHoveredIcon = nullptr;
+    mEmojiScroll->goToTop();
+    mEmojiGrid->clearPanels();
+    mPreview->setIcon(nullptr);
+
+    if (mEmojiGrid->getRect().getWidth() != client_width)
+    {
+        LLRect rect = mEmojiGrid->getRect();
+        rect.mRight = rect.mLeft + client_width;
+        mEmojiGrid->setRect(rect);
+    }
+
+    LLPanel::Params row_panel_params;
+    row_panel_params.rect = LLRect(0, icon_size, row_width, 0);
+
+    LLScrollingPanelList::Params row_list_params;
+    row_list_params.rect = row_panel_params.rect;
+    row_list_params.is_horizontal = TRUE;
+    row_list_params.padding = 0;
+    row_list_params.spacing = icon_spacing;
+
+    LLPanel::Params icon_params;
+    LLRect icon_rect(0, icon_size, icon_size, 0);
+
+    static LLColor4 default_color(0.75f, 0.75f, 0.75f, 1.0f);
+    LLColor4 bg_color = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", default_color);
+
+    if (!mSelectedGroupIndex)
+    {
+        // List all groups
+        for (const auto& group : mFilteredEmojis)
+        {
+            // List all categories in the group
+            for (const auto& category : group)
+            {
+                // List all emojis in the category
+                fillEmojisCategory(category.second, category.first, row_panel_params,
+                    row_list_params, icon_params, icon_rect, max_icons, bg_color);
+            }
+        }
+    }
+    else
+    {
+        // List all categories in the selected group
+        const auto& group = mFilteredEmojis[mSelectedGroupIndex - 1];
+        for (const auto& category : group)
+        {
+            // List all emojis in the category
+            fillEmojisCategory(category.second, category.first, row_panel_params,
+                row_list_params, icon_params, icon_rect, max_icons, bg_color);
+        }
+    }
+
+    if (mEmojiGrid->getPanelList().empty())
+    {
+        showPreview(false);
+        mFocusedIconRow = -1;
+        mFocusedIconCol = -1;
+        if (!mHint.empty())
+        {
+            hideFloater();
+        }
+    }
+    else
+    {
+        showPreview(true);
+        mFocusedIconRow = 0;
+        mFocusedIconCol = 0;
+        moveFocusedIconNext();
+    }
+}
+
+void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchResult>& emojis,
+    const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+    const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg)
+{
+    // Place the category title
+    std::string title =
+        category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") :
+        category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") :
+        isupper(category.front()) ? category : LLStringUtil::capitalize(category);
+    LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title);
+    mEmojiGrid->addPanel(div, true);
+
+    int icon_index = 0;
+    LLEmojiGridRow* row = nullptr;
+
+    if (mFilterPattern.empty())
+    {
+        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+        LLEmojiSearchResult emoji { 0, "", 0, 0 };
+        if (category == RECENTLY_USED_CATEGORY)
+        {
+            for (llwchar code : sRecentlyUsed)
+            {
+                const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code);
+                if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+                {
+                    emoji.Character = code;
+                    emoji.String = e2d->second->ShortCodes.front();
+                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+                        icon_rect, max_icons, bg, row, icon_index);
+                }
+            }
+        }
+        else if (category == FREQUENTLY_USED_CATEGORY)
+        {
+            for (const auto& code : sFrequentlyUsed)
+            {
+                const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code.first);
+                if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+                {
+                    emoji.Character = code.first;
+                    emoji.String = e2d->second->ShortCodes.front();
+                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+                        icon_rect, max_icons, bg, row, icon_index);
+                }
+            }
+        }
+        else
+        {
+            const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs();
+            const LLEmojiDictionary::cat2descrs_map_t::const_iterator& c2d = category2Descr.find(category);
+            if (c2d != category2Descr.end())
+            {
+                for (const LLEmojiDescriptor* descr : c2d->second)
+                {
+                    emoji.Character = descr->Character;
+                    emoji.String = descr->ShortCodes.front();
+                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+                        icon_rect, max_icons, bg, row, icon_index);
+                }
+            }
+        }
+    }
+    else
+    {
+        for (const LLEmojiSearchResult& emoji : emojis)
+        {
+            createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+                icon_rect, max_icons, bg, row, icon_index);
+        }
+    }
+}
+
+void LLFloaterEmojiPicker::createEmojiIcon(const LLEmojiSearchResult& emoji,
+    const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+    const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg,
+    LLEmojiGridRow*& row, int& icon_index)
+{
+    // Place a new row each (max_icons) icons
+    if (!(icon_index % max_icons))
+    {
+        row = new LLEmojiGridRow(row_panel_params, *(const LLScrollingPanelList::Params*)&row_list_params);
+        mEmojiGrid->addPanel(row, true);
+    }
+
+    // Place a new icon to the current row
+    LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, emoji);
+    icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); });
+    icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); });
+    icon->setMouseDownCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseDown(ctrl); });
+    icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseUp(ctrl); });
+    icon->setBackgroundColor(bg);
+    icon->setBackgroundOpaque(1);
+    icon->setRect(icon_rect);
+    row->mList->addPanel(icon, true);
+
+    icon_index++;
+}
+
+void LLFloaterEmojiPicker::showPreview(bool show)
+{
+    //mPreview->setIcon(nullptr);
+    mDummy->setVisible(show ? FALSE : TRUE);
+    mPreview->setVisible(show ? TRUE : FALSE);
+}
+
+void LLFloaterEmojiPicker::onGroupButtonClick(LLUICtrl* ctrl)
+{
+    if (LLButton* button = dynamic_cast<LLButton*>(ctrl))
+    {
+        if (button == mGroupButtons[mSelectedGroupIndex] || button->getToggleState())
+            return;
+
+        auto it = std::find(mGroupButtons.begin(), mGroupButtons.end(), button);
+        if (it == mGroupButtons.end())
+            return;
+
+        selectEmojiGroup(it - mGroupButtons.begin());
+    }
+}
+
+void LLFloaterEmojiPicker::onGroupButtonMouseEnter(LLUICtrl* ctrl)
+{
+    if (LLButton* button = dynamic_cast<LLButton*>(ctrl))
+    {
+        button->setUseFontColor(TRUE);
+    }
+}
+
+void LLFloaterEmojiPicker::onGroupButtonMouseLeave(LLUICtrl* ctrl)
+{
+    if (LLButton* button = dynamic_cast<LLButton*>(ctrl))
+    {
+        button->setUseFontColor(button->getToggleState());
+    }
+}
+
+void LLFloaterEmojiPicker::onEmojiMouseEnter(LLUICtrl* ctrl)
+{
+    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl))
+    {
+        if (mFocusedIcon && mFocusedIcon != icon && mFocusedIcon->isBackgroundVisible())
+        {
+            unselectGridIcon(mFocusedIcon);
+        }
+
+        if (mHoveredIcon && mHoveredIcon != icon)
+        {
+            unselectGridIcon(mHoveredIcon);
+        }
+
+        selectGridIcon(icon);
+
+        mHoveredIcon = icon;
+    }
+}
+
+void LLFloaterEmojiPicker::onEmojiMouseLeave(LLUICtrl* ctrl)
+{
+    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl))
+    {
+        if (icon == mHoveredIcon)
+        {
+            if (icon != mFocusedIcon)
+            {
+                unselectGridIcon(icon);
+            }
+            mHoveredIcon = nullptr;
+        }
+
+        if (!mHoveredIcon && mFocusedIcon && !mFocusedIcon->isBackgroundVisible())
+        {
+            selectGridIcon(mFocusedIcon);
+        }
+    }
+}
+
+void LLFloaterEmojiPicker::onEmojiMouseDown(LLUICtrl* ctrl)
+{
+    if (getSoundFlags() & MOUSE_DOWN)
+    {
+        make_ui_sound("UISndClick");
+    }
+}
+
+void LLFloaterEmojiPicker::onEmojiMouseUp(LLUICtrl* ctrl)
+{
+    if (getSoundFlags() & MOUSE_UP)
+    {
+        make_ui_sound("UISndClickRelease");
+    }
+
+    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl))
+    {
+        LLSD value(wstring_to_utf8str(icon->getText()));
+        setValue(value);
+
+        onCommit();
+
+        if (!mHint.empty() || !(gKeyboard->currentMask(TRUE) & MASK_SHIFT))
+        {
+            hideFloater();
+        }
+    }
+}
+
+void LLFloaterEmojiPicker::selectFocusedIcon()
+{
+    if (mFocusedIcon && mFocusedIcon != mHoveredIcon)
+    {
+        unselectGridIcon(mFocusedIcon);
+    }
+
+    // Both mFocusedIconRow and mFocusedIconCol should be already verified
+    LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(mEmojiGrid->getPanelList()[mFocusedIconRow]);
+    mFocusedIcon = row ? dynamic_cast<LLEmojiGridIcon*>(row->mList->getPanelList()[mFocusedIconCol]) : nullptr;
+
+    if (mFocusedIcon && !mHoveredIcon)
+    {
+        selectGridIcon(mFocusedIcon);
+    }
+}
+
+bool LLFloaterEmojiPicker::moveFocusedIconUp()
+{
+    for (S32 i = mFocusedIconRow - 1; i >= 0; --i)
+    {
+        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
+        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
+        if (row && row->mList->getPanelList().size() > mFocusedIconCol)
+        {
+            mEmojiScroll->scrollToShowRect(row->getBoundingRect());
+            mFocusedIconRow = i;
+            selectFocusedIcon();
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool LLFloaterEmojiPicker::moveFocusedIconDown()
+{
+    S32 rowCount = mEmojiGrid->getPanelList().size();
+    for (S32 i = mFocusedIconRow + 1; i < rowCount; ++i)
+    {
+        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
+        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
+        if (row && row->mList->getPanelList().size() > mFocusedIconCol)
+        {
+            mEmojiScroll->scrollToShowRect(row->getBoundingRect());
+            mFocusedIconRow = i;
+            selectFocusedIcon();
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool LLFloaterEmojiPicker::moveFocusedIconPrev()
+{
+    if (mHoveredIcon)
+        return false;
+
+    if (mFocusedIconCol > 0)
+    {
+        mFocusedIconCol--;
+        selectFocusedIcon();
+        return true;
+    }
+
+    for (S32 i = mFocusedIconRow - 1; i >= 0; --i)
+    {
+        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
+        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
+        if (row && row->mList->getPanelList().size())
+        {
+            mEmojiScroll->scrollToShowRect(row->getBoundingRect());
+            mFocusedIconCol = row->mList->getPanelList().size() - 1;
+            mFocusedIconRow = i;
+            selectFocusedIcon();
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool LLFloaterEmojiPicker::moveFocusedIconNext()
+{
+    if (mHoveredIcon)
+        return false;
+
+    LLScrollingPanel* panel = mEmojiGrid->getPanelList()[mFocusedIconRow];
+    LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
+    S32 colCount = row ? row->mList->getPanelList().size() : 0;
+    if (mFocusedIconCol < colCount - 1)
+    {
+        mFocusedIconCol++;
+        selectFocusedIcon();
+        return true;
+    }
+
+    S32 rowCount = mEmojiGrid->getPanelList().size();
+    for (S32 i = mFocusedIconRow + 1; i < rowCount; ++i)
+    {
+        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
+        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
+        if (row && row->mList->getPanelList().size())
+        {
+            mEmojiScroll->scrollToShowRect(row->getBoundingRect());
+            mFocusedIconCol = 0;
+            mFocusedIconRow = i;
+            selectFocusedIcon();
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void LLFloaterEmojiPicker::selectGridIcon(LLEmojiGridIcon* icon)
+{
+    icon->setBackgroundVisible(TRUE);
+    mPreview->setIcon(icon);
+}
+
+void LLFloaterEmojiPicker::unselectGridIcon(LLEmojiGridIcon* icon)
+{
+    icon->setBackgroundVisible(FALSE);
+    mPreview->setIcon(nullptr);
+}
+
+// virtual
+BOOL LLFloaterEmojiPicker::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+    if (mask == MASK_NONE)
+    {
+        switch (key)
+        {
+        case KEY_UP:
+            moveFocusedIconUp();
+            return TRUE;
+        case KEY_DOWN:
+            moveFocusedIconDown();
+            return TRUE;
+        case KEY_LEFT:
+            moveFocusedIconPrev();
+            return TRUE;
+        case KEY_RIGHT:
+            moveFocusedIconNext();
+            return TRUE;
+        case KEY_ESCAPE:
+            hideFloater();
+            return TRUE;
+        }
+    }
+
+    if (mask == MASK_ALT)
+    {
+        switch (key)
+        {
+        case KEY_LEFT:
+            selectEmojiGroup((mSelectedGroupIndex + mFilteredEmojis.size()) % mGroupButtons.size());
+            return TRUE;
+        case KEY_RIGHT:
+            selectEmojiGroup((mSelectedGroupIndex + 1) % mGroupButtons.size());
+            return TRUE;
+        }
+    }
+
+    if (key == KEY_RETURN)
+    {
+        U64 time = totalTime();
+        // <Shift+Return> comes twice for unknown reason
+        if (mFocusedIcon && (time - mRecentReturnPressedMs > 100000)) // Min interval 0.1 sec.
+        {
+            onEmojiMouseDown(mFocusedIcon);
+            onEmojiMouseUp(mFocusedIcon);
+        }
+        mRecentReturnPressedMs = time;
+        return TRUE;
+    }
+
+    if (mHint.empty())
+    {
+        if (key >= 0x20 && key < 0x80)
+        {
+            if (!mEmojiGrid->getPanelList().empty())
+            {
+                if (mFilterPattern.empty())
+                {
+                    mFilterPattern = ":";
+                }
+                mFilterPattern += (char)key;
+                initialize();
+            }
+            return TRUE;
+        }
+        else if (key == KEY_BACKSPACE)
+        {
+            if (!mFilterPattern.empty())
+            {
+                mFilterPattern.pop_back();
+                if (mFilterPattern == ":")
+                {
+                    mFilterPattern.clear();
+                }
+                initialize();
+            }
+            return TRUE;
+        }
+    }
+
+    return super::handleKey(key, mask, called_from_parent);
+}
+
+// virtual
+void LLFloaterEmojiPicker::goneFromFront()
+{
+    hideFloater();
+}
+
+void LLFloaterEmojiPicker::hideFloater() const
+{
+    LLEmojiHelper::instance().hideHelper(nullptr, true);
+}
+
+// static
+std::list<llwchar>& LLFloaterEmojiPicker::getRecentlyUsed()
+{
+    loadState();
+    return sRecentlyUsed;
+}
+
+// static
+void LLFloaterEmojiPicker::onEmojiUsed(llwchar emoji)
+{
+    // Update sRecentlyUsed
+    auto itr = std::find(sRecentlyUsed.begin(), sRecentlyUsed.end(), emoji);
+    if (itr == sRecentlyUsed.end())
+    {
+        sRecentlyUsed.push_front(emoji);
+    }
+    else if (itr != sRecentlyUsed.begin())
+    {
+        sRecentlyUsed.erase(itr);
+        sRecentlyUsed.push_front(emoji);
+    }
+
+    // Increment and reorder sFrequentlyUsed
+    auto itf = sFrequentlyUsed.begin();
+    while (itf != sFrequentlyUsed.end())
+    {
+        if (itf->first == emoji)
+        {
+            itf->second++;
+            while (itf != sFrequentlyUsed.begin())
+            {
+                auto prior = itf;
+                prior--;
+                if (prior->second > itf->second)
+                    break;
+                prior->swap(*itf);
+                itf = prior;
+            }
+            break;
+        }
+        itf++;
+    }
+    // Append new if not found
+    if (itf == sFrequentlyUsed.end())
+    {
+        // Insert before others with count == 1
+        while (itf != sFrequentlyUsed.begin())
+        {
+            auto prior = itf;
+            prior--;
+            if (prior->second > 1)
+                break;
+            itf = prior;
+        }
+        sFrequentlyUsed.insert(itf, std::make_pair(emoji, 1));
+    }
+}
+
+// static
+void LLFloaterEmojiPicker::loadState()
+{
+    if (!sStateFileName.empty())
+        return; // Already loaded
+
+    sStateFileName = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "emoji_floater_state.xml");
+
+    llifstream file;
+    file.open(sStateFileName.c_str());
+    if (!file.is_open())
+    {
+        LL_WARNS() << "Emoji floater state file is missing or inaccessible: " << sStateFileName << LL_ENDL;
+        return;
+    }
+
+    LLSD state;
+    LLSDSerialize::fromXML(state, file);
+    if (state.isUndefined())
+    {
+        LL_WARNS() << "Emoji floater state file is missing or ill-formed: " << sStateFileName << LL_ENDL;
+        return;
+    }
+
+    // Load and parse sRecentlyUsed
+    std::string recentlyUsed = state[sKeyRecentlyUsed];
+    std::vector<std::string> rtokens = LLStringUtil::getTokens(recentlyUsed, ",");
+    int maxCountR = 20;
+    for (const std::string& token : rtokens)
+    {
+        llwchar emoji = (llwchar)atoi(token.c_str());
+        if (std::find(sRecentlyUsed.begin(), sRecentlyUsed.end(), emoji) == sRecentlyUsed.end())
+        {
+            sRecentlyUsed.push_back(emoji);
+            if (!--maxCountR)
+                break;
+        }
+    }
+
+    // Load and parse sFrequentlyUsed
+    std::string frequentlyUsed = state[sKeyFrequentlyUsed];
+    std::vector<std::string> ftokens = LLStringUtil::getTokens(frequentlyUsed, ",");
+    int maxCountF = 20;
+    for (const std::string& token : ftokens)
+    {
+        std::vector<std::string> pair = LLStringUtil::getTokens(token, ":");
+        if (pair.size() == 2)
+        {
+            llwchar emoji = (llwchar)atoi(pair[0].c_str());
+            if (emoji)
+            {
+                U32 count = atoi(pair[1].c_str());
+                auto it = std::find_if(sFrequentlyUsed.begin(), sFrequentlyUsed.end(),
+                    [emoji](std::pair<llwchar, U32>& it) { return it.first == emoji; });
+                if (it != sFrequentlyUsed.end())
+                {
+                    it->second += count;
+                }
+                else
+                {
+                    sFrequentlyUsed.push_back(std::make_pair(emoji, count));
+                    if (!--maxCountF)
+                        break;
+                }
+            }
+        }
+    }
+
+    // Normalize by minimum
+    if (!sFrequentlyUsed.empty())
+    {
+        U32 delta = sFrequentlyUsed.back().second - 1;
+        for (auto& it : sFrequentlyUsed)
+        {
+            it.second = std::max((U32)0, it.second - delta);
+        }
+    }
+}
+
+// static
+void LLFloaterEmojiPicker::saveState()
+{
+    if (sStateFileName.empty())
+        return; // Not loaded
+
+    if (LLAppViewer::instance()->isSecondInstance())
+        return; // Not allowed
+
+    LLSD state = LLSD::emptyMap();
+
+    if (!sRecentlyUsed.empty())
+    {
+        U32 maxCount = 20;
+        std::string recentlyUsed;
+        for (llwchar emoji : sRecentlyUsed)
+        {
+            if (!recentlyUsed.empty())
+                recentlyUsed += ",";
+            char buffer[32];
+            sprintf(buffer, "%u", (U32)emoji);
+            recentlyUsed += buffer;
+            if (!--maxCount)
+                break;
+        }
+        state[sKeyRecentlyUsed] = recentlyUsed;
+    }
+
+    if (!sFrequentlyUsed.empty())
+    {
+        U32 maxCount = 20;
+        std::string frequentlyUsed;
+        for (auto& it : sFrequentlyUsed)
+        {
+            if (!frequentlyUsed.empty())
+                frequentlyUsed += ",";
+            char buffer[32];
+            sprintf(buffer, "%u:%u", (U32)it.first, (U32)it.second);
+            frequentlyUsed += buffer;
+            if (!--maxCount)
+                break;
+        }
+        state[sKeyFrequentlyUsed] = frequentlyUsed;
+    }
+
+    llofstream stream(sStateFileName.c_str());
+    LLSDSerialize::toPrettyXML(state, stream);
+}
diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d0402ca8399963715a181d4a27668091434b1ec
--- /dev/null
+++ b/indra/newview/llfloateremojipicker.h
@@ -0,0 +1,122 @@
+/**
+ * @file llfloateremojipicker.h
+ * @brief Header file for llfloateremojipicker
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLFLOATEREMOJIPICKER_H
+#define LLFLOATEREMOJIPICKER_H
+
+#include "llfloater.h"
+
+class LLEmojiGridRow;
+class LLEmojiGridIcon;
+struct LLEmojiDescriptor;
+struct LLEmojiSearchResult;
+
+class LLFloaterEmojiPicker : public LLFloater
+{
+    using super = LLFloater;
+
+public:
+    // The callback function will be called with an emoji char.
+    typedef boost::function<void (llwchar)> pick_callback_t;
+    typedef boost::function<void ()> close_callback_t;
+
+    LLFloaterEmojiPicker(const LLSD& key);
+
+    virtual	BOOL postBuild() override;
+    virtual void dirtyRect() override;
+    virtual void goneFromFront() override;
+
+    void hideFloater() const;
+
+    static std::list<llwchar>& getRecentlyUsed();
+    static void onEmojiUsed(llwchar emoji);
+
+    static void loadState();
+    static void saveState();
+
+private:
+    void initialize();
+    void fillGroups();
+    void fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);
+    void fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats);
+    void fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index);
+    void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji);
+    void resizeGroupButtons();
+    void selectEmojiGroup(U32 index);
+    void fillEmojis(bool fromResize = false);
+    void fillEmojisCategory(const std::vector<LLEmojiSearchResult>& emojis,
+        const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+        const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg);
+    void createEmojiIcon(const LLEmojiSearchResult& emoji,
+        const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+        const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg,
+        LLEmojiGridRow*& row, int& icon_index);
+    void showPreview(bool show);
+
+    void onGroupButtonClick(LLUICtrl* ctrl);
+    void onGroupButtonMouseEnter(LLUICtrl* ctrl);
+    void onGroupButtonMouseLeave(LLUICtrl* ctrl);
+    void onEmojiMouseEnter(LLUICtrl* ctrl);
+    void onEmojiMouseLeave(LLUICtrl* ctrl);
+    void onEmojiMouseDown(LLUICtrl* ctrl);
+    void onEmojiMouseUp(LLUICtrl* ctrl);
+
+    void selectFocusedIcon();
+    bool moveFocusedIconUp();
+    bool moveFocusedIconDown();
+    bool moveFocusedIconPrev();
+    bool moveFocusedIconNext();
+
+    void selectGridIcon(LLEmojiGridIcon* icon);
+    void unselectGridIcon(LLEmojiGridIcon* icon);
+
+    void onOpen(const LLSD& key) override;
+    virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
+
+    class LLPanel* mGroups { nullptr };
+    class LLPanel* mBadge { nullptr };
+    class LLScrollContainer* mEmojiScroll { nullptr };
+    class LLScrollingPanelList* mEmojiGrid { nullptr };
+    class LLEmojiPreviewPanel* mPreview { nullptr };
+    class LLTextBox* mDummy { nullptr };
+
+    std::vector<S32> mFilteredEmojiGroups;
+    std::vector<std::map<std::string, std::vector<LLEmojiSearchResult>>> mFilteredEmojis;
+    std::vector<class LLButton*> mGroupButtons;
+
+    std::string mHint;
+    std::string mFilterPattern;
+    U32 mSelectedGroupIndex { 0 };
+    S32 mRecentMaxIcons { 0 };
+    S32 mFocusedIconRow { 0 };
+    S32 mFocusedIconCol { 0 };
+    LLEmojiGridIcon* mFocusedIcon { nullptr };
+    LLEmojiGridIcon* mHoveredIcon { nullptr };
+
+    U64 mRecentReturnPressedMs { 0 };
+};
+
+#endif
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index bb4cc9bca3b93fc0f613a742fbedefbab6bdb15e..40bdf14deba971355d13ffe587c72d3ae356ced0 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -130,11 +130,12 @@ BOOL LLFloaterIMNearbyChat::postBuild()
 	mInputEditor->setKeystrokeCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxKeystroke, this));
 	mInputEditor->setFocusLostCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusLost, this));
 	mInputEditor->setFocusReceivedCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusReceived, this));
-	mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle"));
+	std::string nearbyChatTitle(LLTrans::getString("NearbyChatTitle"));
+	mInputEditor->setLabel(nearbyChatTitle);
 
 	// Title must be defined BEFORE call to addConversationListItem() because
 	// it is used to show the item's name in the conversations list
-	setTitle(LLTrans::getString("NearbyChatTitle"));
+	setTitle(nearbyChatTitle);
 
 	// obsolete, but may be needed for backward compatibility?
 	gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", LLControlVariable::PERSIST_NONDFT);
@@ -590,6 +591,8 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type )
 			S32 channel = 0;
 			stripChannelNumber(text, &channel);
 			
+			updateUsedEmojis(text);
+
 			std::string utf8text = wstring_to_utf8str(text);
 			// Try to trigger a gesture, if not chat to a script.
 			std::string utf8_revised_text;
diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp
index ee9dc35283242de6e6a52c7585dc2ff50d8cec20..ed2a2807b54a55ff65699fc49ab12b65aedbfeb7 100644
--- a/indra/newview/llfloaterimsession.cpp
+++ b/indra/newview/llfloaterimsession.cpp
@@ -249,6 +249,8 @@ void LLFloaterIMSession::sendMsgFromInputEditor()
 			LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
 			if(!text.empty())
 			{
+				updateUsedEmojis(text);
+
 				// Truncate and convert to UTF8 for transport
 				std::string utf8_text = wstring_to_utf8str(text);
 
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index 0b0dce29fba32e0f8a06c3fc465547981b805a14..24cc398f3b06ab2521d3e278220e0fff30c0a079 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -33,18 +33,21 @@
 #include "llagentcamera.h"
 #include "llavataractions.h"
 #include "llavatariconctrl.h"
-#include "llgroupiconctrl.h"
 #include "llchatentry.h"
 #include "llchathistory.h"
 #include "llchiclet.h"
 #include "llchicletbar.h"
 #include "lldraghandle.h"
+#include "llemojidictionary.h"
 #include "llfloaterreg.h"
+#include "llfloateremojipicker.h"
 #include "llfloaterimsession.h"
 #include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container
+#include "llfloaterimnearbychat.h"
+#include "llgroupiconctrl.h"
 #include "lllayoutstack.h"
+#include "llpanelemojicomplete.h"
 #include "lltoolbarview.h"
-#include "llfloaterimnearbychat.h"
 
 const F32 REFRESH_INTERVAL = 1.0f;
 const std::string ICN_GROUP("group_chat_icon");
@@ -56,7 +59,7 @@ void cb_group_do_nothing()
 }
 
 LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)
-:	LLTransientDockableFloater(NULL, false, session_id),
+:	super(NULL, false, session_id),
 	mIsP2PChat(false),
 	mExpandCollapseBtn(NULL),
 	mTearOffBtn(NULL),
@@ -75,7 +78,7 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)
 	mInputPanels(NULL),
 	mChatLayoutPanelHeight(0)
 {
-    setAutoFocus(FALSE);
+	setAutoFocus(FALSE);
 	mSession = LLIMModel::getInstance()->findIMSession(mSessionID);
 
 	mCommitCallbackRegistrar.add("IMSession.Menu.Action",
@@ -88,12 +91,12 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)
 			boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemEnable,  this, _2));
 
 	// Right click menu handling
-    mEnableCallbackRegistrar.add("Avatar.CheckItem",  boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem,	this, _2));
-    mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2));
-    mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2));
-    mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing));
+	mEnableCallbackRegistrar.add("Avatar.CheckItem",  boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem,	this, _2));
+	mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2));
+	mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2));
+	mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing));
 
-    mMinFloaterHeight = getMinHeight();
+	mMinFloaterHeight = getMinHeight();
 }
 
 LLFloaterIMSessionTab::~LLFloaterIMSessionTab()
@@ -101,7 +104,7 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab()
 	delete mRefreshTimer;
 }
 
-//static
+// static
 LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uuid)
 {
 	LLFloaterIMSessionTab* conv;
@@ -118,7 +121,7 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uui
 	return conv;
 };
 
-//static
+// static
 LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid)
 {
 	LLFloaterIMSessionTab* conv;
@@ -134,14 +137,16 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid
 	}
 
 	return conv;
+
 };
 
+// virtual
 void LLFloaterIMSessionTab::setVisible(BOOL visible)
 {
-	if(visible && !mHasVisibleBeenInitialized)
+	if (visible && !mHasVisibleBeenInitialized)
 	{
 		mHasVisibleBeenInitialized = true;
-		if(!gAgentCamera.cameraMouselook())
+		if (!gAgentCamera.cameraMouselook())
 		{
 			LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")->setVisible(true);
 		}
@@ -155,27 +160,26 @@ void LLFloaterIMSessionTab::setVisible(BOOL visible)
 		mInputButtonPanel->setVisible(isTornOff());
 	}
 
-	LLTransientDockableFloater::setVisible(visible);
+	super::setVisible(visible);
 }
 
-/*virtual*/
+// virtual
 void LLFloaterIMSessionTab::setFocus(BOOL focus)
 {
-	LLTransientDockableFloater::setFocus(focus);
+	super::setFocus(focus);
 
-    //Redirect focus to input editor
-    if (focus)
+	// Redirect focus to input editor
+	if (focus)
 	{
-    	updateMessages();
+		updateMessages();
 
-        if (mInputEditor)
-        {
-    	    mInputEditor->setFocus(TRUE);
-        }
+		if (mInputEditor)
+		{
+			mInputEditor->setFocus(TRUE);
+		}
 	}
 }
 
-
 void LLFloaterIMSessionTab::addToHost(const LLUUID& session_id)
 {
 	if ((session_id.notNull() && !gIMMgr->hasSession(session_id))
@@ -220,42 +224,60 @@ void LLFloaterIMSessionTab::assignResizeLimits()
 {
 	bool is_participants_pane_collapsed = mParticipantListPanel->isCollapsed();
 
-    // disable a layoutstack's functionality when participant list panel is collapsed
+	// disable a layoutstack's functionality when participant list panel is collapsed
 	mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed);
 
-    S32 participants_pane_target_width = is_participants_pane_collapsed?
-    		0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing());
+	S32 participants_pane_target_width = is_participants_pane_collapsed?
+			0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing());
 
-    S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth;
+	S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth;
 
 	setResizeLimits(new_min_width, getMinHeight());
 
 	this->mParticipantListAndHistoryStack->updateLayout();
 }
 
+// virtual
 BOOL LLFloaterIMSessionTab::postBuild()
 {
 	BOOL result;
 
 	mBodyStack = getChild<LLLayoutStack>("main_stack");
-    mParticipantListAndHistoryStack = getChild<LLLayoutStack>("im_panels");
+	mParticipantListAndHistoryStack = getChild<LLLayoutStack>("im_panels");
 
 	mCloseBtn = getChild<LLButton>("close_btn");
-	mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this));
+	mCloseBtn->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickClose(this); });
 
 	mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn");
-	mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onSlide, this));
+	mExpandCollapseBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onSlide(this); });
 
 	mExpandCollapseLineBtn = getChild<LLButton>("minz_btn");
-	mExpandCollapseLineBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onCollapseToLine, this));
+	mExpandCollapseLineBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onCollapseToLine(this); });
 
 	mTearOffBtn = getChild<LLButton>("tear_off_btn");
 	mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this));
 
+	mEmojiRecentPanelToggleBtn = getChild<LLButton>("emoji_recent_panel_toggle_btn");
+	mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(); });
+
+	mEmojiRecentPanel = getChild<LLLayoutPanel>("emoji_recent_layout_panel");
+	mEmojiRecentPanel->setVisible(false);
+
+	mEmojiRecentEmptyText = getChild<LLTextBox>("emoji_recent_empty_text");
+	mEmojiRecentEmptyText->setToolTip(mEmojiRecentEmptyText->getText());
+	mEmojiRecentEmptyText->setVisible(false);
+
+	mEmojiRecentIconsCtrl = getChild<LLPanelEmojiComplete>("emoji_recent_icons_ctrl");
+	mEmojiRecentIconsCtrl->setCommitCallback([this](LLUICtrl*, const LLSD& value) { onRecentEmojiPicked(value); });
+	mEmojiRecentIconsCtrl->setVisible(false);
+
+	mEmojiPickerShowBtn = getChild<LLButton>("emoji_picker_show_btn");
+	mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); });
+
 	mGearBtn = getChild<LLButton>("gear_btn");
-    mAddBtn = getChild<LLButton>("add_btn");
+	mAddBtn = getChild<LLButton>("add_btn");
 	mVoiceButton = getChild<LLButton>("voice_call_btn");
-    
+
 	mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel");
 	mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder");
 
@@ -307,17 +329,17 @@ BOOL LLFloaterIMSessionTab::postBuild()
 
 	// Create the root using an ad-hoc base item
 	LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel);
-    LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
-    p.rect = LLRect(0, 0, getRect().getWidth(), 0);
-    p.parent_panel = mParticipantListPanel;
-    p.listener = base_item;
-    p.view_model = &mConversationViewModel;
-    p.root = NULL;
-    p.use_ellipses = true;
-    p.options_menu = "menu_conversation.xml";
-    p.name = "root";
+	LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
+	p.rect = LLRect(0, 0, getRect().getWidth(), 0);
+	p.parent_panel = mParticipantListPanel;
+	p.listener = base_item;
+	p.view_model = &mConversationViewModel;
+	p.root = NULL;
+	p.use_ellipses = true;
+	p.options_menu = "menu_conversation.xml";
+	p.name = "root";
 	mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
-    mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
+	mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
 	mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar);
 	// Attach that root to the scroller
 	mScroller->addChild(mConversationsRoot);
@@ -357,6 +379,7 @@ LLParticipantList* LLFloaterIMSessionTab::getParticipantList()
 	return dynamic_cast<LLParticipantList*>(LLFloaterIMContainer::getInstance()->getSessionModel(mSessionID));
 }
 
+// virtual
 void LLFloaterIMSessionTab::draw()
 {
 	if (mRefreshTimer->hasExpired())
@@ -381,23 +404,24 @@ void LLFloaterIMSessionTab::draw()
 		mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL);
 	}
 
-	LLTransientDockableFloater::draw();
+	super::draw();
 }
 
 void LLFloaterIMSessionTab::enableDisableCallBtn()
 {
-    if (LLVoiceClient::instanceExists() && mVoiceButton)
-    {
-        mVoiceButton->setEnabled(
-            mSessionID.notNull()
-            && mSession
-            && mSession->mSessionInitialized
-            && LLVoiceClient::getInstance()->voiceEnabled()
-            && LLVoiceClient::getInstance()->isVoiceWorking()
-            && mSession->mCallBackEnabled);
-    }
+	if (LLVoiceClient::instanceExists() && mVoiceButton)
+	{
+		mVoiceButton->setEnabled(
+			mSessionID.notNull()
+			&& mSession
+			&& mSession->mSessionInitialized
+			&& LLVoiceClient::getInstance()->voiceEnabled()
+			&& LLVoiceClient::getInstance()->isVoiceWorking()
+			&& mSession->mCallBackEnabled);
+	}
 }
 
+// virtual
 void LLFloaterIMSessionTab::onFocusReceived()
 {
 	setBackgroundOpaque(true);
@@ -407,13 +431,14 @@ void LLFloaterIMSessionTab::onFocusReceived()
 		LLIMModel::instance().sendNoUnreadMessages(mSessionID);
 	}
 
-	LLTransientDockableFloater::onFocusReceived();
+	super::onFocusReceived();
 }
 
+// virtual
 void LLFloaterIMSessionTab::onFocusLost()
 {
 	setBackgroundOpaque(false);
-	LLTransientDockableFloater::onFocusLost();
+	super::onFocusLost();
 }
 
 void LLFloaterIMSessionTab::onInputEditorClicked()
@@ -426,53 +451,130 @@ void LLFloaterIMSessionTab::onInputEditorClicked()
 	gToolBarView->flashCommand(LLCommandId("chat"), false);
 }
 
+void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked()
+{
+    BOOL show = mEmojiRecentPanel->getVisible() ? FALSE : TRUE;
+    if (show)
+    {
+        initEmojiRecentPanel();
+    }
+
+    mEmojiRecentPanel->setVisible(show);
+    mInputEditor->setFocus(TRUE);
+}
+
+void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked()
+{
+    mInputEditor->setFocus(TRUE);
+    mInputEditor->showEmojiHelper();
+}
+
+void LLFloaterIMSessionTab::initEmojiRecentPanel()
+{
+    std::list<llwchar>& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed();
+    if (recentlyUsed.empty())
+    {
+        mEmojiRecentEmptyText->setVisible(TRUE);
+        mEmojiRecentIconsCtrl->setVisible(FALSE);
+    }
+    else
+    {
+        LLWString emojis;
+        for (llwchar emoji : recentlyUsed)
+        {
+            emojis += emoji;
+        }
+        mEmojiRecentIconsCtrl->setEmojis(emojis);
+        mEmojiRecentEmptyText->setVisible(FALSE);
+        mEmojiRecentIconsCtrl->setVisible(TRUE);
+    }
+}
+
+void LLFloaterIMSessionTab::onRecentEmojiPicked(const LLSD& value)
+{
+	LLSD::String str = value.asString();
+	if (str.size())
+	{
+		LLWString wstr = utf8string_to_wstring(str);
+		if (wstr.size())
+		{
+			llwchar emoji = wstr[0];
+			mInputEditor->insertEmoji(emoji);
+		}
+	}
+}
+
+void LLFloaterIMSessionTab::closeFloater(bool app_quitting)
+{
+	LLFloaterEmojiPicker::saveState();
+	super::closeFloater(app_quitting);
+}
+
 std::string LLFloaterIMSessionTab::appendTime()
 {
-	time_t utc_time;
-	utc_time = time_corrected();
-	std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:["
-		+LLTrans::getString("TimeMin")+"]";
+	std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:"
+						  "[" + LLTrans::getString("TimeMin") + "]";
 
 	LLSD substitution;
-
-	substitution["datetime"] = (S32) utc_time;
-	LLStringUtil::format (timeStr, substitution);
+	substitution["datetime"] = (S32)time_corrected();
+	LLStringUtil::format(timeStr, substitution);
 
 	return timeStr;
 }
 
-void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD &args)
+void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD& args)
 {
+	if (chat.mMuted || !mChatHistory)
+		return;
 
 	// Update the participant activity time
 	LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance();
 	if (im_box)
 	{
-		im_box->setTimeNow(mSessionID,chat.mFromID);
+		im_box->setTimeNow(mSessionID, chat.mFromID);
 	}
 	
-
 	LLChat& tmp_chat = const_cast<LLChat&>(chat);
 
-	if(tmp_chat.mTimeStr.empty())
+	if (tmp_chat.mTimeStr.empty())
 		tmp_chat.mTimeStr = appendTime();
 
-	if (!chat.mMuted)
-	{
-		tmp_chat.mFromName = chat.mFromName;
-		LLSD chat_args;
-		if (args) chat_args = args;
-		chat_args["use_plain_text_chat_history"] =
-				gSavedSettings.getBOOL("PlainTextChatHistory");
-		chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime");
-		chat_args["show_names_for_p2p_conv"] =
-				!mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv");
-
-		if (mChatHistory)
-		{
-			mChatHistory->appendMessage(chat, chat_args);
-		}
-	}
+	tmp_chat.mFromName = chat.mFromName;
+
+	LLSD chat_args = args;
+	chat_args["use_plain_text_chat_history"] =
+			gSavedSettings.getBOOL("PlainTextChatHistory");
+	chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime");
+	chat_args["show_names_for_p2p_conv"] = !mIsP2PChat ||
+			gSavedSettings.getBOOL("IMShowNamesForP2PConv");
+
+	mChatHistory->appendMessage(chat, chat_args);
+}
+
+void LLFloaterIMSessionTab::updateUsedEmojis(LLWString text)
+{
+    LLEmojiDictionary* dictionary = LLEmojiDictionary::getInstance();
+    llassert_always(dictionary);
+
+    bool emojiSent = false;
+    for (llwchar& c : text)
+    {
+        if (dictionary->isEmoji(c))
+        {
+            LLFloaterEmojiPicker::onEmojiUsed(c);
+            emojiSent = true;
+        }
+    }
+
+    if (!emojiSent)
+        return;
+
+    LLFloaterEmojiPicker::saveState();
+
+    if (mEmojiRecentPanel->getVisible())
+    {
+        initEmojiRecentPanel();
+    }
 }
 
 static LLTrace::BlockTimerStatHandle FTM_BUILD_CONVERSATION_VIEW_PARTICIPANT("Build Conversation View");
@@ -502,10 +604,10 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant()
 	while (current_participant_model != end_participant_model)
 	{
 		LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model);
-        if (participant_model)
-        {
-            addConversationViewParticipant(participant_model);
-        }
+		if (participant_model)
+		{
+			addConversationViewParticipant(participant_model);
+		}
 		current_participant_model++;
 	}
 }
@@ -525,10 +627,10 @@ void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* p
 	// If not already present, create the participant view and attach it to the root, otherwise, just refresh it
 	if (widget)
 	{
-        if (update_view)
-        {
-            updateConversationViewParticipant(uuid); // overkill?
-        }
+		if (update_view)
+		{
+			updateConversationViewParticipant(uuid); // overkill?
+		}
 	}
 	else
 	{
@@ -578,11 +680,11 @@ void LLFloaterIMSessionTab::refreshConversation()
 		{
 			participants_uuids.push_back(widget_it->first);
 		}
-        if (widget_it->second->getViewModelItem())
-        {
-            widget_it->second->refresh();
-            widget_it->second->setVisible(TRUE);
-        }
+		if (widget_it->second->getViewModelItem())
+		{
+			widget_it->second->refresh();
+			widget_it->second->setVisible(TRUE);
+		}
 		++widget_it;
 	}
 	if (is_ad_hoc || mIsP2PChat)
@@ -638,7 +740,7 @@ void LLFloaterIMSessionTab::refreshConversation()
 // Copied from LLFloaterIMContainer::createConversationViewParticipant(). Refactor opportunity!
 LLConversationViewParticipant* LLFloaterIMSessionTab::createConversationViewParticipant(LLConversationItem* item)
 {
-    LLRect panel_rect = mParticipantListPanel->getRect();
+	LLRect panel_rect = mParticipantListPanel->getRect();
 	
 	LLConversationViewParticipant::Params params;
 	params.name = item->getDisplayName();
@@ -766,7 +868,7 @@ void LLFloaterIMSessionTab::hideAllStandardButtons()
 void LLFloaterIMSessionTab::updateHeaderAndToolbar()
 {
 	// prevent start conversation before its container
-    LLFloaterIMContainer::getInstance();
+	LLFloaterIMContainer::getInstance();
 
 	bool is_not_torn_off = !checkIfTornOff();
 	if (is_not_torn_off)
@@ -783,12 +885,12 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()
 			&& !mIsP2PChat;
 
 	mParticipantListAndHistoryStack->collapsePanel(mParticipantListPanel, !is_participant_list_visible);
-    mParticipantListPanel->setVisible(is_participant_list_visible);
+	mParticipantListPanel->setVisible(is_participant_list_visible);
 
 	// Display collapse image (<<) if the floater is hosted
 	// or if it is torn off but has an open control panel.
 	bool is_expanded = is_not_torn_off || is_participant_list_visible;
-    
+	
 	mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon"));
 	mExpandCollapseBtn->setToolTip(
 			is_not_torn_off?
@@ -817,10 +919,10 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()
  
 void LLFloaterIMSessionTab::forceReshape()
 {
-    LLRect floater_rect = getRect();
-    reshape(llmax(floater_rect.getWidth(), this->getMinWidth()),
-    		llmax(floater_rect.getHeight(), this->getMinHeight()),
-    		true);
+	LLRect floater_rect = getRect();
+	reshape(llmax(floater_rect.getWidth(), this->getMinWidth()),
+			llmax(floater_rect.getHeight(), this->getMinHeight()),
+			true);
 }
 
 
@@ -846,7 +948,7 @@ void LLFloaterIMSessionTab::processChatHistoryStyleUpdate(bool clean_messages/*
 	LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
 	if (nearby_chat)
 	{
-             nearby_chat->reloadMessages(clean_messages);
+			 nearby_chat->reloadMessages(clean_messages);
 	}
 }
 
@@ -892,15 +994,15 @@ void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self)
 	{
 		if (!self->mIsP2PChat)
 		{
-            // The state must toggle the collapsed state of the panel
-           should_be_expanded = self->mParticipantListPanel->isCollapsed();
+			// The state must toggle the collapsed state of the panel
+			should_be_expanded = self->mParticipantListPanel->isCollapsed();
 
 			// Update the expand/collapse flag of the participant list panel and save it
-            gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded);
-            self->mIsParticipantListExpanded = should_be_expanded;
-            
-            // Refresh for immediate feedback
-            self->refreshConversation();
+			gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded);
+			self->mIsParticipantListExpanded = should_be_expanded;
+
+			// Refresh for immediate feedback
+			self->refreshConversation();
 		}
 	}
 
@@ -937,12 +1039,12 @@ void LLFloaterIMSessionTab::reshapeFloater(bool collapse)
 			+ mChatLayoutPanel->getRect().getHeight() - mChatLayoutPanelHeight + 2;
 		floater_rect.mTop -= height;
 
-        setResizeLimits(getMinWidth(), floater_rect.getHeight());
+		setResizeLimits(getMinWidth(), floater_rect.getHeight());
 	}
 	else
 	{
 		floater_rect.mTop = floater_rect.mBottom + mFloaterHeight;
-        setResizeLimits(getMinWidth(), mMinFloaterHeight);
+		setResizeLimits(getMinWidth(), mMinFloaterHeight);
 	}
 
 	enableResizeCtrls(true, true, !collapse);
@@ -967,7 +1069,7 @@ void LLFloaterIMSessionTab::restoreFloater()
 		setShape(floater_rect, true);
 		mBodyStack->updateLayout();
 		mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon"));
-        setResizeLimits(getMinWidth(), mMinFloaterHeight);
+		setResizeLimits(getMinWidth(), mMinFloaterHeight);
 		setMessagePaneExpanded(true);
 		saveCollapsedState();
 		mInputEditor->enableSingleLineMode(false);
@@ -995,8 +1097,8 @@ void LLFloaterIMSessionTab::onTearOffClicked()
 {
 	restoreFloater();
 	setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE);
-    mSaveRect = isTornOff();
-    initRectControl();
+	mSaveRect = isTornOff();
+	initRectControl();
 	LLFloater::onClickTearOff(this);
 	LLFloaterIMContainer* container = LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container");
 
@@ -1086,8 +1188,8 @@ bool LLFloaterIMSessionTab::checkIfTornOff()
 void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata)
 {
 	// Get the list of selected items in the tab
-    std::string command = userdata.asString();
-    uuid_vec_t selected_uuids;
+	std::string command = userdata.asString();
+	uuid_vec_t selected_uuids;
 	getSelectedUUIDs(selected_uuids);
 		
 	// Perform the command (IM, profile, etc...) on the list using the general conversation container method
@@ -1099,8 +1201,8 @@ void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata)
 bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata)
 {
 	// Get the list of selected items in the tab
-    std::string command = userdata.asString();
-    uuid_vec_t selected_uuids;
+	std::string command = userdata.asString();
+	uuid_vec_t selected_uuids;
 	getSelectedUUIDs(selected_uuids);
 	
 	// Perform the item enable test on the list using the general conversation container method
@@ -1111,8 +1213,8 @@ bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata)
 bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata)
 {
 	// Get the list of selected items in the tab
-    std::string command = userdata.asString();
-    uuid_vec_t selected_uuids;
+	std::string command = userdata.asString();
+	uuid_vec_t selected_uuids;
 	getSelectedUUIDs(selected_uuids);
 	
 	// Perform the item check on the list using the general conversation container method
@@ -1122,19 +1224,19 @@ bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata)
 
 void LLFloaterIMSessionTab::getSelectedUUIDs(uuid_vec_t& selected_uuids)
 {
-    const std::set<LLFolderViewItem*> selected_items = mConversationsRoot->getSelectionList();
+	const std::set<LLFolderViewItem*> selected_items = mConversationsRoot->getSelectionList();
 	
-    std::set<LLFolderViewItem*>::const_iterator it = selected_items.begin();
-    const std::set<LLFolderViewItem*>::const_iterator it_end = selected_items.end();
+	std::set<LLFolderViewItem*>::const_iterator it = selected_items.begin();
+	const std::set<LLFolderViewItem*>::const_iterator it_end = selected_items.end();
 	
-    for (; it != it_end; ++it)
-    {
-        LLConversationItem* conversation_item = static_cast<LLConversationItem *>((*it)->getViewModelItem());
-        if (conversation_item)
-        {
-            selected_uuids.push_back(conversation_item->getUUID());
-        }
-    }
+	for (; it != it_end; ++it)
+	{
+		LLConversationItem* conversation_item = static_cast<LLConversationItem *>((*it)->getViewModelItem());
+		if (conversation_item)
+		{
+			selected_uuids.push_back(conversation_item->getUUID());
+		}
+	}
 }
 
 LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem()
@@ -1142,8 +1244,8 @@ LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem()
 	LLConversationItem *conversationItem = NULL;
 
 	if(mConversationsRoot && 
-        mConversationsRoot->getCurSelectedItem() && 
-        mConversationsRoot->getCurSelectedItem()->getViewModelItem())
+		mConversationsRoot->getCurSelectedItem() && 
+		mConversationsRoot->getCurSelectedItem()->getViewModelItem())
 	{
 		conversationItem = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem()) ;
 	}
diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h
index d478922617a5e85f144adacc32c1adbec15925a2..cc985b275320f15de7c7fe9ed5d1e176830bd59b 100644
--- a/indra/newview/llfloaterimsessiontab.h
+++ b/indra/newview/llfloaterimsessiontab.h
@@ -41,10 +41,12 @@
 class LLPanelChatControlPanel;
 class LLChatEntry;
 class LLChatHistory;
+class LLPanelEmojiComplete;
 
 class LLFloaterIMSessionTab
 	: public LLTransientDockableFloater
 {
+	using super = LLTransientDockableFloater;
 
 public:
 	LOG_CLASS(LLFloaterIMSessionTab);
@@ -68,9 +70,8 @@ class LLFloaterIMSessionTab
 	bool isHostAttached() {return mIsHostAttached;}
 	void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;}
 
-    static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid);
-    static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid);
-
+	static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid);
+	static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid);
 
 	bool isNearbyChat() {return mIsNearbyChat;}
 
@@ -80,6 +81,7 @@ class LLFloaterIMSessionTab
 	/*virtual*/ void draw();
 	/*virtual*/ void setVisible(BOOL visible);
 	/*virtual*/ void setFocus(BOOL focus);
+	/*virtual*/ void closeFloater(bool app_quitting = false);
 	
 	// Handle the left hand participant list widgets
 	void addConversationViewParticipant(LLConversationItem* item, bool update_view = true);
@@ -136,15 +138,17 @@ class LLFloaterIMSessionTab
 	virtual void enableDisableCallBtn();
 
 	// process focus events to set a currently active session
-	/* virtual */ void onFocusLost();
 	/* virtual */ void onFocusReceived();
+	/* virtual */ void onFocusLost();
 
 	// prepare chat's params and out one message to chatHistory
-	void appendMessage(const LLChat& chat, const LLSD &args = 0);
+	void appendMessage(const LLChat& chat, const LLSD& args = LLSD());
 
 	std::string appendTime();
 	void assignResizeLimits();
 
+	void updateUsedEmojis(LLWString text);
+
 	S32  mFloaterExtraWidth;
 
 	bool mIsNearbyChat;
@@ -152,8 +156,7 @@ class LLFloaterIMSessionTab
 
 	bool mMessagePaneExpanded;
 	bool mIsParticipantListExpanded;
-    S32 mMinFloaterHeight;
-
+	S32 mMinFloaterHeight;
 
 	LLIMModel::LLIMSession* mSession;
 
@@ -168,32 +171,37 @@ class LLFloaterIMSessionTab
 	LLLayoutPanel* mContentPanel;
 	LLLayoutPanel* mToolbarPanel;
 	LLLayoutPanel* mInputButtonPanel;
+	LLLayoutPanel* mEmojiRecentPanel;
+	LLTextBox* mEmojiRecentEmptyText;
+	LLPanelEmojiComplete* mEmojiRecentIconsCtrl;
 	LLParticipantList* getParticipantList();
 	conversations_widgets_map mConversationsWidgets;
 	LLConversationViewModel mConversationViewModel;
 	LLFolderView* mConversationsRoot;
 	LLScrollContainer* mScroller;
 
-    LLChatHistory* mChatHistory;
+	LLChatHistory* mChatHistory;
 	LLChatEntry* mInputEditor;
-	LLLayoutPanel * mChatLayoutPanel;
-	LLLayoutStack * mInputPanels;
+	LLLayoutPanel* mChatLayoutPanel;
+	LLLayoutStack* mInputPanels;
 	
 	LLButton* mExpandCollapseLineBtn;
 	LLButton* mExpandCollapseBtn;
 	LLButton* mTearOffBtn;
+	LLButton* mEmojiRecentPanelToggleBtn;
+	LLButton* mEmojiPickerShowBtn;
 	LLButton* mCloseBtn;
 	LLButton* mGearBtn;
 	LLButton* mAddBtn;
-    LLButton* mVoiceButton;
+	LLButton* mVoiceButton;
 
 private:
 	// Handling selection and contextual menu
-    void doToSelected(const LLSD& userdata);
-    bool enableContextMenuItem(const LLSD& userdata);
-    bool checkContextMenuItem(const LLSD& userdata);
+	void doToSelected(const LLSD& userdata);
+	bool enableContextMenuItem(const LLSD& userdata);
+	bool checkContextMenuItem(const LLSD& userdata);
 	
-    void getSelectedUUIDs(uuid_vec_t& selected_uuids);
+	void getSelectedUUIDs(uuid_vec_t& selected_uuids);
 	
 	/// Refreshes the floater at a constant rate.
 	virtual void refresh() = 0;
@@ -207,9 +215,14 @@ class LLFloaterIMSessionTab
 
 	void onInputEditorClicked();
 
+	void onEmojiRecentPanelToggleBtnClicked();
+	void onEmojiPickerShowBtnClicked();
+	void initEmojiRecentPanel();
+	void onRecentEmojiPicked(const LLSD& value);
+
 	bool checkIfTornOff();
-    bool mIsHostAttached;
-    bool mHasVisibleBeenInitialized;
+	bool mIsHostAttached;
+	bool mHasVisibleBeenInitialized;
 
 	LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called.
 
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 8b335d57d70be036e5d35672714c2f73e7af8bd9..6e8e37621e39e7138006bd368508d936277d00bf 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -86,7 +86,6 @@
 #include "llviewerstats.h"
 #include "llviewertexteditor.h"
 #include "llviewerwindow.h"
-#include "llvlcomposition.h"
 #include "lltrans.h"
 #include "llagentui.h"
 #include "llmeshrepository.h"
@@ -100,7 +99,6 @@
 #include "llavatarnamecache.h"
 #include "llenvironment.h"
 
-const S32 TERRAIN_TEXTURE_COUNT = 4;
 const S32 CORNER_COUNT = 4;
 
 const U32 MAX_LISTED_NAMES = 100;
@@ -340,7 +338,6 @@ void LLFloaterRegionInfo::onRegionChanged()
     }
 }
 
-// static
 void LLFloaterRegionInfo::requestRegionInfo()
 {
 	LLTabContainer* tab = findChild<LLTabContainer>("region_panels");
@@ -605,24 +602,14 @@ LLPanelRegionEnvironment* LLFloaterRegionInfo::getPanelEnvironment()
 	return panel;
 }
 
-enum class TerrainMaterialType
+LLTerrainMaterials::Type material_type_from_ctrl(LLCheckBoxCtrl* ctrl)
 {
-    TEXTURE,
-    PBR_MATERIAL,
-    COUNT
-};
+    return ctrl->get() ? LLTerrainMaterials::Type::PBR : LLTerrainMaterials::Type::TEXTURE;
+}
 
-TerrainMaterialType material_type_from_index(S32 index)
+void material_type_to_ctrl(LLCheckBoxCtrl* ctrl, LLTerrainMaterials::Type new_type)
 {
-    if (index == 0)
-    {
-        return TerrainMaterialType::TEXTURE;
-    }
-    if (index == 1)
-    {
-        return TerrainMaterialType::PBR_MATERIAL;
-    }
-    return TerrainMaterialType::COUNT;
+    ctrl->set(new_type == LLTerrainMaterials::Type::PBR);
 }
 
 // static
@@ -1327,19 +1314,16 @@ void LLPanelRegionDebugInfo::onClickDebugConsole(void* data)
 
 BOOL LLPanelRegionTerrainInfo::validateTextureSizes()
 {
-    // *TODO: Don't early-exit in PBR material terrain editing mode, and
-    // instead do some reasonable checks that the PBR material is compatible
-    // with the terrain rendering pipeline. Err on the side of permissive.
-    LLComboBox* material_type_ctrl = getChild<LLComboBox>("terrain_material_type");
+	LLCheckBoxCtrl* material_type_ctrl = getChild<LLCheckBoxCtrl>("terrain_material_type");
     if (material_type_ctrl)
     {
-        const TerrainMaterialType material_type = material_type_from_index(material_type_ctrl->getCurrentIndex());
-        const bool is_material_selected = material_type == TerrainMaterialType::PBR_MATERIAL;
+        const LLTerrainMaterials::Type material_type = material_type_from_ctrl(material_type_ctrl);
+        const bool is_material_selected = material_type == LLTerrainMaterials::Type::PBR;
         if (is_material_selected) { return TRUE; }
     }
 
     static const S32 MAX_TERRAIN_TEXTURE_SIZE = 1024;
-	for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+	for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
 	{
 		std::string buffer;
 		buffer = llformat("texture_detail_%d", i);
@@ -1401,6 +1385,21 @@ BOOL LLPanelRegionTerrainInfo::validateTextureHeights()
 /////////////////////////////////////////////////////////////////////////////
 // LLPanelRegionTerrainInfo
 /////////////////////////////////////////////////////////////////////////////
+
+LLPanelRegionTerrainInfo::LLPanelRegionTerrainInfo()
+: LLPanelRegionInfo()
+{
+    const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
+    for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+    {
+        mLastSetTextures[i] = default_textures[i];
+    }
+    for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+    {
+        mLastSetMaterials[i] = LLUUID::null;
+    }
+}
+
 // Initialize statics
 
 BOOL LLPanelRegionTerrainInfo::postBuild()
@@ -1415,12 +1414,12 @@ BOOL LLPanelRegionTerrainInfo::postBuild()
 
 	std::string buffer;
 
-	for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+	for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
 	{
 		buffer = llformat("texture_detail_%d", i);
 		initCtrl(buffer);
 	}
-	for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+	for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
 	{
 		buffer = llformat("material_detail_%d", i);
 		initCtrl(buffer);
@@ -1446,57 +1445,27 @@ BOOL LLPanelRegionTerrainInfo::postBuild()
         mRegionChangedSlot = gAgent.addRegionChangedCallback(boost::bind(&LLPanelRegionTerrainInfo::onRegionChanged,this));
     }
 
-    refresh();
-
 	return LLPanelRegionInfo::postBuild();
 }
 
-// virtual
-void LLPanelRegionTerrainInfo::refresh()
+void LLPanelRegionTerrainInfo::onSelectMaterialType()
 {
-    static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false);
-
-    LLTextBox* texture_text = getChild<LLTextBox>("detail_texture_text");
-    if (texture_text) { texture_text->setVisible(!feature_pbr_terrain_enabled); }
-
-    LLComboBox* material_type_ctrl = getChild<LLComboBox>("terrain_material_type");
-    if (material_type_ctrl)
-    {
-        material_type_ctrl->setVisible(feature_pbr_terrain_enabled);
-
-        bool has_material_assets = false;
-
-        std::string buffer;
-        for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
-        {
-            buffer = llformat("material_detail_%d", i);
-            LLTextureCtrl* material_ctrl = getChild<LLTextureCtrl>(buffer);
-            if (material_ctrl && material_ctrl->getImageAssetID().notNull())
-            {
-                has_material_assets = true;
-                break;
-            }
-        }
-
-        TerrainMaterialType material_type = material_type_from_index(material_type_ctrl->getCurrentIndex());
-
-        if (!feature_pbr_terrain_enabled) { material_type = TerrainMaterialType::TEXTURE; }
-
-        const bool is_material_selected = material_type == TerrainMaterialType::PBR_MATERIAL;
-        material_type_ctrl->setEnabled(feature_pbr_terrain_enabled && !(is_material_selected && has_material_assets));
-    }
+    updateForMaterialType();
+    onChangeAnything();
 }
 
-void LLPanelRegionTerrainInfo::onSelectMaterialType()
+void LLPanelRegionTerrainInfo::updateForMaterialType()
 {
-    LLComboBox* material_type_ctrl = getChild<LLComboBox>("terrain_material_type");
+    LLCheckBoxCtrl* material_type_ctrl = getChild<LLCheckBoxCtrl>("terrain_material_type");
     if (!material_type_ctrl) { return; }
-    const TerrainMaterialType material_type = material_type_from_index(material_type_ctrl->getCurrentIndex());
-    const bool show_texture_controls = material_type == TerrainMaterialType::TEXTURE;
-    const bool show_material_controls = material_type == TerrainMaterialType::PBR_MATERIAL;
+    const LLTerrainMaterials::Type material_type = material_type_from_ctrl(material_type_ctrl);
+    const bool show_texture_controls = material_type == LLTerrainMaterials::Type::TEXTURE;
+    const bool show_material_controls = material_type == LLTerrainMaterials::Type::PBR;
+
+    // Toggle visibility of correct swatches
     std::string buffer;
     LLTextureCtrl* texture_ctrl;
-    for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+    for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
     {
         buffer = llformat("texture_detail_%d", i);
         texture_ctrl = getChild<LLTextureCtrl>(buffer);
@@ -1505,7 +1474,7 @@ void LLPanelRegionTerrainInfo::onSelectMaterialType()
             texture_ctrl->setVisible(show_texture_controls);
         }
     }
-    for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+    for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
     {
         buffer = llformat("material_detail_%d", i);
         texture_ctrl = getChild<LLTextureCtrl>(buffer);
@@ -1514,6 +1483,12 @@ void LLPanelRegionTerrainInfo::onSelectMaterialType()
             texture_ctrl->setVisible(show_material_controls);
         }
     }
+
+    // Toggle visibility of labels
+    LLUICtrl* texture_label = getChild<LLUICtrl>("detail_texture_text");
+	if (texture_label) { texture_label->setVisible(show_texture_controls); }
+    LLUICtrl* material_label = getChild<LLUICtrl>("detail_material_text");
+	if (material_label) { material_label->setVisible(show_material_controls); }
 }
 
 void LLPanelRegionTerrainInfo::onRegionChanged()
@@ -1554,31 +1529,96 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
 
 		LLVLComposition* compp = region->getComposition();
 
-        // Are these 4 texture IDs or 4 material IDs? Who knows! Let's set the IDs on both pickers for now.
+        static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false);
+
+        const bool textures_ready = compp->texturesReady(false, false);
+        const bool materials_ready = feature_pbr_terrain_enabled && compp->materialsReady(false, false);
+
+        bool set_texture_swatches;
+        bool set_material_swatches;
+        bool reset_texture_swatches;
+        bool reset_material_swatches;
+        LLTerrainMaterials::Type material_type;
+        if (!textures_ready && !materials_ready)
+        {
+            // Are these 4 texture IDs or 4 material IDs? Who knows! Let's set
+            // the IDs on both pickers for now.
+            material_type = LLTerrainMaterials::Type::TEXTURE;
+            set_texture_swatches = true;
+            set_material_swatches = true;
+            reset_texture_swatches = false;
+            reset_material_swatches = false;
+        }
+        else
+        {
+            material_type = compp->getMaterialType();
+            set_texture_swatches = material_type == LLTerrainMaterials::Type::TEXTURE;
+            set_material_swatches = !set_texture_swatches;
+            reset_texture_swatches = !set_texture_swatches;
+            reset_material_swatches = !set_material_swatches;
+        }
+
+		LLCheckBoxCtrl* material_type_ctrl = getChild<LLCheckBoxCtrl>("terrain_material_type");
+		if (material_type_ctrl) { material_type_to_ctrl(material_type_ctrl, material_type); }
+		updateForMaterialType();
+        material_type_ctrl->setVisible(feature_pbr_terrain_enabled);
+
 		LLTextureCtrl* asset_ctrl;
 		std::string buffer;
-		for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
-		{
-			buffer = llformat("texture_detail_%d", i);
-			asset_ctrl = getChild<LLTextureCtrl>(buffer);
-			if(asset_ctrl)
-			{
-				LL_DEBUGS() << "Detail Texture " << i << ": "
-						 << compp->getDetailAssetID(i) << LL_ENDL;
-				LLUUID tmp_id(compp->getDetailAssetID(i));
-				asset_ctrl->setImageAssetID(tmp_id);
-			}
-		}
-		for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
-		{
-			buffer = llformat("material_detail_%d", i);
-			asset_ctrl = getChild<LLTextureCtrl>(buffer);
-			if(asset_ctrl)
-			{
-				LLUUID tmp_id(compp->getDetailAssetID(i));
-				asset_ctrl->setImageAssetID(tmp_id);
-			}
-		}
+        if (set_texture_swatches)
+        {
+            for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+            {
+                buffer = llformat("texture_detail_%d", i);
+                asset_ctrl = getChild<LLTextureCtrl>(buffer);
+                if(asset_ctrl)
+                {
+                    LL_DEBUGS() << "Detail Texture " << i << ": "
+                             << compp->getDetailAssetID(i) << LL_ENDL;
+                    LLUUID tmp_id(compp->getDetailAssetID(i));
+                    asset_ctrl->setImageAssetID(tmp_id);
+                }
+            }
+        }
+        if (set_material_swatches)
+        {
+            for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+            {
+                buffer = llformat("material_detail_%d", i);
+                asset_ctrl = getChild<LLTextureCtrl>(buffer);
+                if(asset_ctrl)
+                {
+                    LL_DEBUGS() << "Detail Material " << i << ": "
+                             << compp->getDetailAssetID(i) << LL_ENDL;
+                    LLUUID tmp_id(compp->getDetailAssetID(i));
+                    asset_ctrl->setImageAssetID(tmp_id);
+                }
+            }
+        }
+        if (reset_texture_swatches)
+        {
+            for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+            {
+                buffer = llformat("texture_detail_%d", i);
+                asset_ctrl = getChild<LLTextureCtrl>(buffer);
+                if(asset_ctrl)
+                {
+                    asset_ctrl->setImageAssetID(mLastSetTextures[i]);
+                }
+            }
+        }
+        if (reset_material_swatches)
+        {
+            for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+            {
+                buffer = llformat("material_detail_%d", i);
+                asset_ctrl = getChild<LLTextureCtrl>(buffer);
+                if(asset_ctrl)
+                {
+                    asset_ctrl->setImageAssetID(mLastSetMaterials[i]);
+                }
+            }
+        }
 
 		for(S32 i = 0; i < CORNER_COUNT; ++i)
     	{
@@ -1650,46 +1690,39 @@ BOOL LLPanelRegionTerrainInfo::sendUpdate()
 	std::string id_str;
 	LLMessageSystem* msg = gMessageSystem;
 
-    // Use material IDs instead of texture IDs if all material IDs are set, AND the mode is set to PBR materials.
-    S32 materials_used = 0;
-    LLComboBox* material_type_ctrl = getChild<LLComboBox>("terrain_material_type");
-    if (material_type_ctrl)
+    // Send either material IDs instead of texture IDs depending on
+    // terrain_material_type - they both occupy the same slot.
+	LLCheckBoxCtrl* material_type_ctrl = getChild<LLCheckBoxCtrl>("terrain_material_type");
+    const LLTerrainMaterials::Type material_type = material_type_ctrl ? material_type_from_ctrl(material_type_ctrl) : LLTerrainMaterials::Type::TEXTURE;
+    for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
     {
-        const TerrainMaterialType material_type = material_type_from_index(material_type_ctrl->getCurrentIndex());
-        const bool is_material_selected = material_type == TerrainMaterialType::PBR_MATERIAL;
-        if (is_material_selected)
-        {
-            for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
-            {
-                buffer = llformat("material_detail_%d", i);
-                asset_ctrl = getChild<LLTextureCtrl>(buffer);
-                if(asset_ctrl && asset_ctrl->getImageAssetID().notNull())
-                {
-                    ++materials_used;
-                }
-            }
-        }
-    }
-	for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
-	{
-        if (materials_used == TERRAIN_TEXTURE_COUNT)
+        if (material_type == LLTerrainMaterials::Type::PBR)
         {
             buffer = llformat("material_detail_%d", i);
-            asset_ctrl = getChild<LLTextureCtrl>(buffer);
         }
         else
         {
             buffer = llformat("texture_detail_%d", i);
-            asset_ctrl = getChild<LLTextureCtrl>(buffer);
         }
-		if(asset_ctrl)
-		{
-			LLUUID tmp_id(asset_ctrl->getImageAssetID());
-			tmp_id.toString(id_str);
-			buffer = llformat("%d %s", i, id_str.c_str());
-			strings.push_back(buffer);
-		}
-	}
+        asset_ctrl = getChild<LLTextureCtrl>(buffer);
+
+        if (!asset_ctrl) { continue; }
+
+        LLUUID tmp_id(asset_ctrl->getImageAssetID());
+        tmp_id.toString(id_str);
+        buffer = llformat("%d %s", i, id_str.c_str());
+        strings.push_back(buffer);
+
+        // Store asset for later terrain editing
+        if (material_type == LLTerrainMaterials::Type::PBR)
+        {
+            mLastSetMaterials[i] = tmp_id;
+        }
+        else
+        {
+            mLastSetTextures[i] = tmp_id;
+        }
+    }
 	sendEstateOwnerMessage(msg, "texturedetail", invoice, strings);
 	strings.clear();
 
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index 91e1f5b0580d3dc9843df80413cfba8bae439771..315c6aca0f533cbf284813d751a452c599c8b417 100644
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -36,6 +36,7 @@
 #include "llpanel.h"
 #include "llextendedstatus.h"
 #include "llpanelenvironment.h"
+#include "llvlcomposition.h"
 
 #include "lleventcoro.h"
 
@@ -243,7 +244,7 @@ class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
 	LOG_CLASS(LLPanelRegionTerrainInfo);
 
 public:
-	LLPanelRegionTerrainInfo() : LLPanelRegionInfo() {}
+	LLPanelRegionTerrainInfo();
 	~LLPanelRegionTerrainInfo() {}
 	
 	BOOL postBuild() override;
@@ -258,8 +259,8 @@ class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
 
 	//static void onChangeAnything(LLUICtrl* ctrl, void* userData);			// callback for any change, to enable commit button
 	
-    void refresh() override;
     void onSelectMaterialType();
+    void updateForMaterialType();
 
 	static void onClickDownloadRaw(void*);
 	static void onClickUploadRaw(void*);
@@ -274,6 +275,8 @@ class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
 	bool mConfirmedTextureHeights;
 	bool mAskedTextureHeights;
     boost::signals2::connection mRegionChangedSlot;
+    LLUUID mLastSetTextures[LLTerrainMaterials::ASSET_COUNT];
+    LLUUID mLastSetMaterials[LLTerrainMaterials::ASSET_COUNT];
 };
 
 /////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index 67a205417e1052ebc1f7d7860abdabae8989e5b7..db69c3e2c37c63045ac2eab033324628f3949966 100644
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -1601,7 +1601,7 @@ void LLOverlapPanel::draw()
 		LLUI::translate(5,getRect().getHeight()-20);	// translate to top-5,left-5
 		LLView::sDrawPreviewHighlights = FALSE;
 		LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color,
-				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
 	}
 	else
 	{
@@ -1619,7 +1619,7 @@ void LLOverlapPanel::draw()
 			std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");
 			S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;
 			LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color,
-					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
 			// widen panel enough to fit this text
 			LLRect rect = getRect();
 			setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
@@ -1685,7 +1685,7 @@ void LLOverlapPanel::draw()
 		// draw currently-selected element at top of overlappers
 		LLUI::translate(0,-mSpacing);
 		LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color,
-				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
 		LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight());	// skip spacing distance + height
 		LLView::sPreviewClickedElement->draw();
 
@@ -1700,7 +1700,7 @@ void LLOverlapPanel::draw()
 			// draw name
 			LLUI::translate(0,-mSpacing);
 			LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color,
-					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
 
 			// draw element
 			LLUI::translate(0,-mSpacing-viewp->getRect().getHeight());	// skip spacing distance + height
diff --git a/indra/newview/llfriendcard.h b/indra/newview/llfriendcard.h
index f5679d7d85d97749e494f803895bd11b866be87e..ef0dda7949c63fd3fdcc657143ffa96a5e612444 100644
--- a/indra/newview/llfriendcard.h
+++ b/indra/newview/llfriendcard.h
@@ -55,7 +55,7 @@ class LLFriendCardsManager
     };
 
 	// LLFriendObserver implementation
-	void changed(U32 mask)
+	void changed(U32 mask) override
 	{
 		onFriendListUpdate(mask);
 	}
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
index 7c8e8279c257ee49038a071f58d3a9dae4a4943a..7bb60f00e2c5450719a69c302b01d83915f4085f 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -135,7 +135,7 @@ class LLGestureMgr : public LLSingleton<LLGestureMgr>, public LLInventoryFetchIt
 	void notifyObservers();
 
 	// Overriding so we can update active gesture names and notify observers 
-	void changed(U32 mask); 
+	void changed(U32 mask) override;
 
 	BOOL matchPrefix(const std::string& in_str, std::string* out_str);
 
@@ -150,7 +150,7 @@ class LLGestureMgr : public LLSingleton<LLGestureMgr>, public LLInventoryFetchIt
 	void runStep(LLMultiGesture* gesture, LLGestureStep* step);
 
 	// LLInventoryCompletionObserver trigger
-	void done();
+	void done() override;
 
 	// Used by loadGesture
 	static void onLoadComplete(const LLUUID& asset_uuid,
diff --git a/indra/newview/llhudrender.cpp b/indra/newview/llhudrender.cpp
index dff310ecf930bfda15c71964e73dbfe35a606f9e..c1f17c9d33f91ad1c846ec778cf8c0222dc9a17d 100644
--- a/indra/newview/llhudrender.cpp
+++ b/indra/newview/llhudrender.cpp
@@ -138,7 +138,7 @@ void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent,
 	LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f));
 	F32 right_x;
 	
-	font.render(wstr, 0, 0, 1, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x);
+	font.render(wstr, 0, 0, 1, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x, /*use_ellipses*/false, /*use_color*/true);
 
 	LLUI::popMatrix();
 	gGL.popMatrix();
diff --git a/indra/newview/llimagefiltersmanager.h b/indra/newview/llimagefiltersmanager.h
index d06212d85aea06a7ad0d911723c2b8d7965c19de..05d1806da4043088307779977e127b56858676ee 100644
--- a/indra/newview/llimagefiltersmanager.h
+++ b/indra/newview/llimagefiltersmanager.h
@@ -45,7 +45,7 @@ class LLImageFiltersManager : public LLSingleton<LLImageFiltersManager>
 	void loadAllFilters();
 	void loadFiltersFromDir(const std::string& dir);
     
-	/*virtual*/ void initSingleton();
+	/*virtual*/ void initSingleton() override;
     
 	// List of filters : first is the user friendly localized name, second is the xml file name
     std::map<std::string,std::string> mFiltersList;
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index 946eb02f26bf6a50abbd10881512af61b9040d06..bace97d37a2742f1509a2ce43e695b4d66d7555c 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -537,7 +537,7 @@ class LLCallDialogManager : public LLSingleton<LLCallDialogManager>
 	static void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent);
 
 private:
-	void initSingleton();
+	void initSingleton() override;
 	void onVoiceChannelChangedInt(const LLUUID &session_id);
 	void onVoiceChannelStateChangedInt(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent);
 
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 68581d04ebdec8bdb61f3edcfd3c04f4136cd2b6..d4ca58f778ac61833d03e1645ae0adf04e7344d7 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -2402,11 +2402,17 @@ void LLInventoryGallery::startDrag()
 {
     std::vector<EDragAndDropType> types;
     uuid_vec_t ids;
+    LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_AGENT;
     for (LLUUID& selected_id : mSelectedItemIDs)
     {
         const LLInventoryItem* item = gInventory.getItem(selected_id);
         if (item)
         {
+            if (item->getPermissions().getOwner() == ALEXANDRIA_LINDEN_ID)
+            {
+                src = LLToolDragAndDrop::SOURCE_LIBRARY;
+            }
+
             EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(item->getType());
             types.push_back(type);
             ids.push_back(selected_id);
@@ -2416,12 +2422,17 @@ void LLInventoryGallery::startDrag()
         if (cat && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())
             && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType()))
         {
+            if (cat->getOwnerID() == ALEXANDRIA_LINDEN_ID)
+            {
+                src = LLToolDragAndDrop::SOURCE_LIBRARY;
+            }
+
             EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType());
             types.push_back(type);
             ids.push_back(selected_id);
         }
     }
-    LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT);
+    LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, src);
 }
 
 bool LLInventoryGallery::areViewsInitialized()
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 01d6469010c6daee1fe821a957233cbfd1d7a092..6dc4f25d029aa9329133af3413211f3a72b901f5 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1360,7 +1360,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;
 					return false;
 				}
 				LLMeshRepository::sCacheBytesRead += size;
@@ -1473,7 +1473,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
 					return false;
 				}
 				LLMeshRepository::sCacheBytesRead += size;
@@ -1575,7 +1575,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Failed to allocate memory for physics shape, size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Failed to allocate memory for physics shape, size: " << size << LL_ENDL;
 					return false;
 				}
 				file.read(buffer, size);
@@ -1770,7 +1770,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;
 					// todo: for now it will result in indefinite constant retries, should result in timeout
 					// or in retry-count and disabling mesh. (but usually viewer is beyond saving at this point)
 					return false;
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
index 14840f1b2e3e8f1bf21ffb4dec4e6104e7696fb9..dda407e708b6d288d63d1e65ddf6440485cb022a 100644
--- a/indra/newview/llmutelist.h
+++ b/indra/newview/llmutelist.h
@@ -73,7 +73,7 @@ class LLMuteList : public LLSingleton<LLMuteList>
 {
 	LLSINGLETON(LLMuteList);
 	~LLMuteList();
-	/*virtual*/ void cleanupSingleton();
+	/*virtual*/ void cleanupSingleton() override;
 public:
 	// reasons for auto-unmuting a resident
 	enum EAutoReason 
diff --git a/indra/newview/llnavigationbar.h b/indra/newview/llnavigationbar.h
index 11c671294a3c4d745c3348c0ce39ad4068bf2871..4649f5bcb0f42f8dabb61ca6df879e567a5fa858 100755
--- a/indra/newview/llnavigationbar.h
+++ b/indra/newview/llnavigationbar.h
@@ -92,10 +92,10 @@ class LLNavigationBar
 
 public:
 	
-	/*virtual*/ void	draw();
-	/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL	postBuild();
-	/*virtual*/ void	setVisible(BOOL visible);
+	/*virtual*/ void	draw() override;
+	/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL	postBuild() override;
+	/*virtual*/ void	setVisible(BOOL visible) override;
 
 	void handleLoginComplete();
 	void clearHistoryCache();
diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h
index 2f136d48e8f4fd237323b7ada8233e1a8462b062..56f2ceb8b1aff90267541cb9b8d3ed58c03f5843 100644
--- a/indra/newview/lloutfitobserver.h
+++ b/indra/newview/lloutfitobserver.h
@@ -40,7 +40,7 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 public:
 
-	virtual void changed(U32 mask);
+	virtual void changed(U32 mask) override;
 
 	void notifyOutfitLockChanged() { mOutfitLockChanged();  }
 
diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6e3a10e13d4925669e6c13909d68b95fa2be36c
--- /dev/null
+++ b/indra/newview/llpanelemojicomplete.cpp
@@ -0,0 +1,579 @@
+/**
+* @file llpanelemojicomplete.h
+* @brief Header file for LLPanelEmojiComplete
+*
+* $LicenseInfo:firstyear=2012&license=lgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2011, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llemojidictionary.h"
+#include "llemojihelper.h"
+#include "llpanelemojicomplete.h"
+#include "llscrollbar.h"
+#include "lluictrlfactory.h"
+
+constexpr U32 MIN_MOUSE_MOVE_DELTA = 4;
+constexpr U32 MIN_SHORT_CODE_WIDTH = 100;
+constexpr U32 DEF_PADDING = 8;
+
+// ============================================================================
+// LLPanelEmojiComplete
+//
+
+static LLDefaultChildRegistry::Register<LLPanelEmojiComplete> r("emoji_complete");
+
+LLPanelEmojiComplete::Params::Params()
+    : autosize("autosize")
+    , noscroll("noscroll")
+    , vertical("vertical")
+    , max_visible("max_visible")
+    , padding("padding", DEF_PADDING)
+    , selected_image("selected_image")
+{
+}
+
+LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p)
+    : LLUICtrl(p)
+    , mAutoSize(p.autosize)
+    , mNoScroll(p.noscroll)
+    , mVertical(p.vertical)
+    , mMaxVisible(p.max_visible)
+    , mPadding(p.padding)
+    , mSelectedImage(p.selected_image)
+    , mIconFont(LLFontGL::getFontEmojiHuge())
+    , mTextFont(LLFontGL::getFontSansSerifBig())
+    , mScrollbar(nullptr)
+{
+    if (mVertical)
+    {
+        LLScrollbar::Params sbparams;
+        sbparams.orientation(LLScrollbar::VERTICAL);
+        sbparams.change_callback([this](S32 index, LLScrollbar*) { onScrollbarChange(index); });
+        mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams);
+        addChild(mScrollbar);
+    }
+}
+
+LLPanelEmojiComplete::~LLPanelEmojiComplete()
+{
+}
+
+void LLPanelEmojiComplete::draw()
+{
+    LLUICtrl::draw();
+
+    if (!mTotalEmojis)
+        return;
+
+    const size_t firstVisibleIdx = mScrollPos;
+    const size_t lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mTotalEmojis);
+
+    if (mCurSelected >= firstVisibleIdx && mCurSelected < lastVisibleIdx)
+    {
+        S32 x, y, width, height;
+        if (mVertical)
+        {
+            x = mRenderRect.mLeft;
+            y = mRenderRect.mTop - (mCurSelected - firstVisibleIdx + 1) * mEmojiHeight;
+            width = mRenderRect.getWidth();
+            height = mEmojiHeight;
+        }
+        else
+        {
+            x = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth;
+            y = mRenderRect.mBottom;
+            width = mEmojiWidth;
+            height = mRenderRect.getHeight();
+        }
+        mSelectedImage->draw(x, y, width, height);
+    }
+
+    F32 iconCenterX = mRenderRect.mLeft + (F32)mEmojiWidth / 2;
+    F32 iconCenterY = mRenderRect.mTop - (F32)mEmojiHeight / 2;
+    F32 textLeft = mVertical ? mRenderRect.mLeft + mEmojiWidth + mPadding : 0;
+    F32 textWidth = mVertical ? getRect().getWidth() - textLeft - mPadding : 0;
+
+    for (U32 curIdx = firstVisibleIdx; curIdx < lastVisibleIdx; curIdx++)
+    {
+        LLWString text(1, mEmojis[curIdx].Character);
+        mIconFont->render(text, 0, iconCenterX, iconCenterY,
+            LLColor4::white, LLFontGL::HCENTER, LLFontGL::VCENTER, LLFontGL::NORMAL,
+            LLFontGL::DROP_SHADOW_SOFT, 1);
+        if (mVertical)
+        {
+            const std::string& shortCode = mEmojis[curIdx].String;
+            F32 x0 = textLeft;
+            F32 x1 = textWidth;
+            if (mEmojis[curIdx].Begin)
+            {
+                std::string text = shortCode.substr(0, mEmojis[curIdx].Begin);
+                mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white,
+                    LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+                    text.size(), x1);
+                x0 += mTextFont->getWidthF32(text);
+                x1 = textLeft + textWidth - x0;
+            }
+            if (x1 > 0 && mEmojis[curIdx].End > mEmojis[curIdx].Begin)
+            {
+                std::string text = shortCode.substr(mEmojis[curIdx].Begin, mEmojis[curIdx].End - mEmojis[curIdx].Begin);
+                mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::yellow6,
+                    LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+                    text.size(), x1);
+                x0 += mTextFont->getWidthF32(text);
+                x1 = textLeft + textWidth - x0;
+            }
+            if (x1 > 0 && mEmojis[curIdx].End < shortCode.size())
+            {
+                std::string text = shortCode.substr(mEmojis[curIdx].End);
+                mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white,
+                    LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+                    text.size(), x1);
+            }
+            iconCenterY -= mEmojiHeight;
+        }
+        else
+        {
+            iconCenterX += mEmojiWidth;
+        }
+    }
+}
+
+BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask)
+{
+    if (mScrollbar && mScrollbar->getVisible() && childrenHandleHover(x, y, mask))
+        return TRUE;
+
+    LLVector2 curHover(x, y);
+    if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA)
+    {
+        size_t index = posToIndex(x, y);
+        if (index < mTotalEmojis)
+            mCurSelected = index;
+        mLastHover = curHover;
+    }
+
+    return TRUE;
+}
+
+BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+    bool handled = false;
+    if (mTotalEmojis && MASK_NONE == mask)
+    {
+        switch (key)
+        {
+        case KEY_HOME:
+            select(0);
+            handled = true;
+            break;
+
+        case KEY_END:
+            select(mTotalEmojis - 1);
+            handled = true;
+            break;
+
+        case KEY_PAGE_DOWN:
+            select(mCurSelected + mVisibleEmojis - 1);
+            handled = true;
+            break;
+
+        case KEY_PAGE_UP:
+            select(mCurSelected - llmin(mCurSelected, mVisibleEmojis + 1));
+            handled = true;
+            break;
+
+        case KEY_LEFT:
+        case KEY_UP:
+            selectPrevious();
+            handled = true;
+            break;
+
+        case KEY_RIGHT:
+        case KEY_DOWN:
+            selectNext();
+            handled = true;
+            break;
+
+        case KEY_RETURN:
+            onCommit();
+            handled = true;
+            break;
+        }
+    }
+
+    if (handled)
+    {
+        return TRUE;
+    }
+
+    return LLUICtrl::handleKey(key, mask, called_from_parent);
+}
+
+BOOL LLPanelEmojiComplete::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+    if (mScrollbar && mScrollbar->getVisible() && childrenHandleMouseDown(x, y, mask))
+        return TRUE;
+
+    mCurSelected = posToIndex(x, y);
+    mLastHover = LLVector2(x, y);
+
+    return TRUE;
+}
+
+BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+    if (mScrollbar && mScrollbar->getVisible() && childrenHandleMouseUp(x, y, mask))
+        return TRUE;
+
+    mCurSelected = posToIndex(x, y);
+    onCommit();
+
+    return TRUE;
+}
+
+BOOL LLPanelEmojiComplete::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+    if (mNoScroll)
+        return FALSE;
+
+    if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(x, y, clicks))
+    {
+        mCurSelected = posToIndex(x, y);
+        return TRUE;
+    }
+
+    if (mTotalEmojis > mVisibleEmojis)
+    {
+        // In case of wheel up (clicks < 0) we shouldn't subtract more than value of mScrollPos
+        // Example: if mScrollPos = 0, clicks = -1 then (mScrollPos + clicks) becomes SIZE_MAX
+        // As a result of llclamp<size_t>() mScrollPos becomes (mTotalEmojis - mVisibleEmojis)
+        S32 newScrollPos = llmax(0, (S32)mScrollPos + clicks);
+        mScrollPos = llclamp<size_t>((size_t)newScrollPos, 0, mTotalEmojis - mVisibleEmojis);
+        mCurSelected = posToIndex(x, y);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+void LLPanelEmojiComplete::onCommit()
+{
+    if (mCurSelected < mTotalEmojis)
+    {
+        LLSD value(wstring_to_utf8str(LLWString(1, mEmojis[mCurSelected].Character)));
+        setValue(value);
+        LLUICtrl::onCommit();
+    }
+}
+
+void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+    LLUICtrl::reshape(width, height, called_from_parent);
+    if (mAutoSize)
+    {
+        updateConstraints();
+    }
+    else
+    {
+        onEmojisChanged();
+    }
+}
+
+void LLPanelEmojiComplete::setEmojis(const LLWString& emojis)
+{
+    mEmojis.clear();
+
+    auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+    for (const llwchar& emoji : emojis)
+    {
+        std::string shortCode;
+        if (mVertical)
+        {
+            auto it = emoji2descr.find(emoji);
+            if (it != emoji2descr.end() && !it->second->ShortCodes.empty())
+            {
+                shortCode = it->second->ShortCodes.front();
+            }
+        }
+        mEmojis.emplace_back(emoji, shortCode, 0, 0);
+    }
+
+    mTotalEmojis = mEmojis.size();
+    mCurSelected = 0;
+
+    onEmojisChanged();
+}
+
+void LLPanelEmojiComplete::setEmojiHint(const std::string& hint)
+{
+    llwchar curEmoji = mCurSelected < mTotalEmojis ? mEmojis[mCurSelected].Character : 0;
+
+    LLEmojiDictionary::instance().findByShortCode(mEmojis, hint);
+    mTotalEmojis = mEmojis.size();
+
+    mCurSelected = 0;
+    for (size_t i = 1; i < mTotalEmojis; ++i)
+    {
+        if (mEmojis[i].Character == curEmoji)
+        {
+            mCurSelected = i;
+            break;
+        }
+    }
+
+    onEmojisChanged();
+}
+
+U32 LLPanelEmojiComplete::getMaxShortCodeWidth() const
+{
+    U32 max_width = 0;
+    for (const LLEmojiSearchResult& result : mEmojis)
+    {
+        S32 width = mTextFont->getWidth(result.String);
+        if (width > max_width)
+        {
+            max_width = width;
+        }
+    }
+    return max_width;
+}
+
+void LLPanelEmojiComplete::onEmojisChanged()
+{
+    if (mAutoSize)
+    {
+        S32 width, height;
+        mVisibleEmojis = llmin(mTotalEmojis, mMaxVisible);
+        if (mVertical)
+        {
+            U32 maxShortCodeWidth = getMaxShortCodeWidth();
+            U32 shortCodeWidth = llmax(maxShortCodeWidth, MIN_SHORT_CODE_WIDTH);
+            width = mEmojiWidth + shortCodeWidth + mPadding * 2;
+            if (!mNoScroll && mVisibleEmojis < mTotalEmojis)
+            {
+                width += mScrollbar->getThickness();
+            }
+            height = mVisibleEmojis * mEmojiHeight;
+        }
+        else
+        {
+            width = mVisibleEmojis * mEmojiWidth;
+            height = getRect().getHeight();
+        }
+        LLUICtrl::reshape(width, height, false);
+    }
+    else
+    {
+        mVisibleEmojis = mVertical ?
+            mEmojiHeight ? getRect().getHeight() / mEmojiHeight : 0 :
+            mEmojiWidth ? getRect().getWidth() / mEmojiWidth : 0;
+    }
+
+    updateConstraints();
+}
+
+void LLPanelEmojiComplete::onScrollbarChange(S32 index)
+{
+    mScrollPos = llclamp<size_t>(index, 0, mTotalEmojis - mVisibleEmojis);
+}
+
+size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const
+{
+    if (mRenderRect.pointInRect(x, y))
+    {
+        U32 pos = mVertical ? (U32)(mRenderRect.mTop - y) / mEmojiHeight : x / mEmojiWidth;
+        return llmin(mScrollPos + pos, mTotalEmojis - 1);
+    }
+    return std::string::npos;
+}
+
+void LLPanelEmojiComplete::select(size_t emoji_idx)
+{
+    mCurSelected = llclamp<size_t>(emoji_idx, 0, mTotalEmojis - 1);
+
+    updateScrollPos();
+}
+
+void LLPanelEmojiComplete::selectNext()
+{
+    if (!mTotalEmojis)
+        return;
+
+    mCurSelected = (mCurSelected < mTotalEmojis - 1) ? mCurSelected + 1 : 0;
+
+    updateScrollPos();
+}
+
+void LLPanelEmojiComplete::selectPrevious()
+{
+    if (!mTotalEmojis)
+        return;
+
+    mCurSelected = (mCurSelected && mCurSelected < mTotalEmojis) ? mCurSelected - 1 : mTotalEmojis - 1;
+
+    updateScrollPos();
+}
+
+void LLPanelEmojiComplete::updateConstraints()
+{
+    mRenderRect = getLocalRect();
+
+    mEmojiWidth = mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2;
+    if (mVertical)
+    {
+        mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2;
+        if (!mNoScroll && mVisibleEmojis < mTotalEmojis)
+        {
+            mRenderRect.mRight -= mScrollbar->getThickness();
+            mScrollbar->setDocSize(mTotalEmojis);
+            mScrollbar->setPageSize(mVisibleEmojis);
+            mScrollbar->setOrigin(mRenderRect.mRight, 0);
+            mScrollbar->reshape(mScrollbar->getThickness(), mRenderRect.mTop, TRUE);
+            mScrollbar->setVisible(TRUE);
+        }
+        else
+        {
+            mScrollbar->setVisible(FALSE);
+        }
+    }
+    else
+    {
+        mEmojiHeight = mRenderRect.getHeight();
+        mRenderRect.stretch((mRenderRect.getWidth() - mVisibleEmojis * mEmojiWidth) / -2, 0);
+    }
+
+    updateScrollPos();
+}
+
+void LLPanelEmojiComplete::updateScrollPos()
+{
+    if (mNoScroll || 0 == mTotalEmojis || mTotalEmojis < mVisibleEmojis || 0 == mCurSelected)
+    {
+        mScrollPos = 0;
+        if (mCurSelected >= mVisibleEmojis)
+        {
+            mCurSelected = mVisibleEmojis ? mVisibleEmojis - 1 : 0;
+        }
+    }
+    else if (mTotalEmojis - 1 == mCurSelected)
+    {
+        mScrollPos = mTotalEmojis - mVisibleEmojis;
+    }
+    else
+    {
+        mScrollPos = mCurSelected - ((float)mCurSelected / (mTotalEmojis - 2) * (mVisibleEmojis - 2));
+    }
+
+    if (mScrollbar && mScrollbar->getVisible())
+    {
+        mScrollbar->setDocPos(mScrollPos);
+    }
+}
+
+// ============================================================================
+// LLFloaterEmojiComplete
+//
+
+LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey)
+    : LLFloater(sdKey)
+{
+    // This floater should hover on top of our dependent (with the dependent having the focus)
+    setFocusStealsFrontmost(false);
+    setAutoFocus(false);
+    setBackgroundVisible(false);
+    setIsChrome(true);
+}
+
+BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+    bool handled = false;
+    if (MASK_NONE == mask)
+    {
+        switch (key)
+        {
+            case KEY_ESCAPE:
+                LLEmojiHelper::instance().hideHelper();
+                handled = true;
+                break;
+        }
+    }
+
+    if (handled)
+        return TRUE;
+
+    return LLFloater::handleKey(key, mask, called_from_parent);
+}
+
+void LLFloaterEmojiComplete::onOpen(const LLSD& key)
+{
+    mEmojiCtrl->setEmojiHint(key["hint"].asString());
+    if (0 == mEmojiCtrl->getEmojiCount())
+    {
+        LLEmojiHelper::instance().hideHelper();
+        return;
+    }
+
+    if (mEmojiCtrl->isAutoSize())
+    {
+        LLRect outer_rect = getRect();
+        const LLRect& inner_rect = mEmojiCtrl->getRect();
+        outer_rect.mTop = outer_rect.mBottom + inner_rect.mBottom * 2 + inner_rect.getHeight();
+        outer_rect.mRight = outer_rect.mLeft + inner_rect.mLeft * 2 + inner_rect.getWidth();
+        setRect(outer_rect);
+    }
+
+    gFloaterView->adjustToFitScreen(this, FALSE);
+}
+
+BOOL LLFloaterEmojiComplete::postBuild()
+{
+    mEmojiCtrl = findChild<LLPanelEmojiComplete>("emoji_complete_ctrl");
+    mEmojiCtrl->setCommitCallback(
+        [this](LLUICtrl* ctrl, const LLSD& param)
+        {
+            setValue(param);
+            onCommit();
+        });
+
+    mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth();
+    mEmojiCtrlVert = getRect().getHeight() - mEmojiCtrl->getRect().getHeight();
+
+    return LLFloater::postBuild();
+}
+
+void LLFloaterEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+    if (called_from_parent)
+    {
+        LLFloater::reshape(width, height, called_from_parent);
+    }
+    else
+    {
+        LLRect outer(getRect()), inner(mEmojiCtrl->getRect());
+        outer.mRight = outer.mLeft + inner.getWidth() + mEmojiCtrlHorz;
+        outer.mTop = outer.mBottom + inner.getHeight() + mEmojiCtrlVert;
+        setRect(outer);
+    }
+}
+
+// ============================================================================
diff --git a/indra/newview/llpanelemojicomplete.h b/indra/newview/llpanelemojicomplete.h
new file mode 100644
index 0000000000000000000000000000000000000000..36a965202e21ddf1c5fdd00dfc53a5e9b716b356
--- /dev/null
+++ b/indra/newview/llpanelemojicomplete.h
@@ -0,0 +1,132 @@
+/**
+* @file llpanelemojicomplete.h
+* @brief Header file for LLPanelEmojiComplete
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#pragma once
+
+#include "llemojidictionary.h"
+#include "llfloater.h"
+#include "lluictrl.h"
+
+class LLScrollbar;
+
+// ============================================================================
+// LLPanelEmojiComplete
+//
+
+class LLPanelEmojiComplete : public LLUICtrl
+{
+    friend class LLUICtrlFactory;
+public:
+    struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+    {
+        Optional<bool>       autosize;
+        Optional<bool>       noscroll;
+        Optional<bool>       vertical;
+        Optional<S32>        max_visible,
+                             padding;
+
+        Optional<LLUIImage*> selected_image;
+
+        Params();
+    };
+
+protected:
+    LLPanelEmojiComplete(const LLPanelEmojiComplete::Params&);
+
+public:
+    virtual ~LLPanelEmojiComplete();
+
+    void draw() override;
+    BOOL handleHover(S32 x, S32 y, MASK mask) override;
+    BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
+    BOOL handleMouseDown(S32 x, S32 y, MASK mask) override;
+    BOOL handleMouseUp(S32 x, S32 y, MASK mask) override;
+    BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) override;
+    void onCommit() override;
+    void reshape(S32 width, S32 height, BOOL called_from_parent) override;
+
+public:
+    size_t getEmojiCount() const { return mEmojis.size(); }
+    void setEmojis(const LLWString& emojis);
+    void setEmojiHint(const std::string& hint);
+    bool isAutoSize() const { return mAutoSize; }
+    U32 getMaxShortCodeWidth() const;
+
+protected:
+    void onEmojisChanged();
+    void onScrollbarChange(S32 index);
+    size_t posToIndex(S32 x, S32 y) const;
+    void select(size_t emoji_idx);
+    void selectNext();
+    void selectPrevious();
+    void updateConstraints();
+    void updateScrollPos();
+
+protected:
+    const bool      mAutoSize;
+    const bool      mNoScroll;
+    const bool      mVertical;
+    const size_t    mMaxVisible;
+    const S32       mPadding;
+    const LLUIImagePtr mSelectedImage;
+    const LLFontGL* mIconFont;
+    const LLFontGL* mTextFont;
+
+    std::vector<LLEmojiSearchResult> mEmojis;
+    LLScrollbar*    mScrollbar;
+    LLRect          mRenderRect;
+    U16             mEmojiWidth = 0;
+    U16             mEmojiHeight = 0;
+    size_t          mTotalEmojis = 0;
+    size_t          mVisibleEmojis = 0;
+    size_t          mFirstVisible = 0;
+    size_t          mScrollPos = 0;
+    size_t          mCurSelected = 0;
+    LLVector2       mLastHover;
+};
+
+// ============================================================================
+// LLFloaterEmojiComplete
+//
+
+class LLFloaterEmojiComplete : public LLFloater
+{
+public:
+    LLFloaterEmojiComplete(const LLSD& sdKey);
+
+public:
+    BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
+    void onOpen(const LLSD& key) override;
+    BOOL postBuild() override;
+    void reshape(S32 width, S32 height, BOOL called_from_parent) override;
+
+protected:
+    LLPanelEmojiComplete* mEmojiCtrl = nullptr;
+    S32 mEmojiCtrlHorz = 0;
+    S32 mEmojiCtrlVert = 0;
+};
+
+// ============================================================================
diff --git a/indra/newview/llpaneltopinfobar.h b/indra/newview/llpaneltopinfobar.h
index 78dd99702906f95f2f32bc6aa0045d838ceb6dbb..b6c263e33157fc1386baccec0d0d2b68b256ac1f 100644
--- a/indra/newview/llpaneltopinfobar.h
+++ b/indra/newview/llpaneltopinfobar.h
@@ -46,8 +46,8 @@ class LLPanelTopInfoBar : public LLPanel, public LLSingleton<LLPanelTopInfoBar>,
 public:
 	typedef boost::signals2::signal<void ()> resize_signal_t;
 
-	/*virtual*/ BOOL postBuild();
-	/*virtual*/ void draw();
+	/*virtual*/ BOOL postBuild() override;
+	/*virtual*/ void draw() override;
 
 	/**
 	 * Updates location and parcel icons on login complete
@@ -83,7 +83,7 @@ class LLPanelTopInfoBar : public LLPanel, public LLSingleton<LLPanelTopInfoBar>,
 	 */
 	void initParcelIcons();
 
-	BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+	BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override;
 
 	/**
 	 * Handles clicks on the parcel icons.
diff --git a/indra/newview/llpathfindingpathtool.h b/indra/newview/llpathfindingpathtool.h
index 88cb3a15f89e1e2131d4f613b1ebd746712a9616..f98624e30d2bf0dca4029b2d2917c912f131bc90 100644
--- a/indra/newview/llpathfindingpathtool.h
+++ b/indra/newview/llpathfindingpathtool.h
@@ -66,17 +66,17 @@ class LLPathfindingPathTool : public LLTool, public LLSingleton<LLPathfindingPat
 	typedef boost::signals2::signal<void (void)> path_event_signal_t;
 	typedef boost::signals2::connection          path_event_slot_t;
 
-	virtual BOOL      handleMouseDown(S32 pX, S32 pY, MASK pMask);
-	virtual BOOL      handleMouseUp(S32 pX, S32 pY, MASK pMask);
-	virtual BOOL      handleMiddleMouseDown(S32 pX, S32 pY, MASK pMask);
-	virtual BOOL      handleMiddleMouseUp(S32 pX, S32 pY, MASK pMask);
-	virtual BOOL      handleRightMouseDown(S32 pX, S32 pY, MASK pMask);
-	virtual BOOL      handleRightMouseUp(S32 pX, S32 pY, MASK pMask);
-	virtual BOOL      handleDoubleClick(S32 x, S32 y, MASK mask);
+	virtual BOOL      handleMouseDown(S32 pX, S32 pY, MASK pMask) override;
+	virtual BOOL      handleMouseUp(S32 pX, S32 pY, MASK pMask) override;
+	virtual BOOL      handleMiddleMouseDown(S32 pX, S32 pY, MASK pMask) override;
+	virtual BOOL      handleMiddleMouseUp(S32 pX, S32 pY, MASK pMask) override;
+	virtual BOOL      handleRightMouseDown(S32 pX, S32 pY, MASK pMask) override;
+	virtual BOOL      handleRightMouseUp(S32 pX, S32 pY, MASK pMask) override;
+	virtual BOOL      handleDoubleClick(S32 x, S32 y, MASK mask) override;
 
-	virtual BOOL      handleHover(S32 pX, S32 pY, MASK pMask);
+	virtual BOOL      handleHover(S32 pX, S32 pY, MASK pMask) override;
 
-	virtual BOOL      handleKey(KEY pKey, MASK pMask);
+	virtual BOOL      handleKey(KEY pKey, MASK pMask) override;
 
 	EPathStatus       getPathStatus() const;
 
diff --git a/indra/newview/llproductinforequest.h b/indra/newview/llproductinforequest.h
index d1036374e8008302ff83f8b8d68ee12f4d7eaade..0b94c39d1147ad7965022d34672206f0216f7f79 100644
--- a/indra/newview/llproductinforequest.h
+++ b/indra/newview/llproductinforequest.h
@@ -46,7 +46,7 @@ class LLProductInfoRequestManager : public LLSingleton<LLProductInfoRequestManag
 	std::string getDescriptionForSku(const std::string& sku);
 
 private:
-	/* virtual */ void initSingleton();
+	/* virtual */ void initSingleton() override;
 
     void getLandDescriptionsCoro(std::string url);
     LLSD mSkuDescriptions;
diff --git a/indra/newview/llrecentpeople.h b/indra/newview/llrecentpeople.h
index 1b322f2c0a98b3a68d70ad59815b6987db474e4e..0c04222a9faf54673e7c82b02ca99117a1c798ab 100644
--- a/indra/newview/llrecentpeople.h
+++ b/indra/newview/llrecentpeople.h
@@ -106,7 +106,7 @@ class LLRecentPeople: public LLSingleton<LLRecentPeople>, public LLOldEvents::LL
 	/**
 	 * LLSimpleListener interface.
 	 */
-	/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
+	/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) override;
 
 	void updateAvatarsArrivalTime(uuid_vec_t& uuids);
 	F32 getArrivalTimeByID(const LLUUID& id);
diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp
index 9b1d2d48c65cdbe7f7bb1316f88ce1d1cc86129b..2643ee95f8852d7da4dbc7afbb09c464cf36a2d6 100644
--- a/indra/newview/llsceneview.cpp
+++ b/indra/newview/llsceneview.cpp
@@ -99,7 +99,6 @@ void LLSceneView::draw()
 	std::vector<F32> physics_cost[2];
 	F32 total_physics[] = { 0.f, 0.f };
 	
-
 	LLViewerRegion* region = gAgent.getRegion();
 	if (region)
 	{
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 30e12faca50e9fa6b6d849c21ff4f61909750f4c..620dcad760a9c7403915eb345e77b8c38a2130e5 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -5526,9 +5526,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,
 	LLSelectNode* linkset_root = NULL;
 	LLViewerRegion*	last_region;
 	LLViewerRegion*	current_region;
-
-//	S32 objects_sent = 0;
-//	S32 packets_sent = 0;
 	S32 objects_in_this_packet = 0;
 
 	bool link_operation = message_name == "ObjectLink";
@@ -5660,7 +5657,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,
 			(*pack_body)(node, user_data);
             // do any related logging
             (*log_func)(node, user_data);
-//			++objects_sent;
 			++objects_in_this_packet;
 
 			// and on to the next object
@@ -5678,7 +5674,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,
 		{
 			// otherwise send current message and start new one
 			gMessageSystem->sendReliable( last_region->getHost());
-//			packets_sent++;
 			objects_in_this_packet = 0;
 
 			gMessageSystem->newMessage(message_name.c_str());
@@ -5695,7 +5690,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,
 				{
 					// add root instance into new message
 					(*pack_body)(linkset_root, user_data);
-//					++objects_sent;
 					++objects_in_this_packet;
 				}
 			}
@@ -5709,7 +5703,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,
 	if (gMessageSystem->getCurrentSendTotal() > 0)
 	{
 		gMessageSystem->sendReliable( current_region->getHost());
-//		packets_sent++;
 	}
 	else
 	{
diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h
index ed795b515586ea1b41cc34d02afe412bcc61b3f0..22c9481687770fc3f0f4064b8c49cfd99af85b1b 100644
--- a/indra/newview/llspeakers.h
+++ b/indra/newview/llspeakers.h
@@ -338,7 +338,7 @@ class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLActiveSpeak
 	LOG_CLASS(LLActiveSpeakerMgr);
 
 protected:
-	virtual void updateSpeakerList();
+	virtual void updateSpeakerList() override;
 };
 
 class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLLocalSpeakerMgr>
@@ -347,7 +347,7 @@ class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLLocalSpeaker
 	~LLLocalSpeakerMgr ();
 	LOG_CLASS(LLLocalSpeakerMgr);
 protected:
-	virtual void updateSpeakerList();
+	virtual void updateSpeakerList() override;
 };
 
 #endif // LL_LLSPEAKERS_H
diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp
index 0111d8869cf9fd5ac6610c6248f8e779efbd6f35..c4c9673be34f4f21ec0c2db821eec0260518967f 100644
--- a/indra/newview/llspeakingindicatormanager.cpp
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -53,7 +53,7 @@ class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, L
 	LOG_CLASS(SpeakingIndicatorManager);
 
 protected:
-    void                cleanupSingleton();
+    void                cleanupSingleton() override;
 
 public:
 
@@ -88,7 +88,7 @@ class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, L
 	 * So, method does not calculate difference between these list it only switches off already 
 	 * switched on indicators and switches on indicators of voice channel participants
 	 */
-	void onParticipantsChanged();
+	void onParticipantsChanged() override;
 	
 private:
 	typedef std::set<LLUUID> speaker_ids_t;
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 62703e3499a8584ffcdfdb34b50b235d11ed2abe..84cd6e2da7553fb1801a57e3d47f7d9c8d23bf5c 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -588,8 +588,7 @@ void LLGLTexMemBar::draw()
 	x_right = 550.0;
 	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP,
-											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX,
-											 &x_right, FALSE);
+											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &x_right);
 
 	F32Kilobits bandwidth(LLAppViewer::getTextureFetch()->getTextureBandwidth());
 	F32Kilobits max_bandwidth(gSavedSettings.getF32("ThrottleBandwidthKBPS"));
diff --git a/indra/newview/lltoolbrush.h b/indra/newview/lltoolbrush.h
index c108d8325681b0f54317f5d5899f05444bcb5ae7..6545ee36119909c2ff534a3ff1f892eb3f31affb 100644
--- a/indra/newview/lltoolbrush.h
+++ b/indra/newview/lltoolbrush.h
@@ -49,27 +49,27 @@ class LLToolBrushLand : public LLTool, public LLEditMenuHandler, public LLSingle
 public:
 	
 	// x,y in window coords, 0,0 = left,bot
-	virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
-	virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );		
-	virtual BOOL handleHover( S32 x, S32 y, MASK mask );
-	virtual void handleSelect();
-	virtual void handleDeselect();
+	virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ) override;
+	virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ) override;
+	virtual BOOL handleHover( S32 x, S32 y, MASK mask ) override;
+	virtual void handleSelect() override;
+	virtual void handleDeselect() override;
 
 	// isAlwaysRendered() - return true if this is a tool that should
 	// always be rendered regardless of selection.
-	virtual BOOL isAlwaysRendered() { return TRUE; }
+	virtual BOOL isAlwaysRendered()  override { return TRUE; }
 
 	// Draw the area that will be affected.
-	virtual void render();
+	virtual void render() override;
 
 	// on Idle is where the land modification actually occurs
 	static void onIdle(void* brush_tool);  
 
-	void			onMouseCaptureLost();
+	void onMouseCaptureLost() override;
 
 	void modifyLandInSelectionGlobal();
-	virtual void	undo();
-	virtual BOOL	canUndo() const	{ return TRUE; }
+	virtual void	undo() override;
+	virtual BOOL	canUndo() const	 override { return TRUE; }
 
 protected:
 	void brush( void );
diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h
index 86506f725ef4a93fa3563955759efa453589bd6c..f539a045b7681697bb9de707721674b62d795187 100644
--- a/indra/newview/lltoolcomp.h
+++ b/indra/newview/lltoolcomp.h
@@ -108,11 +108,11 @@ class LLToolCompInspect : public LLToolComposite, public LLSingleton<LLToolCompI
 public:
 
 	// Overridden from LLToolComposite
-    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleKey(KEY key, MASK mask);
-	virtual void		onMouseCaptureLost();
+    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
+    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleKey(KEY key, MASK mask) override;
+	virtual void		onMouseCaptureLost() override;
 			void		keyUp(KEY key, MASK mask);
 
 	static void pickCallback(const LLPickInfo& pick_info);
@@ -133,13 +133,13 @@ class LLToolCompTranslate : public LLToolComposite, public LLSingleton<LLToolCom
 public:
 
 	// Overridden from LLToolComposite
-	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);			// Returns to the default tool
-	virtual void		render();
+	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;			// Returns to the default tool
+	virtual void		render() override;
 
-	virtual LLTool*		getOverrideTool(MASK mask);
+	virtual LLTool*		getOverrideTool(MASK mask) override;
 
 	static void pickCallback(const LLPickInfo& pick_info);
 };
@@ -154,13 +154,13 @@ class LLToolCompScale : public LLToolComposite, public LLSingleton<LLToolCompSca
 public:
 
 	// Overridden from LLToolComposite
-    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-    virtual BOOL		handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);			// Returns to the default tool
-	virtual void		render();
+    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+    virtual BOOL		handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;			// Returns to the default tool
+	virtual void		render() override;
 
-	virtual LLTool*		getOverrideTool(MASK mask);
+	virtual LLTool*		getOverrideTool(MASK mask) override;
 	
 	static void pickCallback(const LLPickInfo& pick_info);
 };
@@ -176,13 +176,13 @@ class LLToolCompRotate : public LLToolComposite, public LLSingleton<LLToolCompRo
 public:
 
 	// Overridden from LLToolComposite
-    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-    virtual BOOL		handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual void		render();
+    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+    virtual BOOL		handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual void		render() override;
 
-	virtual LLTool*		getOverrideTool(MASK mask);
+	virtual LLTool*		getOverrideTool(MASK mask) override;
 
 	static void pickCallback(const LLPickInfo& pick_info);
 
@@ -199,9 +199,9 @@ class LLToolCompCreate : public LLToolComposite, public LLSingleton<LLToolCompCr
 public:
 
 	// Overridden from LLToolComposite
-    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
+    virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+    virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
 	
 	static void pickCallback(const LLPickInfo& pick_info);
 protected:
@@ -224,16 +224,16 @@ class LLToolCompGun : public LLToolComposite, public LLSingleton<LLToolCompGun>
 public:
 
 	// Overridden from LLToolComposite
-    virtual BOOL			handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL			handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL			handleDoubleClick(S32 x, S32 y, MASK mask);
-	virtual BOOL			handleRightMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL			handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL			handleScrollWheel(S32 x, S32 y, S32 clicks);
-	virtual void			onMouseCaptureLost();
-	virtual void			handleSelect();
-	virtual void			handleDeselect();
-	virtual LLTool*			getOverrideTool(MASK mask) { return NULL; }
+    virtual BOOL			handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL			handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL			handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	virtual BOOL			handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL			handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL			handleScrollWheel(S32 x, S32 y, S32 clicks) override;
+	virtual void			onMouseCaptureLost() override;
+	virtual void			handleSelect() override;
+	virtual void			handleDeselect() override;
+	virtual LLTool*			getOverrideTool(MASK mask) override { return NULL; }
 
 protected:
 	LLToolGun*			mGun;
diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h
index db2fe87f20a0c62a15d3cbe3ef640c4d54f77859..60a2f01107d7f42ba46d68409682a927ff02d6e7 100644
--- a/indra/newview/lltooldraganddrop.h
+++ b/indra/newview/lltooldraganddrop.h
@@ -48,12 +48,12 @@ class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop>
 	typedef boost::signals2::signal<void ()> enddrag_signal_t;
 
 	// overridden from LLTool
-	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleKey(KEY key, MASK mask);
-	virtual BOOL	handleToolTip(S32 x, S32 y, MASK mask);
-	virtual void	onMouseCaptureLost();
-	virtual void	handleDeselect();
+	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleKey(KEY key, MASK mask) override;
+	virtual BOOL	handleToolTip(S32 x, S32 y, MASK mask) override;
+	virtual void	onMouseCaptureLost() override;
+	virtual void	handleDeselect() override;
 
 	void			setDragStart( S32 x, S32 y );			// In screen space
 	BOOL			isOverThreshold( S32 x, S32 y );		// In screen space
diff --git a/indra/newview/lltoolface.h b/indra/newview/lltoolface.h
index e4b8ae12b87d4a62e95e727feee1527acc97b149..7c8ff2048068e9da9eabb7f2d53358c8d0469ff2 100644
--- a/indra/newview/lltoolface.h
+++ b/indra/newview/lltoolface.h
@@ -39,11 +39,11 @@ class LLToolFace
 	virtual ~LLToolFace();
 public:
 
-	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleDoubleClick(S32 x, S32 y, MASK mask);
-	virtual void	handleSelect();
-	virtual void	handleDeselect();
-	virtual void	render();			// draw face highlights
+	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	virtual void	handleSelect() override;
+	virtual void	handleDeselect() override;
+	virtual void	render() override;			// draw face highlights
 
 	static void pickCallback(const LLPickInfo& pick_info);
 };
diff --git a/indra/newview/lltoolfocus.h b/indra/newview/lltoolfocus.h
index ef71f9230a701b209c7cb3db64b9fa93f151bfeb..54d9827ae630366d38e4c9507c8c6b5eb9f148b2 100644
--- a/indra/newview/lltoolfocus.h
+++ b/indra/newview/lltoolfocus.h
@@ -38,16 +38,16 @@ class LLToolCamera
 	virtual ~LLToolCamera();
 public:
 
-	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
+	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleHover(S32 x, S32 y, MASK mask) override;
 
-	virtual void	onMouseCaptureLost();
+	virtual void	onMouseCaptureLost() override;
 
-	virtual void	handleSelect();
-	virtual void	handleDeselect();
+	virtual void	handleSelect() override;
+	virtual void	handleDeselect() override;
 
-	virtual LLTool*	getOverrideTool(MASK mask) { return NULL; }
+	virtual LLTool*	getOverrideTool(MASK mask) override { return NULL; }
 
     void setClickPickPending() { mClickPickPending = true; }
 	static void pickCallback(const LLPickInfo& pick_info);
diff --git a/indra/newview/lltoolindividual.h b/indra/newview/lltoolindividual.h
index e7c2060fba01909da6a84bc444dc7f03d9e256bf..89dd9d9796f43c6f934ffc9df8b856da1c5851a1 100644
--- a/indra/newview/lltoolindividual.h
+++ b/indra/newview/lltoolindividual.h
@@ -43,11 +43,9 @@ class LLToolIndividual : public LLTool, public LLSingleton<LLToolIndividual>
 	virtual ~LLToolIndividual();
 public:
 
-	virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
-	virtual void handleSelect();
-	//virtual void handleDeselect();
-	//virtual void render();
+	virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	virtual void handleSelect() override;
 
 	static void pickCallback(const LLPickInfo& pick_info);
 
diff --git a/indra/newview/lltoolobjpicker.h b/indra/newview/lltoolobjpicker.h
index 5ad9b67e217859e85709603b33b0ea29b775e53b..a55cd223de2718eda2791b18acc8469487125f1f 100644
--- a/indra/newview/lltoolobjpicker.h
+++ b/indra/newview/lltoolobjpicker.h
@@ -38,16 +38,16 @@ class LLToolObjPicker : public LLTool, public LLSingleton<LLToolObjPicker>
 	LLSINGLETON(LLToolObjPicker);
 public:
 
-	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleHover(S32 x, S32 y, MASK mask);
+	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleHover(S32 x, S32 y, MASK mask) override;
 
-	virtual void 		handleSelect();
-	virtual void 		handleDeselect();
+	virtual void 		handleSelect() override;
+	virtual void 		handleDeselect() override;
 
-	virtual void		onMouseCaptureLost();
+	virtual void		onMouseCaptureLost() override;
 
-	virtual void 		setExitCallback(void (*callback)(void *), void *callback_data);
+	void 		setExitCallback(void (*callback)(void *), void *callback_data);
 
 	LLUUID				getObjectID() const { return mHitObjectID; }
 
diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h
index 8f6100e4b44003ba900e67a653098b867b32c927..dca0d12cf6cf5e346b0c03dda94bdf40540487bf 100644
--- a/indra/newview/lltoolpie.h
+++ b/indra/newview/lltoolpie.h
@@ -42,26 +42,26 @@ class LLToolPie : public LLTool, public LLSingleton<LLToolPie>
 public:
 
 	// Virtual functions inherited from LLMouseHandler
-	virtual BOOL		handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down);
-	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleRightMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleRightMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
+	virtual BOOL		handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) override;
+	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleRightMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
 	BOOL				handleScrollWheelAny(S32 x, S32 y, S32 clicks_x, S32 clicks_y);
-	virtual BOOL		handleScrollWheel(S32 x, S32 y, S32 clicks);
-	virtual BOOL		handleScrollHWheel(S32 x, S32 y, S32 clicks);
-	virtual BOOL		handleToolTip(S32 x, S32 y, MASK mask);
+	virtual BOOL		handleScrollWheel(S32 x, S32 y, S32 clicks) override;
+	virtual BOOL		handleScrollHWheel(S32 x, S32 y, S32 clicks) override;
+	virtual BOOL		handleToolTip(S32 x, S32 y, MASK mask) override;
 
-	virtual void		render();
+	virtual void		render() override;
 
-	virtual void		stopEditing();
+	virtual void		stopEditing() override;
 
-	virtual void		onMouseCaptureLost();
-	virtual void		handleSelect();
-	virtual void		handleDeselect();
-	virtual LLTool*		getOverrideTool(MASK mask);
+	virtual void		onMouseCaptureLost() override;
+	virtual void		handleSelect() override;
+	virtual void		handleDeselect() override;
+	virtual LLTool*		getOverrideTool(MASK mask) override;
 
 	LLPickInfo&			getPick() { return mPick; }
 	U8					getClickAction() { return mClickAction; }
diff --git a/indra/newview/lltoolpipette.h b/indra/newview/lltoolpipette.h
index 7575d8ad18454b60ad66451af839338b4efdc6d0..2636811c66d4513d8865eeec937858cf750e5ae2 100644
--- a/indra/newview/lltoolpipette.h
+++ b/indra/newview/lltoolpipette.h
@@ -47,10 +47,10 @@ class LLToolPipette
 	virtual ~LLToolPipette();
 
 public:
-	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
-	virtual BOOL	handleToolTip(S32 x, S32 y, MASK mask);
+	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleHover(S32 x, S32 y, MASK mask) override;
+	virtual BOOL	handleToolTip(S32 x, S32 y, MASK mask) override;
 
 	// Note: Don't return connection; use boost::bind + boost::signals2::trackable to disconnect slots
 	typedef boost::signals2::signal<void (const LLTextureEntry& te)> signal_t;
diff --git a/indra/newview/lltoolselectland.h b/indra/newview/lltoolselectland.h
index b5ba72f16d5d0073e3bcb6127cf12c136b1352c4..88bc4e2e3d64f561e102430a434bac682abb3816 100644
--- a/indra/newview/lltoolselectland.h
+++ b/indra/newview/lltoolselectland.h
@@ -39,15 +39,15 @@ class LLToolSelectLand
 	virtual ~LLToolSelectLand();
 
 public:
-	/*virtual*/ BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-	/*virtual*/ BOOL		handleHover(S32 x, S32 y, MASK mask);
-	/*virtual*/ void		render();				// draw the select rectangle
-	/*virtual*/ BOOL		isAlwaysRendered()		{ return TRUE; }
+	/*virtual*/ BOOL		handleMouseDown(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleDoubleClick(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleMouseUp(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ BOOL		handleHover(S32 x, S32 y, MASK mask) override;
+	/*virtual*/ void		render() override;				// draw the select rectangle
+	/*virtual*/ BOOL		isAlwaysRendered() override	{ return TRUE; }
 
-	/*virtual*/ void		handleSelect();
-	/*virtual*/ void		handleDeselect();
+	/*virtual*/ void		handleSelect() override;
+	/*virtual*/ void		handleDeselect() override;
 
 protected:
 	BOOL			outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h
index a40042380a0766bb1ead56105f609121dd2cadf5..f82c5ffa9849427f177e97c16f4fe18df863c137 100644
--- a/indra/newview/llversioninfo.h
+++ b/indra/newview/llversioninfo.h
@@ -47,7 +47,7 @@ class LLStoreListener;
 class LLVersionInfo: public LLSingleton<LLVersionInfo>
 {
 	LLSINGLETON(LLVersionInfo);
-	void initSingleton();
+	void initSingleton() override;
 public:
 	~LLVersionInfo();
 
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 1c3c547bc155cdfd034ff7f764390b4e997e2739..0d2d62fd77aac5821782f4ad612f4ae4fd1f25e0 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -25,7 +25,7 @@
  */
 
 #include "llviewerprecompiledheaders.h"
-#include "llviewerchat.h" 
+#include "llviewerchat.h"
 
 // newview includes
 #include "llagent.h" 	// gAgent		
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 3a08f748d6090bf723ab0af780d0925b7b5567e6..8353d4d1d78f3c88d359eb4b31b36515925cb444 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -66,6 +66,7 @@
 #include "llfloaterdestinations.h"
 #include "llfloaterdisplayname.h"
 #include "llfloatereditextdaycycle.h"
+#include "llfloateremojipicker.h"
 #include "llfloaterenvironmentadjust.h"
 #include "llfloaterexperienceprofile.h"
 #include "llfloaterexperiences.h"
@@ -161,6 +162,7 @@
 #include "llfloaterimnearbychat.h"
 #include "llpanelblockedlist.h"
 #include "llpanelprofileclassifieds.h"
+#include "llpanelemojicomplete.h"
 #include "llpreviewanim.h"
 #include "llpreviewgesture.h"
 #include "llpreviewnotecard.h"
@@ -339,7 +341,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>);
     LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChangeItemThumbnail>);
 	LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater);
-    LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>);
+	LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>);
 	LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>);
 	LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>);
 	LLFloaterReg::add("add_landmark", "floater_create_landmark.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCreateLandmark>);
@@ -347,18 +349,20 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("delete_pref_preset", "floater_delete_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeletePrefPreset>);
 	LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>);
 
+	LLFloaterReg::add("emoji_picker", "floater_emoji_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEmojiPicker>);
+	LLFloaterReg::add("emoji_complete", "floater_emoji_complete.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEmojiComplete>);
 	LLFloaterReg::add("env_post_process", "floater_post_process.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPostProcess>);
 
-    LLFloaterReg::add("env_fixed_environmentent_water", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentWater>);
-    LLFloaterReg::add("env_fixed_environmentent_sky", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentSky>);
+	LLFloaterReg::add("env_fixed_environmentent_water", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentWater>);
+	LLFloaterReg::add("env_fixed_environmentent_sky", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentSky>);
 
-    LLFloaterReg::add("env_adjust_snapshot", "floater_adjust_environment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEnvironmentAdjust>);
+	LLFloaterReg::add("env_adjust_snapshot", "floater_adjust_environment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEnvironmentAdjust>);
 
-    LLFloaterReg::add("env_edit_extdaycycle", "floater_edit_ext_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditExtDayCycle>);
-    LLFloaterReg::add("my_environments", "floater_my_environments.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMyEnvironment>);
+	LLFloaterReg::add("env_edit_extdaycycle", "floater_edit_ext_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditExtDayCycle>);
+	LLFloaterReg::add("my_environments", "floater_my_environments.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMyEnvironment>);
 
-    LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>);
-    LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>);
+	LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>);
+	LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>);
 	LLFloaterReg::add("experience_profile", "floater_experienceprofile.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperienceProfile>);
 	LLFloaterReg::add("experience_search", "floater_experience_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiencePicker>);
 
diff --git a/indra/newview/llviewerhelp.h b/indra/newview/llviewerhelp.h
index da50e07a4362a092c2241e26929dbf6be5f52a87..bbd20bc07ebcb5612087a517cf53c5dbba94d753 100644
--- a/indra/newview/llviewerhelp.h
+++ b/indra/newview/llviewerhelp.h
@@ -43,21 +43,21 @@ class LLViewerHelp : public LLHelp, public LLSingleton<LLViewerHelp>
 
  public:
 	/// display the specified help topic in the help viewer
-	/*virtual*/ void showTopic(const std::string &topic);
+	/*virtual*/ void showTopic(const std::string &topic) override;
 
-	std::string getURL(const std::string& topic);
+	std::string getURL(const std::string& topic) override;
 
 	// return topic derived from viewer UI focus, else default topic
 	std::string getTopicFromFocus();
 
 	/// return default (fallback) topic name suitable for showTopic()
-	/*virtual*/ std::string defaultTopic();
+	/*virtual*/ std::string defaultTopic() override;
 
 	// return topic to use before the user logs in
-	/*virtual*/ std::string preLoginTopic();
+	/*virtual*/ std::string preLoginTopic() override;
 
 	// return topic to use for the top-level help, invoked by F1
-	/*virtual*/ std::string f1HelpTopic();
+	/*virtual*/ std::string f1HelpTopic() override;
 };
 
 #endif // header guard
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index f1f42afd81ac127be5f68b27b257f97d9acd8f1f..ad7c4bcefa34c028394744c24fab7c4888a706ee 100644
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -74,7 +74,7 @@ class LLViewerMedia: public LLSingleton<LLViewerMedia>
 {
 	LLSINGLETON(LLViewerMedia);
 	~LLViewerMedia();
-	void initSingleton();
+	void initSingleton() override;
 	LOG_CLASS(LLViewerMedia);
 
 public:
diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h
index effd08a559b77858abff7acb90d5ac69b41f94e5..2310e4dbfcf53f3fd1e68a3288e4687479725d5d 100644
--- a/indra/newview/llviewermediafocus.h
+++ b/indra/newview/llviewermediafocus.h
@@ -54,10 +54,10 @@ class LLViewerMediaFocus :
 	void setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero);
 	void clearHover();
 	
-	/*virtual*/ bool	getFocus();
-	/*virtual*/ BOOL	handleKey(KEY key, MASK mask, BOOL called_from_parent);
-	/*virtual*/ BOOL	handleKeyUp(KEY key, MASK mask, BOOL called_from_parent);
-	/*virtual*/ BOOL	handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+	bool	getFocus();
+	/*virtual*/ BOOL	handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
+	/*virtual*/ BOOL	handleKeyUp(KEY key, MASK mask, BOOL called_from_parent) override;
+	/*virtual*/ BOOL	handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) override;
 	BOOL handleScrollWheel(const LLVector2& texture_coords, S32 clicks_x, S32 clicks_y);
 	BOOL handleScrollWheel(S32 x, S32 y, S32 clicks_x, S32 clicks_y);
 
@@ -92,12 +92,12 @@ class LLViewerMediaFocus :
 	LLUUID getControlsMediaID();
 
     // The MoaP object wants keyup and keydown events.  Overridden to return true.
-    virtual bool    wantsKeyUpKeyDown() const;
-    virtual bool    wantsReturnKey() const;
+    virtual bool    wantsKeyUpKeyDown() const override;
+    virtual bool    wantsReturnKey() const override;
 
 protected:
-	/*virtual*/ void	onFocusReceived();
-	/*virtual*/ void	onFocusLost();
+	/*virtual*/ void	onFocusReceived() override;
+	/*virtual*/ void	onFocusLost() override;
 
 private:
 	
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 981984db6ae24469d62804e1296a4ad72610eb9b..d5e4de03a964138296122a7edb83331bc89c9879 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -289,6 +289,7 @@ void handle_disconnect_viewer(void *);
 
 void force_error_breakpoint(void *);
 void force_error_llerror(void *);
+void force_error_llerror_msg(void*);
 void force_error_bad_memory_access(void *);
 void force_error_infinite_loop(void *);
 void force_error_software_exception(void *);
@@ -1437,6 +1438,30 @@ class LLAdvancedCheckDebugViews : public view_listener_t
 
 
 
+///////////////////
+// DEBUG UNICODE //
+///////////////////
+
+
+class LLAdvancedToggleDebugUnicode : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		LLView::sDebugUnicode = !(LLView::sDebugUnicode);
+		return true;
+	}
+};
+
+class LLAdvancedCheckDebugUnicode : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		return LLView::sDebugUnicode;
+	}
+};
+
+
+
 ///////////////////////
 // XUI NAME TOOLTIPS //
 ///////////////////////
@@ -2390,6 +2415,15 @@ class LLAdvancedForceErrorLlerror : public view_listener_t
 	}
 };
 
+class LLAdvancedForceErrorLlerrorMsg: public view_listener_t
+{
+    bool handleEvent(const LLSD& userdata)
+    {
+        force_error_llerror_msg(NULL);
+        return true;
+    }
+};
+
 class LLAdvancedForceErrorBadMemoryAccess : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -8369,6 +8403,11 @@ void force_error_llerror(void *)
     LLAppViewer::instance()->forceErrorLLError();
 }
 
+void force_error_llerror_msg(void*)
+{
+    LLAppViewer::instance()->forceErrorLLErrorMsg();
+}
+
 void force_error_bad_memory_access(void *)
 {
     LLAppViewer::instance()->forceErrorBadMemoryAccess();
@@ -8523,23 +8562,8 @@ void handle_show_url(const LLSD& param)
 
 void handle_report_bug(const LLSD& param)
 {
-	LLUIString url(param.asString());
-	
-	LLStringUtil::format_map_t replace;
-	std::string environment = LLAppViewer::instance()->getViewerInfoString(true);
-	boost::regex regex;
-	regex.assign("</?nolink>");
-	std::string stripped_env = boost::regex_replace(environment, regex, "");
-
-	replace["[ENVIRONMENT]"] = LLURI::escape(stripped_env);
-	LLSLURL location_url;
-	LLAgentUI::buildSLURL(location_url);
-	replace["[LOCATION]"] = LLURI::escape(location_url.getSLURLString());
-
-	LLUIString file_bug_url = gSavedSettings.getString("ReportBugURL");
-	file_bug_url.setArgs(replace);
-
-	LLWeb::loadURLExternal(file_bug_url.getString());
+    std::string url = gSavedSettings.getString("ReportBugURL");
+    LLWeb::loadURLExternal(url);
 }
 
 void handle_buy_currency_test(void*)
@@ -9539,6 +9563,8 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedCheckDebugClicks(), "Advanced.CheckDebugClicks");
 	view_listener_t::addMenu(new LLAdvancedCheckDebugViews(), "Advanced.CheckDebugViews");
 	view_listener_t::addMenu(new LLAdvancedToggleDebugViews(), "Advanced.ToggleDebugViews");
+	view_listener_t::addMenu(new LLAdvancedCheckDebugUnicode(), "Advanced.CheckDebugUnicode");
+	view_listener_t::addMenu(new LLAdvancedToggleDebugUnicode(), "Advanced.ToggleDebugUnicode");
 	view_listener_t::addMenu(new LLAdvancedToggleXUINameTooltips(), "Advanced.ToggleXUINameTooltips");
 	view_listener_t::addMenu(new LLAdvancedCheckXUINameTooltips(), "Advanced.CheckXUINameTooltips");
 	view_listener_t::addMenu(new LLAdvancedToggleDebugMouseEvents(), "Advanced.ToggleDebugMouseEvents");
@@ -9608,6 +9634,7 @@ void initialize_menus()
 	// Advanced > Debugging
 	view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint");
 	view_listener_t::addMenu(new LLAdvancedForceErrorLlerror(), "Advanced.ForceErrorLlerror");
+    view_listener_t::addMenu(new LLAdvancedForceErrorLlerrorMsg(), "Advanced.ForceErrorLlerrorMsg");
 	view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccess(), "Advanced.ForceErrorBadMemoryAccess");
 	view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccessCoro(), "Advanced.ForceErrorBadMemoryAccessCoro");
 	view_listener_t::addMenu(new LLAdvancedForceErrorInfiniteLoop(), "Advanced.ForceErrorInfiniteLoop");
@@ -9638,7 +9665,11 @@ void initialize_menus()
 	
 	//Develop (clear cache immediately)
 	commit.add("Develop.ClearCache", boost::bind(&handle_cache_clear_immediately) );
-    
+
+	// Develop (Fonts debugging)
+	commit.add("Develop.Fonts.Dump", boost::bind(&LLFontGL::dumpFonts));
+	commit.add("Develop.Fonts.DumpTextures", boost::bind(&LLFontGL::dumpFontTextures));
+
 	// Admin >Object
 	view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy");
 	view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf");
diff --git a/indra/newview/llviewerparcelaskplay.h b/indra/newview/llviewerparcelaskplay.h
index dc711917d29368f1e755d6ecd0594a6833782e31..56faddae663b56998a5886bde5309e1fe3182d11 100644
--- a/indra/newview/llviewerparcelaskplay.h
+++ b/indra/newview/llviewerparcelaskplay.h
@@ -34,8 +34,8 @@ class LLViewerParcelAskPlay : public LLSingleton<LLViewerParcelAskPlay>
 {
     LLSINGLETON(LLViewerParcelAskPlay);
     ~LLViewerParcelAskPlay();
-    void initSingleton();
-    void cleanupSingleton();
+    void initSingleton() override;
+    void cleanupSingleton() override;
 public:
     // functor expects functor(region_id, parcel_id, url, play/stop)
     typedef boost::function<void(const LLUUID&, const S32&, const std::string&, const bool&)> ask_callback;
diff --git a/indra/newview/llviewerparcelmedia.h b/indra/newview/llviewerparcelmedia.h
index 779a65bdf83a5cb239b485e2379bc21ec2675658..790b2b71fc7a3ec8b581707ae845977f01d7af09 100644
--- a/indra/newview/llviewerparcelmedia.h
+++ b/indra/newview/llviewerparcelmedia.h
@@ -74,7 +74,7 @@ class LLViewerParcelMedia : public LLViewerMediaObserver, public LLSingleton<LLV
 	void sendMediaNavigateMessage(const std::string& url);
 
 	// inherited from LLViewerMediaObserver
-	virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event);
+	virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) override;
 
 private:
 	void processParcelMediaCommandMessage(LLMessageSystem *msg);
diff --git a/indra/newview/llviewerparcelmediaautoplay.h b/indra/newview/llviewerparcelmediaautoplay.h
index d71fd4c07508a8d4c49a7e62e07009138d85d2e7..e83085dee0656907e3ca8af78ce164b7f413261e 100644
--- a/indra/newview/llviewerparcelmediaautoplay.h
+++ b/indra/newview/llviewerparcelmediaautoplay.h
@@ -35,7 +35,7 @@ class LLViewerParcelMediaAutoPlay : LLEventTimer, public LLSingleton<LLViewerPar
 {
 	LLSINGLETON(LLViewerParcelMediaAutoPlay);
 public:
-	virtual BOOL tick();
+	virtual BOOL tick() override;
 	static void playStarted();
 
  private:
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index e8dd96daee21c4b551f0b8d01d4384e82a15a7fb..10a2cfa32a5bf9b5e4a276d975b1f46fb3c11a96 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -244,9 +244,9 @@ class LLUIImageList : public LLImageProviderInterface, public LLSingleton<LLUIIm
 	LLSINGLETON_EMPTY_CTOR(LLUIImageList);
 public:
 	// LLImageProviderInterface
-	/*virtual*/ LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority);
-	/*virtual*/ LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority);
-	void cleanUp();
+	/*virtual*/ LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority) override;
+	/*virtual*/ LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority) override;
+	void cleanUp() override;
 
 	bool initFromFile();
 
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 0aae6f4b35ca9dcdefd8128e1c310b9aefa5176f..1459686f104b578f96fda7a34a819a62364fe6cb 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -955,8 +955,7 @@ class LLDebugText
 		{
 			const Line& line = *iter;
 			LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor,
-											 LLFontGL::LEFT, LLFontGL::TOP,
-											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+					LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
 		}
 	}
 
@@ -1958,7 +1957,11 @@ LLViewerWindow::LLViewerWindow(const Params& p)
 	// Initialize OpenGL Renderer
 	LLVertexBuffer::initClass(mWindow);
 	LL_INFOS("RenderInit") << "LLVertexBuffer initialization done." << LL_ENDL ;
-	gGL.init(true);
+	if (!gGL.init(true))
+    {
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBVideoDrvErr"));
+        LL_ERRS() << "gGL not initialized" << LL_ENDL;
+    }
 
 	if (LLFeatureManager::getInstance()->isSafe()
 		|| (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion())
@@ -3000,6 +3003,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 					case KEY_PAGE_UP:
 					case KEY_PAGE_DOWN:
 					case KEY_HOME:
+					case KEY_END:
 						// when chatbar is empty or ArrowKeysAlwaysMove set,
 						// pass arrow keys on to avatar...
 						return FALSE;
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index 09b21e4e0addfd87981b52b21252a2663425331f..506ab005c59e6af3a3abbe1ca38c0a8daeb4a086 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -44,7 +44,6 @@
 
 static const U32 BASE_SIZE = 128;
 
-
 F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
 {
 	// Not sure if this is the right math...
@@ -91,10 +90,10 @@ BOOL LLTerrainMaterials::generateMaterials()
 LLUUID LLTerrainMaterials::getDetailAssetID(S32 asset)
 {
     llassert(mDetailTextures[asset] && mDetailMaterials[asset]);
-    // *HACK: Assume both the the material and texture were fetched in the same
-    // way using the same UUID. However, we may not know at this point which
-    // one will load.
-	return mDetailTextures[asset]->getID();
+    // Assume both the the material and texture were fetched in the same way
+    // using the same UUID. However, we may not know at this point which one
+    // will load.
+	return mDetailTextures[asset] ? mDetailTextures[asset]->getID() : LLUUID::null;
 }
 
 LLPointer<LLViewerFetchedTexture> fetch_terrain_texture(const LLUUID& id)
@@ -133,7 +132,7 @@ bool LLTerrainMaterials::texturesReady(bool boost, bool strict)
     // *NOTE: Calls to textureReady may boost textures. Do not early-return.
     for (S32 i = 0; i < ASSET_COUNT; i++)
     {
-        ready[i] = textureReady(mDetailTextures[i], boost);
+        ready[i] = mDetailTextures[i].notNull() && textureReady(mDetailTextures[i], boost);
     }
 
     bool one_ready = false;
@@ -250,6 +249,7 @@ bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bo
     // Material is loaded, but textures may not be
     if (!textures_set)
     {
+        textures_set = true;
         // *NOTE: These can sometimes be set to to nullptr due to
         // updateTEMaterialTextures. For the sake of robustness, we emulate
         // that fetching behavior by setting textures of null IDs to nullptr.
@@ -257,9 +257,6 @@ bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bo
         mat->mNormalTexture            = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]);
         mat->mMetallicRoughnessTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]);
         mat->mEmissiveTexture          = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]);
-        textures_set                   = true;
-
-        return false;
     }
 
     // *NOTE: Calls to textureReady may boost textures. Do not early-return.
@@ -288,16 +285,29 @@ bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bo
     return true;
 }
 
+// static
+const LLUUID (&LLVLComposition::getDefaultTextures())[ASSET_COUNT]
+{
+    const static LLUUID default_textures[LLVLComposition::ASSET_COUNT] =
+    {
+        TERRAIN_DIRT_DETAIL,
+        TERRAIN_GRASS_DETAIL,
+        TERRAIN_MOUNTAIN_DETAIL,
+        TERRAIN_ROCK_DETAIL
+    };
+    return default_textures;
+}
 
 LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
     LLTerrainMaterials(),
 	LLViewerLayer(width, scale)
 {
 	// Load Terrain Textures - Original ones
-	setDetailAssetID(0, TERRAIN_DIRT_DETAIL);
-	setDetailAssetID(1, TERRAIN_GRASS_DETAIL);
-	setDetailAssetID(2, TERRAIN_MOUNTAIN_DETAIL);
-	setDetailAssetID(3, TERRAIN_ROCK_DETAIL);
+    const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
+    for (S32 i = 0; i < ASSET_COUNT; ++i)
+    {
+        setDetailAssetID(i, default_textures[i]);
+    }
 
 	mSurfacep = surfacep;
 
diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h
index 73bfca6ed49f5a95910a6af59f596e87f7a39ea8..7397ff1e8dcd84a2953ff545cdf0baf9e5c20dd3 100644
--- a/indra/newview/llvlcomposition.h
+++ b/indra/newview/llvlcomposition.h
@@ -58,7 +58,7 @@ class LLTerrainMaterials
 
     BOOL generateMaterials();
 
-	LLUUID getDetailAssetID(S32 asset);
+	virtual LLUUID getDetailAssetID(S32 asset);
 	virtual void setDetailAssetID(S32 asset, const LLUUID& id);
     Type getMaterialType();
     bool texturesReady(bool boost, bool strict);
@@ -82,6 +82,11 @@ extern LLTerrainMaterials gLocalTerrainMaterials;
 class LLVLComposition : public LLTerrainMaterials, public LLViewerLayer
 {
 public:
+	// Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc.
+	// So we need to compress heights into this range.
+    static const S32 ASSET_COUNT = 4;
+	static const LLUUID (&getDefaultTextures())[ASSET_COUNT];
+
 	LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale);
 	/*virtual*/ ~LLVLComposition();
 
@@ -93,10 +98,6 @@ class LLVLComposition : public LLTerrainMaterials, public LLViewerLayer
 	// Generate texture from composition values.
 	BOOL generateMinimapTileLand(const F32 x, const F32 y, const F32 width, const F32 height);		
 
-	// Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc.
-	// So we need to compress heights into this range.
-    static const S32 ASSET_COUNT = 4;
-
 	// Use these as indeces ito the get/setters below that use 'corner'
 	enum ECorner
 	{
diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h
index 309c3eebddfea599b9decd08d3dc362fe096457b..e68bfbe1ffa9409594e0147bb85fa49fba04ad41 100644
--- a/indra/newview/llvoicechannel.h
+++ b/indra/newview/llvoicechannel.h
@@ -170,12 +170,12 @@ class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoice
 	LLSINGLETON(LLVoiceChannelProximal);
 public:
 
-	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
-	/*virtual*/ void handleStatusChange(EStatusType status);
-	/*virtual*/ void handleError(EStatusType status);
-	/*virtual*/ BOOL isActive();
-	/*virtual*/ void activate();
-	/*virtual*/ void deactivate();
+	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal) override;
+	/*virtual*/ void handleStatusChange(EStatusType status) override;
+	/*virtual*/ void handleError(EStatusType status) override;
+	/*virtual*/ BOOL isActive() override;
+	/*virtual*/ void activate() override;
+	/*virtual*/ void deactivate() override;
 
 };
 
@@ -184,15 +184,15 @@ class LLVoiceChannelP2P : public LLVoiceChannelGroup
 public:
 	LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id);
 
-	/*virtual*/ void handleStatusChange(EStatusType status);
-	/*virtual*/ void handleError(EStatusType status);
-    /*virtual*/ void activate();
-	/*virtual*/ void getChannelInfo();
+	/*virtual*/ void handleStatusChange(EStatusType status) override;
+	/*virtual*/ void handleError(EStatusType status) override;
+    /*virtual*/ void activate() override;
+	/*virtual*/ void getChannelInfo() override;
 
 	void setSessionHandle(const std::string& handle, const std::string &inURI);
 
 protected:
-	virtual void setState(EState state);
+	virtual void setState(EState state) override;
 
 private:
 
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index e3ab99c675400aff2757b86b97d7fc86646f5770..ae2aec0e9ce9372b15ca2ee12a174e6300824636 100644
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -64,26 +64,26 @@ class LLVivoxVoiceClient :	public LLSingleton<LLVivoxVoiceClient>,
 	/// @name LLVoiceModuleInterface virtual implementations
 	///  @see LLVoiceModuleInterface
 	//@{
-	virtual void init(LLPumpIO *pump);	// Call this once at application startup (creates connector)
-	virtual void terminate();	// Call this to clean up during shutdown
+	virtual void init(LLPumpIO *pump) override;	// Call this once at application startup (creates connector)
+	virtual void terminate() override;	// Call this to clean up during shutdown
 	
-	virtual const LLVoiceVersionInfo& getVersion();
+	virtual const LLVoiceVersionInfo& getVersion() override;
 	
-	virtual void updateSettings(); // call after loading settings and whenever they change
+	virtual void updateSettings() override; // call after loading settings and whenever they change
 
 	// Returns true if vivox has successfully logged in and is not in error state	
-	virtual bool isVoiceWorking() const;
+	virtual bool isVoiceWorking() const override;
 
 	/////////////////////
 	/// @name Tuning
 	//@{
-	virtual void tuningStart();
-	virtual void tuningStop();
-	virtual bool inTuningMode();
+	virtual void tuningStart() override;
+	virtual void tuningStop() override;
+	virtual bool inTuningMode() override;
 	
-	virtual void tuningSetMicVolume(float volume);
-	virtual void tuningSetSpeakerVolume(float volume);
-	virtual float tuningGetEnergy(void);
+	virtual void tuningSetMicVolume(float volume) override;
+	virtual void tuningSetSpeakerVolume(float volume) override;
+	virtual float tuningGetEnergy(void) override;
 	//@}
 	
 	/////////////////////
@@ -91,40 +91,40 @@ class LLVivoxVoiceClient :	public LLSingleton<LLVivoxVoiceClient>,
 	//@{
 	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
 	// i.e. when the daemon is running and connected, and the device lists are populated.
-	virtual bool deviceSettingsAvailable();
-	virtual bool deviceSettingsUpdated();  //return if the list has been updated and never fetched,  only to be called from the voicepanel.
+	virtual bool deviceSettingsAvailable() override;
+	virtual bool deviceSettingsUpdated() override;  //return if the list has been updated and never fetched,  only to be called from the voicepanel.
 	
 	// Requery the vivox daemon for the current list of input/output devices.
 	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
 	// (use this if you want to know when it's done).
 	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
-	virtual void refreshDeviceLists(bool clearCurrentList = true);
+	virtual void refreshDeviceLists(bool clearCurrentList = true) override;
 	
-	virtual void setCaptureDevice(const std::string& name);
-	virtual void setRenderDevice(const std::string& name);
+	virtual void setCaptureDevice(const std::string& name) override;
+	virtual void setRenderDevice(const std::string& name) override;
 	
-	virtual LLVoiceDeviceList& getCaptureDevices();
-	virtual LLVoiceDeviceList& getRenderDevices();
+	virtual LLVoiceDeviceList& getCaptureDevices() override;
+	virtual LLVoiceDeviceList& getRenderDevices() override;
 	//@}	
 	
-	virtual void getParticipantList(std::set<LLUUID> &participants);
-	virtual bool isParticipant(const LLUUID& speaker_id);
+	virtual void getParticipantList(std::set<LLUUID> &participants) override;
+	virtual bool isParticipant(const LLUUID& speaker_id) override;
 
 	// Send a text message to the specified user, initiating the session if necessary.
 	// virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;};
 	
 	// close any existing text IM session with the specified user
-	virtual void endUserIMSession(const LLUUID &uuid);
+	virtual void endUserIMSession(const LLUUID &uuid) override;
 
 	// Returns true if calling back the session URI after the session has closed is possible.
 	// Currently this will be false only for PSTN P2P calls.		
 	// NOTE: this will return true if the session can't be found. 
-	virtual BOOL isSessionCallBackPossible(const LLUUID &session_id);
+	virtual BOOL isSessionCallBackPossible(const LLUUID &session_id) override;
 	
 	// Returns true if the session can accepte text IM's.
 	// Currently this will be false only for PSTN P2P calls.
 	// NOTE: this will return true if the session can't be found. 
-	virtual BOOL isSessionTextIMPossible(const LLUUID &session_id);
+	virtual BOOL isSessionTextIMPossible(const LLUUID &session_id) override;
 	
 	
 	////////////////////////////
@@ -132,21 +132,21 @@ class LLVivoxVoiceClient :	public LLSingleton<LLVivoxVoiceClient>,
 	//@{
 	// returns true iff the user is currently in a proximal (local spatial) channel.
 	// Note that gestures should only fire if this returns true.
-	virtual bool inProximalChannel();
+	virtual bool inProximalChannel() override;
 	
 	virtual void setNonSpatialChannel(const std::string &uri,
-									  const std::string &credentials);
+									  const std::string &credentials) override;
 	
 	virtual bool setSpatialChannel(const std::string &uri,
-								   const std::string &credentials);
+								   const std::string &credentials) override;
 	
-	virtual void leaveNonSpatialChannel();
+	virtual void leaveNonSpatialChannel() override;
 	
-	virtual void leaveChannel(void);	
+	virtual void leaveChannel(void) override;
 	
 	// Returns the URI of the current channel, or an empty string if not currently in a channel.
 	// NOTE that it will return an empty string if it's in the process of joining a channel.
-	virtual std::string getCurrentChannel();
+	virtual std::string getCurrentChannel() override;
 	//@}
 	
 	
@@ -154,59 +154,59 @@ class LLVivoxVoiceClient :	public LLSingleton<LLVivoxVoiceClient>,
 	/// @name invitations
 	//@{
 	// start a voice channel with the specified user
-	virtual void callUser(const LLUUID &uuid);	
-	virtual bool isValidChannel(std::string &channelHandle);
-	virtual bool answerInvite(std::string &channelHandle);
-	virtual void declineInvite(std::string &channelHandle);
+	virtual void callUser(const LLUUID &uuid) override;
+	virtual bool isValidChannel(std::string &channelHandle) override;
+	virtual bool answerInvite(std::string &channelHandle) override;
+	virtual void declineInvite(std::string &channelHandle) override;
 	//@}
 	
 	/////////////////////////
 	/// @name Volume/gain
 	//@{
-	virtual void setVoiceVolume(F32 volume);
-	virtual void setMicGain(F32 volume);
+	virtual void setVoiceVolume(F32 volume) override;
+	virtual void setMicGain(F32 volume) override;
 	//@}
 	
 	/////////////////////////
 	/// @name enable disable voice and features
 	//@{
-	virtual bool voiceEnabled();
-	virtual void setVoiceEnabled(bool enabled);
-	virtual BOOL lipSyncEnabled();	
-	virtual void setLipSyncEnabled(BOOL enabled);
-	virtual void setMuteMic(bool muted);		// Set the mute state of the local mic.
+	virtual bool voiceEnabled() override;
+	virtual void setVoiceEnabled(bool enabled) override;
+	virtual BOOL lipSyncEnabled() override;
+	virtual void setLipSyncEnabled(BOOL enabled) override;
+	virtual void setMuteMic(bool muted) override;		// Set the mute state of the local mic.
 	//@}
 		
 	//////////////////////////
 	/// @name nearby speaker accessors
 	//@{
-	virtual BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar
-	virtual std::string getDisplayName(const LLUUID& id);
-	virtual BOOL isParticipantAvatar(const LLUUID &id);
-	virtual BOOL getIsSpeaking(const LLUUID& id);
-	virtual BOOL getIsModeratorMuted(const LLUUID& id);
-	virtual F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
-	virtual BOOL getOnMuteList(const LLUUID& id);
-	virtual F32 getUserVolume(const LLUUID& id);
-	virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)	
+	virtual BOOL getVoiceEnabled(const LLUUID& id) override;		// true if we've received data for this avatar
+	virtual std::string getDisplayName(const LLUUID& id) override;
+	virtual BOOL isParticipantAvatar(const LLUUID &id) override;
+	virtual BOOL getIsSpeaking(const LLUUID& id) override;
+	virtual BOOL getIsModeratorMuted(const LLUUID& id) override;
+	virtual F32 getCurrentPower(const LLUUID& id) override;		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
+	virtual BOOL getOnMuteList(const LLUUID& id) override;
+	virtual F32 getUserVolume(const LLUUID& id) override;
+	virtual void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal)
 	//@}
 	
 	// authorize the user
 	virtual void userAuthorized(const std::string& user_id,
-								const LLUUID &agentID);
+								const LLUUID &agentID) override;
 	
 	//////////////////////////////
 	/// @name Status notification
 	//@{
-	virtual void addObserver(LLVoiceClientStatusObserver* observer);
-	virtual void removeObserver(LLVoiceClientStatusObserver* observer);
-	virtual void addObserver(LLFriendObserver* observer);
-	virtual void removeObserver(LLFriendObserver* observer);		
-	virtual void addObserver(LLVoiceClientParticipantObserver* observer);
-	virtual void removeObserver(LLVoiceClientParticipantObserver* observer);
+	virtual void addObserver(LLVoiceClientStatusObserver* observer) override;
+	virtual void removeObserver(LLVoiceClientStatusObserver* observer) override;
+	virtual void addObserver(LLFriendObserver* observer) override;
+	virtual void removeObserver(LLFriendObserver* observer) override;
+	virtual void addObserver(LLVoiceClientParticipantObserver* observer) override;
+	virtual void removeObserver(LLVoiceClientParticipantObserver* observer) override;
 	//@}
 	
-	virtual std::string sipURIFromID(const LLUUID &id);
+	virtual std::string sipURIFromID(const LLUUID &id) override;
 	//@}
 
 	/// @name LLVoiceEffectInterface virtual implementations
@@ -216,32 +216,32 @@ class LLVivoxVoiceClient :	public LLSingleton<LLVivoxVoiceClient>,
 	//////////////////////////
 	/// @name Accessors
 	//@{
-	virtual bool setVoiceEffect(const LLUUID& id);
-	virtual const LLUUID getVoiceEffect();
-	virtual LLSD getVoiceEffectProperties(const LLUUID& id);
+	virtual bool setVoiceEffect(const LLUUID& id) override;
+	virtual const LLUUID getVoiceEffect() override;
+	virtual LLSD getVoiceEffectProperties(const LLUUID& id) override;
 
-	virtual void refreshVoiceEffectLists(bool clear_lists);
-	virtual const voice_effect_list_t& getVoiceEffectList() const;
-	virtual const voice_effect_list_t& getVoiceEffectTemplateList() const;
+	virtual void refreshVoiceEffectLists(bool clear_lists) override;
+	virtual const voice_effect_list_t& getVoiceEffectList() const override;
+	virtual const voice_effect_list_t& getVoiceEffectTemplateList() const override;
 	//@}
 
 	//////////////////////////////
 	/// @name Status notification
 	//@{
-	virtual void addObserver(LLVoiceEffectObserver* observer);
-	virtual void removeObserver(LLVoiceEffectObserver* observer);
+	virtual void addObserver(LLVoiceEffectObserver* observer) override;
+	virtual void removeObserver(LLVoiceEffectObserver* observer) override;
 	//@}
 
 	//////////////////////////////
 	/// @name Effect preview buffer
 	//@{
-	virtual void enablePreviewBuffer(bool enable);
-	virtual void recordPreviewBuffer();
-	virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null);
-	virtual void stopPreviewBuffer();
+	virtual void enablePreviewBuffer(bool enable) override;
+	virtual void recordPreviewBuffer() override;
+	virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override;
+	virtual void stopPreviewBuffer() override;
 
-	virtual bool isPreviewRecording();
-	virtual bool isPreviewPlaying();
+	virtual bool isPreviewRecording() override;
+	virtual bool isPreviewPlaying() override;
 	//@}
 
 	//@}
@@ -750,7 +750,7 @@ class LLVivoxVoiceClient :	public LLSingleton<LLVivoxVoiceClient>,
 	std::string getAudioSessionURI();
 	std::string getAudioSessionHandle();
 			
-    void setHidden(bool hidden); //virtual
+    void setHidden(bool hidden) override; //virtual
 	void sendPositionAndVolumeUpdate(void);
 	
     void sendCaptureAndRenderDevices();
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
index 36e6da802b1fde073600429cdbb71355c26e065e..575b1dbe7e0da8e204bb388fe94d5bed0f1d8e8e 100644
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -865,6 +865,10 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 		mReferenceBuffer->unmapBuffer();
 		llassert(vertex_count == max_vertices);
 		llassert(index_count == max_indices);
+#ifndef SHOW_ASSERT
+        (void)vertex_count;
+        (void)index_count;
+#endif
 	}
 
 	//generate tree mesh
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index f7774a708643aafbcfb2e847e985a9878aaeaf4c..0dbe50d5e4f033d56710c82f3e7caf7a744819e0 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -428,7 +428,7 @@ class LLWearableItemsList : public LLInventoryItemsList
 	{
 		LLSINGLETON(ContextMenu);
 	public:
-		/*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y);
+		/*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) override;
 
 		void show(LLView* spawning_view, LLWearableType::EType w_type, S32 x, S32 y);
 
@@ -441,7 +441,7 @@ class LLWearableItemsList : public LLInventoryItemsList
 			MASK_UNKNOWN		= 0x10,
 		};
 
-		/* virtual */ LLContextMenu* createMenu();
+		/* virtual */ LLContextMenu* createMenu() override;
 		void updateItemsVisibility(LLContextMenu* menu);
 		void updateItemsLabels(LLContextMenu* menu);
 		static void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val);
@@ -472,7 +472,7 @@ class LLWearableItemsList : public LLInventoryItemsList
 
 	virtual ~LLWearableItemsList();
 
-	/*virtual*/ LLPanel* createNewItem(LLViewerInventoryItem* item);
+	/*virtual*/ LLPanel* createNewItem(LLViewerInventoryItem* item) override;
 
 	void updateList(const LLUUID& category_id);
 
diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h
index 524adba652960c849583ae81b253f782cea679fe..31dbfb8ffd01082d2c96d86860598f1ec119b21f 100644
--- a/indra/newview/llwindebug.h
+++ b/indra/newview/llwindebug.h
@@ -40,9 +40,9 @@ class LLWinDebug:
 {
 	LLSINGLETON_EMPTY_CTOR(LLWinDebug);
 public:
-	void initSingleton();
+	void initSingleton() override;
 	static void generateMinidump(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL);
-	void cleanupSingleton();
+	void cleanupSingleton() override;
 private:
 	static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename);
 };
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index cc947c87d5196d6f7d0e2280fcf87f40b327c87c..a8676d2ad6c8035dfa8c433f8135ae18c46a7139 100755
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -520,7 +520,7 @@ void LLWorldMapView::draw()
 					S32_MAX, //max_chars
 					mMapScale, //max_pixels
 					NULL,
-					TRUE); //use ellipses
+					/*use_ellipses*/TRUE);
 			}
 		}
 	}
diff --git a/indra/newview/skins/default/textures/icons/emoji_picker_icon.png b/indra/newview/skins/default/textures/icons/emoji_picker_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad4f3fa63c170d190a54eb3dd62daefc67037794
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/emoji_picker_icon.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 2126db32df754880179207d051b7efd2d218cc22..1a49ec7e24735ce91f856119b16c44457717951c 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -206,6 +206,7 @@ with the same filename but different name
 
   <texture name="DropTarget" file_name="widgets/DropTarget.png" preload="false" />
 
+  <texture name="Emoji_Picker_Icon" file_name="icons/emoji_picker_icon.png" preload="true" />
   <texture name="ExternalBrowser_Off" file_name="icons/ExternalBrowser_Off.png" preload="false" />
   <texture name="Edit_Wrench" file_name="icons/Edit_Wrench.png" preload="false" />
 
@@ -213,7 +214,7 @@ with the same filename but different name
 
   <texture name="Presets_Icon" file_name="icons/Presets_Icon.png" preload="true" />
   <texture name="Presets_Icon_Graphic" file_name="icons/Presets_Icon_Graphic.png" preload="true" />
- <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />
+  <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />
   <texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" />
   <texture name="Favorite_Star_Press" file_name="navbar/Favorite_Star_Press.png" preload="false" />
   <texture name="Favorite_Star_Over" file_name="navbar/Favorite_Star_Over.png" preload="false" />
@@ -342,7 +343,7 @@ with the same filename but different name
   <texture name="Inv_Underpants" file_name="icons/Inv_Underpants.png" preload="false" />
   <texture name="Inv_Undershirt" file_name="icons/Inv_Undershirt.png" preload="false" />
   <texture name="Inv_Link" file_name="icons/Inv_Link.png" preload="false" />
-    <texture name="Inv_Settings" file_name="icons/Inv_Settings.png" preload="false" />
+  <texture name="Inv_Settings" file_name="icons/Inv_Settings.png" preload="false" />
   <texture name="Inv_SettingsSky" file_name="icons/Inv_SettingsSky.png" preload="false" />
   <texture name="Inv_SettingsWater" file_name="icons/Inv_SettingsWater.png" preload="false" />
   <texture name="Inv_SettingsDay" file_name="icons/Inv_SettingsDay.png" preload="false" />
diff --git a/indra/newview/skins/default/xui/da/emoji_categories.xml b/indra/newview/skins/default/xui/da/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..456b18e4e216be0941190f1938fb1a1199b95a38
--- /dev/null
+++ b/indra/newview/skins/default/xui/da/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>smileys and følelser</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>mennesker and krop</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>komponenter</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>dyr and natur</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>mad and drikke</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>rejser and steder</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>oplevelser</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>objekter</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>symboler</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/de/emoji_categories.xml b/indra/newview/skins/default/xui/de/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ed63d0bac9d3bfb15529d5b395f4f0d7d3424961
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>Smileys and Emotionen</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>Menschen and Körper</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>Komponenten</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>Tiere and Natur</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>Essen and Trinken</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>Reisen and Orte</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>Aktivitäten</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>Gegenstände</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>Symbole</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/en/emoji_categories.xml b/indra/newview/skins/default/xui/en/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0315d0c43aa4036503dd602a1d5dac985356e687
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>smileys and emotion</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>people and body</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>components</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>animals and nature</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>food and drink</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>travel and places</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>activities</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>objects</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>symbols</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/en/floater_activeim.xml b/indra/newview/skins/default/xui/en/floater_activeim.xml
index b79c5d9a19773ae6740a8fe50e10fa43b57e99d1..42c3e7e93532183e52d0f094c97f3508cf184928 100644
--- a/indra/newview/skins/default/xui/en/floater_activeim.xml
+++ b/indra/newview/skins/default/xui/en/floater_activeim.xml
@@ -23,7 +23,7 @@
 		<scrolling_panel_list
 			follows="left|right"
 			layout="topleft"
-      left="1"      
+			left="1"
 			name="chiclet_row_panel_list"
 			width="318"/>
 	</scroll_container>
diff --git a/indra/newview/skins/default/xui/en/floater_emoji_complete.xml b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d290d647e8a7ca84485302b1664a3caad701b225
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ name="emoji_complete"
+ single_instance="true"
+ layout="topleft"
+ bg_opaque_image="Window_NoTitle_Foreground"
+ bg_alpha_image="Window_NoTitle_Background"
+ can_close="false"
+ can_dock="false"
+ can_drag_on_left="false"
+ can_minimize="false"
+ can_resize="false"
+ can_tear_off="false"
+ header_height="0"
+ legacy_header_height="0"
+ show_title="false"
+ width="240"
+ height="40"
+ >
+	<emoji_complete
+	 name="emoji_complete_ctrl"
+	 follows="top|left"
+	 layout="topleft"
+	 autosize="true"
+	 vertical="true"
+	 max_visible="7"
+	 padding="4"
+	 width="230"
+	 height="30"
+	 left="5"
+	 top="5"
+	 >
+	</emoji_complete>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d21f8c82bc5cb8bfc0d2dcd72c4d168eece9d254
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+    name="emojipicker"
+    title="CHOOSE EMOJI"
+    help_topic="emojipicker"
+    single_instance="true"
+    can_minimize="false"
+    can_tear_off="false"
+    can_resize="true"
+    auto_close="true"
+    layout="topleft"
+    min_width="250"
+    chrome="true"
+    height="350"
+    width="304">
+  <floater.string name="title_for_recently_used" value="Recently used"/>
+  <floater.string name="title_for_frequently_used" value="Frequently used"/>
+  <scroll_container
+      name="EmojiGridContainer"
+      layout="topleft"
+      follows="all"
+      ignore_arrow_keys="true"
+      top="25"
+      left="0"
+      height="275">
+    <scrolling_panel_list
+        name="EmojiGrid"
+        layout="topleft"
+        follows="top|left|right"
+        padding="4"
+        spacing="0"
+        top="0"
+        left="0"/>
+  </scroll_container>
+  <panel
+      name="Groups"
+      layout="topleft"
+      follows="top|left|right"
+      top="0"
+      left="0"
+      height="25">
+    <panel
+      name="Badge"
+      layout="bottomleft"
+      follows="bottom|left"
+      background_visible="true"
+      background_opaque="true"
+      bg_opaque_color="FrogGreen"
+      tab_stop="false"
+      bottom="0"
+      height="2"
+      width="20"
+      />
+  </panel>
+  <text
+      name="Dummy"
+      type="string"
+      layout="bottomleft"
+      follows="bottom|left|right"
+      halign="center"
+      valign="center"
+      bottom="14"
+      left="10"
+      height="25">No emoji selected</text>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml
index da84fbeea62449f6dda28ec0b43b44fc5fb38d17..a6493c5e24fb73e193e5071660d6d6a9d6905385 100644
--- a/indra/newview/skins/default/xui/en/floater_im_session.xml
+++ b/indra/newview/skins/default/xui/en/floater_im_session.xml
@@ -238,7 +238,7 @@
                 </layout_stack>
             </layout_panel>
             <layout_panel
-             height="35"
+             height="30"
              auto_resize="false"
              name="chat_layout_panel">
                 <layout_stack
@@ -249,7 +249,7 @@
                  name="input_panels"
                  top="0"
                  bottom="-1"
-                 left="0"
+                 left="1"
                  right="-1">
                     <layout_panel
                      name="input_editor_layout_panel">
@@ -260,7 +260,7 @@
                          default_icon_name="Generic_Person"
                          layout="topleft"
                          left="3"
-                         bottom="-9"
+                         bottom="-4"
                          visible="false"
                          width="20" />
                         <group_icon
@@ -270,7 +270,7 @@
                          default_icon_name="Generic_Group"
                          layout="topleft"
                          left="3"
-                         bottom="-9"
+                         bottom="-4"
                          visible="false"
                          width="20" />
                         <icon
@@ -279,7 +279,7 @@
                          image_name="Nearby_chat_icon"
                          layout="topleft"
                          left="3"
-                         bottom="-9"
+                         bottom="-4"
                          name="nearby_chat_icon"
                          visible="false"
                          width="20"/>
@@ -288,17 +288,31 @@
                          expand_lines_count="5"
                          follows="left|right|bottom"
                          font="SansSerifSmall"
-                         height="20"    
+                         height="20"
                          is_expandable="true"
                          text_tentative_color="TextFgTentativeColor"
+                         bg_writeable_color="ScriptBackground"
                          name="chat_editor"
                          max_length="1023"
                          spellcheck="true"
                          tab_group="3"
-                         bottom="-8"
+                         bottom="-3"
                          left_pad="5"
-                         right="-5"
+                         right="-30"
                          wrap="true" />
+                        <button
+                         name="emoji_recent_panel_toggle_btn"
+                         tool_tip="Shows/hides recent emojis"
+                         follows="right|bottom"
+                         font="EmojiLarge"
+                         image_hover_unselected="Toolbar_Middle_Over"
+                         image_selected="Toolbar_Middle_Selected"
+                         image_unselected="Toolbar_Middle_Off"
+                         image_overlay="Emoji_Picker_Icon"
+                         bottom="-2"
+                         right="-1"
+                         height="25"
+                         width="25"/>
                     </layout_panel>
                     <layout_panel
                      auto_resize="false"
@@ -319,6 +333,42 @@
                     </layout_panel>
                 </layout_stack>
             </layout_panel>
+            <layout_panel
+             name="emoji_recent_layout_panel"
+             height="30"
+             auto_resize="false">
+                <text
+                 name="emoji_recent_empty_text"
+                 follows="top|left|right"
+                 layout="topleft"
+                 auto_resize="false"
+                 h_pad="20"
+                 v_pad="10"
+                 top="0"
+                 left="1"
+                 right="-65"
+                 height="30"
+                >Recently used emojis will appear here</text>
+                <emoji_complete
+                 name="emoji_recent_icons_ctrl"
+                 follows="top|left|right"
+                 layout="topleft"
+                 max_visible="20"
+                 top="0"
+                 left="1"
+                 right="-65"
+                 height="30"/>
+                <button
+                 name="emoji_picker_show_btn"
+                 label="More"
+                 tool_tip="Shows/hides emoji picker"
+                 follows="right|bottom"
+                 layout="topleft"
+                 bottom="-5"
+                 right="-3"
+                 height="20"
+                 width="60"/>
+            </layout_panel>
         </layout_stack>
     </view>
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml
index dcbdfa8794dc52ae23eff12b7d99f457b0fb6ebc..ac5467c0368bac57120acf3cec8e7d9b82e18e20 100644
--- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml
+++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml
@@ -73,6 +73,8 @@
      spellcheck="true"
      tab_group="1"
      top="46"
+     use_color="true"
+     show_emoji_helper="true"
      width="392"
      word_wrap="true">
         Loading...
diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml
index d88c267a95e63bccedf1e939fe602e9ff304a858..40045625fdd53a4f7374c93ef8ae25f139dc1d0c 100644
--- a/indra/newview/skins/default/xui/en/fonts.xml
+++ b/indra/newview/skins/default/xui/en/fonts.xml
@@ -3,6 +3,7 @@
 
   <font name="default" comment="default font files (global fallbacks)">
     <file>DejaVuSans.ttf</file>
+    <file functor="is_emoji">TwemojiSVG.ttf</file>
     <os name="Windows">
       <file>meiryo.TTC</file>
       <file>MSGOTHIC.TTC</file>
@@ -69,6 +70,11 @@
     <file>DejaVuSans-BoldOblique.ttf</file>
   </font>
 
+  <font name="Emoji"
+	comment="Name of emoji font">
+    <file>TwemojiSVG.ttf</file>
+  </font>
+
   <font name="Monospace"
 	comment="Name of monospace font">
     <file>DejaVuSansMono.ttf</file>
diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml
index 96fac1c6e845dbd8bbde3fc0c654c28f425f6c6a..40399b33ef2337f263625941ae3c87cd0a9f0972 100644
--- a/indra/newview/skins/default/xui/en/menu_login.xml
+++ b/indra/newview/skins/default/xui/en/menu_login.xml
@@ -159,6 +159,32 @@
              parameter="ui_preview" />
         </menu_item_call>
       <menu_item_separator />
+      <menu
+       create_jump_keys="true"
+       label="Fonts"
+       name="Fonts"
+       tear_off="true">
+        <menu_item_call
+         label="Show Font Test"
+         name="Show Font Test">
+          <menu_item_call.on_click
+           function="Floater.Show"
+           parameter="font_test" />
+        </menu_item_call>
+        <menu_item_separator />
+        <menu_item_call
+         label="Dump Fonts"
+         name="Dump Fonts">
+          <menu_item_call.on_click
+           function="Develop.Fonts.Dump" />
+        </menu_item_call>
+        <menu_item_call
+         label="Dump Font Textures"
+         name="Dump Font Textures">
+          <menu_item_call.on_click
+           function="Develop.Fonts.DumpTextures" />
+        </menu_item_call>
+      </menu>
       <menu
        create_jump_keys="true"
        label="UI Tests"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 9b238693e0ef88ca95c87a77fc0590dde111d2a5..4719b091ab4f89b57f049c5c294440f1058da2a4 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2716,6 +2716,12 @@ function="World.EnvPreset"
                 <menu_item_call.on_click
                  function="Advanced.ForceErrorLlerror" />
             </menu_item_call>
+            <menu_item_call
+             label="Force LLError, Message And Crash"
+             name="Force LLError And Crash">
+                <menu_item_call.on_click
+                 function="Advanced.ForceErrorLlerrorMsg" />
+            </menu_item_call>
             <menu_item_call
              label="Force Bad Memory Access"
              name="Force Bad Memory Access">
@@ -3537,6 +3543,18 @@ function="World.EnvPreset"
              function="Advanced.WebContentTest"
              parameter="http://duckduckgo.com"/>
           </menu_item_call>
+          <menu_item_call
+           label="Dump Fonts"
+           name="Dump Fonts">
+            <menu_item_call.on_click
+             function="Develop.Fonts.Dump" />
+          </menu_item_call>
+          <menu_item_call
+           label="Dump Font Textures"
+           name="Dump Font Textures">
+            <menu_item_call.on_click
+             function="Develop.Fonts.DumpTextures" />
+          </menu_item_call>
           <menu_item_call
              label="Dump SelectMgr"
              name="Dump SelectMgr">
@@ -3605,6 +3623,14 @@ function="World.EnvPreset"
                 <menu_item_check.on_click
                  function="Advanced.ToggleDebugViews" />
             </menu_item_check>
+            <menu_item_check
+             label="Debug Unicode"
+             name="Debug Unicode">
+                <menu_item_check.on_check
+                 function="Advanced.CheckDebugUnicode" />
+                <menu_item_check.on_click
+                 function="Advanced.ToggleDebugUnicode" />
+            </menu_item_check>
             <menu_item_check
              label="Debug Name Tooltips"
              name="Debug Name Tooltips">
@@ -4412,7 +4438,7 @@ function="World.EnvPreset"
                     <menu_item_call.on_click
                      function="PromptShowURL"
                      name="PublicIssueTracker_url"
-                     parameter="WebLaunchPublicIssue,http://jira.secondlife.com" />
+                     parameter="WebLaunchPublicIssue,https://feedback.secondlife.com/" />
                 </menu_item_call>
                 <menu_item_call
                  label="Public Issue Tracker Help"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
index fe74cea2f12fcf06f472ae503d17a33235170158..8a9d3b755e9dc69c0edfe36725487d0e337bf615 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
@@ -27,7 +27,7 @@
     left_delta="110"
     name="preset_text"
     top="5"
-    width="120">
+    width="320">
       (None)
   </text>
 
diff --git a/indra/newview/skins/default/xui/en/panel_region_terrain.xml b/indra/newview/skins/default/xui/en/panel_region_terrain.xml
index 88855ab739b9b997f2b1a82740246cc08b88e88c..f8d2c90d0a3e722013e2a2060d0064b954ff56ce 100644
--- a/indra/newview/skins/default/xui/en/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/en/panel_region_terrain.xml
@@ -76,33 +76,17 @@
      left="8"
      top="30"
      width="460" />
-    <combo_box
-     layout="topleft"
-     left="10"
-     top="105"
-     follows="left|top"
-     name="terrain_material_type"
-     width="170">
-        <combo_box.item
-         label="Terrain Textures"
-         name="Textures"
-         value="Textures" />
-        <combo_box.item
-         label="Terrain PBR Materials"
-         name="PBRMaterials"
-         value="PBRMaterials" />
-    </combo_box>
     <text
      type="string"
      length="1"
-     follows="left|top"
      halign="left"
      valign="center"
+     follows="left|top"
      height="20"
      layout="topleft"
      name="detail_texture_text"
-     top_delta="0"
-     left_delta="0"
+     left="10"
+     top="105"
      width="170">
         Terrain Textures
     </text>
@@ -114,12 +98,25 @@
      follows="left|top"
      height="20"
      layout="topleft"
-     top_delta="0"
-     left_delta="180"
-     name="detail_texture_limits_text"
-     width="200">
-        Maximum size: 1024x1024
+     name="detail_material_text"
+     left="10"
+     top="105"
+     width="170">
+        Terrain Materials
     </text>
+    <check_box
+     height="20"
+     halign="left"
+     valign="center"
+     follows="left|top"
+     layout="topleft"
+     top_delta="1"
+     left_delta="180"
+     label="PBR Metallic Roughness"
+     name="terrain_material_type"
+     tool_tip="If checked, use PBR Metallic Roughness materials for terrain. Otherwise, use textures."
+     left_pad="2"
+     width="200" />
     <texture_picker
      follows="left|top"
      height="100"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index e405d9ea10aadf9325c2580a361eeccbf67f6449..7360c06f86145de580426b4dcb22f5aae907bbfe 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -490,9 +490,9 @@ http://secondlife.com/support for help fixing this problem.
 	<!-- build floater -->
 	<string name="multiple_textures">Multiple</string>
 
-<string name="use_texture">Use texture</string>
-    <string name="manip_hint1">Move mouse cursor over ruler</string>
-    <string name="manip_hint2">to snap to grid</string>
+	<string name="use_texture">Use texture</string>
+	<string name="manip_hint1">Move mouse cursor over ruler</string>
+	<string name="manip_hint2">to snap to grid</string>
 
 	<!-- world map -->
 	<string name="texture_loading">Loading...</string>
@@ -507,14 +507,14 @@ http://secondlife.com/support for help fixing this problem.
 
 	<!-- Chat -->
 	<string name="NearbyChatTitle">Nearby chat</string>
-  <string name="NearbyChatLabel">(Nearby chat)</string>
+	<string name="NearbyChatLabel">(Nearby chat)</string>
 	<string name="whisper">whispers:</string>
 	<string name="shout">shouts:</string>
 	<string name="ringing">Connecting to in-world Voice Chat...</string>
 	<string name="connected">Connected</string>
 	<string name="unavailable">Voice not available at your current location</string>
 	<string name="hang_up">Disconnected from in-world Voice Chat</string>
-  <string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string>
+	<string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string>
 	<string name="ScriptQuestionCautionChatGranted">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been granted permission to: [PERMISSIONS].</string>
 	<string name="ScriptQuestionCautionChatDenied">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been denied permission to: [PERMISSIONS].</string>
 	<string name="AdditionalPermissionsRequestHeader">If you allow access to your account, you will also be allowing the object to:</string>
@@ -3019,8 +3019,19 @@ Running in window.
 
 If you continue to receive this message, contact the [SUPPORT_SITE].
 	</string>
+    <string name="MBOutOfMemoryTitle">Out Of Memory</string>
+    <string name="MBOutOfMemoryErr">
+        [APP_NAME]'s request for memory failed. Application can't proceed and will be closed.
 
-	<!-- Avatar Shape Information -->
+If your computer's RAM is low, quit any heavy applications before runing Second Life, allocate a page file or reduce graphical settings like draw distance.
+    </string>
+    <string name="MBMissingFile">
+        [APP_NAME] couldn't access or find some of the files it needs and will be closed.
+
+Please reinstall viewer from  https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall.
+    </string>
+
+    <!-- Avatar Shape Information -->
 <string name="5 O'Clock Shadow">5 O'Clock Shadow</string>
 
 <string name="All White">All White</string>
diff --git a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml
index f9facb593ac79e2cd1282909524a560c9fcdf876..c550f634e54d2cf9a5071758fbebd31725a35c52 100644
--- a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml
+++ b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml
@@ -1,4 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <chat_editor
   name="chat_editor"
-  show_context_menu="true"/>
+  show_context_menu="true"
+  show_emoji_helper="true"
+  use_color="true"
+  />
diff --git a/indra/newview/skins/default/xui/en/widgets/chat_history.xml b/indra/newview/skins/default/xui/en/widgets/chat_history.xml
index c0a948931c90b7465bc9232170ef8769e543b862..c4300c93502216bdb90a3dcc9f35a2319cf16200 100644
--- a/indra/newview/skins/default/xui/en/widgets/chat_history.xml
+++ b/indra/newview/skins/default/xui/en/widgets/chat_history.xml
@@ -10,11 +10,11 @@
   bottom_separator_pad="1"
   top_header_pad="12"
   bottom_header_pad="5"
-	max_length="2147483647"
-	track_bottom="true"
-	name="chat_history"
-	type="string"
-	word_wrap="true"
+  max_length="2147483647"
+  track_bottom="true"
+  name="chat_history"
+  type="string"
+  word_wrap="true"
   line_spacing.multiple="1.0" 
   font="SansSerif">
   <more_chat_text
diff --git a/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6cc8d7118f65374f6d40a3e6a9ae2eb93cc35ed9
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<emoji_complete
+  autosize="false"
+  hover_image="ListItem_Over"
+  selected_image="ListItem_Select"
+  max_visible="7"
+  padding="8"
+  >
+</emoji_complete>
diff --git a/indra/newview/skins/default/xui/es/emoji_categories.xml b/indra/newview/skins/default/xui/es/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b1b73eba5e1b3c8ee784764c5466cc3a882a5049
--- /dev/null
+++ b/indra/newview/skins/default/xui/es/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>emoticonos y emoción</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>personas y cuerpo</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>componentes</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>animales y la naturaleza</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>comida y bebida</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>viajes y lugares</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>actividades</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>objetos</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>símbolos</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/fr/emoji_categories.xml b/indra/newview/skins/default/xui/fr/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..38dc9cb8f8c555036b60f22f0e5886f27c2d9c6d
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>smileys et émotion</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>les gens et le corps</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>composants</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>animaux et la nature</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>nourriture et boissons</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>voyages et lieux</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>activités</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>objets</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>symboles</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/it/emoji_categories.xml b/indra/newview/skins/default/xui/it/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a4782e60a69a652232229115ad25ea2c0164e93c
--- /dev/null
+++ b/indra/newview/skins/default/xui/it/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>smileys and emozione</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>persone e corpo</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>componenti</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>animali and natura</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>cibo e bevande</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>viaggi and luoghi</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>attività</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>oggetti</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>simboli</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/ja/emoji_categories.xml b/indra/newview/skins/default/xui/ja/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7750f4ad2e78aa5360c9fefea6db124237a68f8b
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>スマイリーと感情</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>人体</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>コンポーネント</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>動物自然</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>飲み物・食べ物</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>旅行・場所</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>有効化</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>オブジェクト</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>シンボル</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/pl/emoji_categories.xml b/indra/newview/skins/default/xui/pl/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9aad7af794f2155cf458fb491b100499131ed565
--- /dev/null
+++ b/indra/newview/skins/default/xui/pl/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>buźki and emocje</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>ludzie and ciało</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>składniki</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>zwierzęta and przyroda</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>jedzenie i picie</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>podróże and miejsca</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>aktywność</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>objekt</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>symbole</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/pt/emoji_categories.xml b/indra/newview/skins/default/xui/pt/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..887444b9574628d87a462daaf713c608966068bb
--- /dev/null
+++ b/indra/newview/skins/default/xui/pt/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>sorrisos e emoção</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>pessoas e corpo</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>componentes</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>animais e natureza</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>comida e bebida</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>viagens e lugares</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>atividades</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>objetos</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>símbolos</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/ru/emoji_categories.xml b/indra/newview/skins/default/xui/ru/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b08f0d811771067292ca158b2a94cc93006ffd07
--- /dev/null
+++ b/indra/newview/skins/default/xui/ru/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>смайлики и люди</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>тело людей</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>компонент</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>животные и природа</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>еда и напитки</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>путешествия и местности</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>варианты досуга</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>предметы</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>символы</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/skins/default/xui/zh/emoji_categories.xml b/indra/newview/skins/default/xui/zh/emoji_categories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fbe6165eebc4437e4f4b425d9b2e497dd1ca11af
--- /dev/null
+++ b/indra/newview/skins/default/xui/zh/emoji_categories.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd">
+  <array>
+    <map>
+      <key>Name</key>
+      <string>smileys and emotion</string>
+      <key>Category</key>
+      <string>笑脸</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>people and body</string>
+      <key>Category</key>
+      <string>人体</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>components</string>
+      <key>Category</key>
+      <string>组件</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>animals and nature</string>
+      <key>Category</key>
+      <string>野生动物</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>food and drink</string>
+      <key>Category</key>
+      <string>食物飲料</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>travel and places</string>
+      <key>Category</key>
+      <string>旅遊地點</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>activities</string>
+      <key>Category</key>
+      <string>个人活动</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>objects</string>
+      <key>Category</key>
+      <string>物件</string>
+    </map>
+    <map>
+      <key>Name</key>
+      <string>symbols</string>
+      <key>Category</key>
+      <string>人的符号</string>
+    </map>
+  </array>
+</llsd>
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 1fa4df16824551c2a6dfdd66469ae810e3b886a8..c7f32d0da9320f375991154df8f755764d418adb 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -141,7 +141,7 @@ def construct(self):
                 self.path("*.tga")
 
             # Include our fonts
-            with self.prefix(src_dst="fonts"):
+            with self.prefix(src="../packages/fonts",src_dst="fonts"):
                 self.path("*.ttf")
                 self.path("*.txt")
 
@@ -559,6 +559,10 @@ def construct(self):
                 self.path("OpenAL32.dll")
                 self.path("alut.dll")
 
+            # For ICU4C
+            self.path("icudt48.dll")
+            self.path("icuuc48.dll")
+
             # For textures
             self.path("openjp2.dll")
 
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index a265e1273bb83de2226db3970fc4e46bf7a234c4..94478a5263c912dee0608f26ea0ebf2e4f38414c 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -54,6 +54,12 @@
 #endif
 
 #ifndef LL_WINDOWS
+
+typedef struct {
+  void *re_pcre;
+  size_t re_nsub;
+  size_t re_erroffset;
+} regex_t;
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #endif