Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 03 Aug 2015 15:04:31 -0400
changeset 287583 d191709cd9331027bd000b77c7db93f85a12392c
parent 287582 0ab5f96fa1046fd3ae518dea4871f46f187ac7f7 (current diff)
parent 287559 abc56d57f6e1aebade48949fb557d26eae555df8 (diff)
child 287584 397afd7d1e6bd7e6510502af3fe921244a863b5e
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
dom/bluetooth/bluedroid/BluetoothDaemonConnector.cpp
dom/bluetooth/bluedroid/BluetoothDaemonConnector.h
dom/push/test/test_push_manager_worker.html
gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
mobile/android/tests/browser/robocop/testAboutLogins.java
mobile/android/tests/browser/robocop/testAboutLogins.js
mobile/android/tests/browser/robocop/testAppConstants.java
mobile/android/tests/browser/robocop/testAppConstants.js
security/manager/ssl/tests/unit/test_keysize/cert9.db
security/manager/ssl/tests/unit/test_keysize/key4.db
security/manager/ssl/tests/unit/test_keysize/pkcs11.txt
testing/docker/ubuntu-build/REPOSITORY
testing/docker/ubuntu32-build/REPOSITORY
testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled.html
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1011,17 +1011,16 @@ pref("network.proxy.pac_generator", true
 // comma.  Specify '*' in the list to apply to all apps.
 pref("network.proxy.browsing.app_origins", "app://system.gaiamobile.org");
 
 // Enable Web Speech synthesis API
 pref("media.webspeech.synth.enabled", true);
 
 // Enable Web Speech recognition API
 pref("media.webspeech.recognition.enable", true);
-pref("media.webspeech.service.default", "pocketsphinx");
 
 // Downloads API
 pref("dom.mozDownloads.enabled", true);
 pref("dom.downloads.max_retention_days", 7);
 
 // External Helper Application Handling
 //
 // All external helper application handling can require the docshell to be
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "8dba2077f5e7137253fbb3faf10cd0b5f7da25c2", 
+        "git_revision": "dbacf8364f4505d021b7d8fb9cabea325004dbcc", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "4a0216037684d9f9299e8ca7470ca05c2ead4ab5", 
+    "revision": "5612f302a6b5bb48cd518d114c38597df570b66c", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8dba2077f5e7137253fbb3faf10cd0b5f7da25c2"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dbacf8364f4505d021b7d8fb9cabea325004dbcc"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ddfd98cdafefaa1b60273d5568b8dbd13730dae"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
@@ -5,27 +5,26 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothDaemonInterface.h"
 #include <cutils/properties.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include "BluetoothDaemonA2dpInterface.h"
 #include "BluetoothDaemonAvrcpInterface.h"
-#include "BluetoothDaemonConnector.h"
 #include "BluetoothDaemonGattInterface.h"
 #include "BluetoothDaemonHandsfreeInterface.h"
 #include "BluetoothDaemonHelpers.h"
 #include "BluetoothDaemonSetupInterface.h"
 #include "BluetoothDaemonSocketInterface.h"
 #include "mozilla/ipc/DaemonRunnables.h"
 #include "mozilla/ipc/DaemonSocket.h"
+#include "mozilla/ipc/DaemonSocketConnector.h"
 #include "mozilla/ipc/ListenSocket.h"
 #include "mozilla/unused.h"
-#include "prrng.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 using namespace mozilla::ipc;
 
 static const int sRetryInterval = 100; // ms
 
 //
@@ -1758,56 +1757,16 @@ public:
   }
 
 private:
   BluetoothDaemonInterface* mInterface;
   nsRefPtr<BluetoothResultHandler> mRes;
   bool mRegisteredSocketModule;
 };
 
-nsresult
-BluetoothDaemonInterface::CreateRandomAddressString(
-  const nsACString& aPrefix, unsigned long aPostfixLength,
-  nsACString& aAddress)
-{
-  static const char sHexChar[16] = {
-    [0x0] = '0', [0x1] = '1', [0x2] = '2', [0x3] = '3',
-    [0x4] = '4', [0x5] = '5', [0x6] = '6', [0x7] = '7',
-    [0x8] = '8', [0x9] = '9', [0xa] = 'a', [0xb] = 'b',
-    [0xc] = 'c', [0xd] = 'd', [0xe] = 'e', [0xf] = 'f'
-  };
-
-  unsigned short seed[3];
-
-  if (NS_WARN_IF(!PR_GetRandomNoise(seed, sizeof(seed)))) {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
-  aAddress = aPrefix;
-  aAddress.Append('-');
-
-  while (aPostfixLength) {
-
-    // Android doesn't provide rand_r, so we use nrand48 here,
-    // even though it's deprecated.
-    long value = nrand48(seed);
-
-    size_t bits = sizeof(value) * CHAR_BIT;
-
-    while ((bits > 4) && aPostfixLength) {
-      aAddress.Append(sHexChar[value&0xf]);
-      bits -= 4;
-      value >>= 4;
-      --aPostfixLength;
-    }
-  }
-
-  return NS_OK;
-}
-
 /*
  * The init procedure consists of several steps.
  *
  *  (1) Start listening for the command channel's socket connection: We
  *      do this before anything else, so that we don't miss connection
  *      requests from the Bluetooth daemon. This step will create a
  *      listen socket.
  *
@@ -1869,24 +1828,23 @@ BluetoothDaemonInterface::Init(
   }
 
   // The listen socket's name is generated with a random postfix. This
   // avoids naming collisions if we still have a listen socket from a
   // previously failed cleanup. It also makes it hard for malicious
   // external programs to capture the socket name or connect before
   // the daemon can do so. If no random postfix can be generated, we
   // simply use the base name as-is.
-  nsresult rv = CreateRandomAddressString(NS_LITERAL_CSTRING(BASE_SOCKET_NAME),
-                                          POSTFIX_LENGTH,
-                                          mListenSocketName);
+  nsresult rv = DaemonSocketConnector::CreateRandomAddressString(
+    NS_LITERAL_CSTRING(BASE_SOCKET_NAME), POSTFIX_LENGTH, mListenSocketName);
   if (NS_FAILED(rv)) {
     mListenSocketName.AssignLiteral(BASE_SOCKET_NAME);
   }
 
-  rv = mListenSocket->Listen(new BluetoothDaemonConnector(mListenSocketName),
+  rv = mListenSocket->Listen(new DaemonSocketConnector(mListenSocketName),
                              mCmdChannel);
   if (NS_FAILED(rv)) {
     OnConnectError(CMD_CHANNEL);
     return;
   }
 
   // The protocol implementation needs a command channel for
   // sending commands to the daemon. We set it here, because
--- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
@@ -134,20 +134,16 @@ protected:
     LISTEN_SOCKET,
     CMD_CHANNEL,
     NTF_CHANNEL
   };
 
   BluetoothDaemonInterface();
   ~BluetoothDaemonInterface();
 
-  nsresult CreateRandomAddressString(const nsACString& aPrefix,
-                                     unsigned long aPostfixLength,
-                                     nsACString& aAddress);
-
   // Methods for |DaemonSocketConsumer| and |ListenSocketConsumer|
   //
 
   void OnConnectSuccess(int aIndex) override;
   void OnConnectError(int aIndex) override;
   void OnDisconnect(int aIndex) override;
 
 private:
--- a/dom/bluetooth/moz.build
+++ b/dom/bluetooth/moz.build
@@ -87,17 +87,16 @@ if CONFIG['MOZ_B2G_BT']:
                 'bluez',
             ]
             DEFINES['MOZ_B2G_BT_BLUEZ'] = True
         elif CONFIG['MOZ_B2G_BT_DAEMON']:
             SOURCES += [
                 'bluedroid/BluetoothA2dpManager.cpp',
                 'bluedroid/BluetoothDaemonA2dpInterface.cpp',
                 'bluedroid/BluetoothDaemonAvrcpInterface.cpp',
-                'bluedroid/BluetoothDaemonConnector.cpp',
                 'bluedroid/BluetoothDaemonGattInterface.cpp',
                 'bluedroid/BluetoothDaemonHandsfreeInterface.cpp',
                 'bluedroid/BluetoothDaemonHelpers.cpp',
                 'bluedroid/BluetoothDaemonInterface.cpp',
                 'bluedroid/BluetoothDaemonSetupInterface.cpp',
                 'bluedroid/BluetoothDaemonSocketInterface.cpp',
                 'bluedroid/BluetoothOppManager.cpp',
                 'bluedroid/BluetoothPbapManager.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_SetNFCFocus.js
@@ -0,0 +1,30 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1188639 - Check permission to use setNFCFocus
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+function hasSetNFCFocus() {
+  return new Promise((resolve, reject) => {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('mozbrowser', 'true');
+    iframe.addEventListener('mozbrowserloadend', e => {
+      is(iframe.setNFCFocus !== undefined, true,
+         "has permission to use setNFCFocus");
+      resolve();
+    });
+    document.body.appendChild(iframe);
+  });
+}
+
+function runTest() {
+  SpecialPowers.pushPermissions(
+    [{ 'type': 'nfc-manager', 'allow': 1, 'context': document }],
+    () => hasSetNFCFocus().then(SimpleTest.finish));
+}
+
+addEventListener('testready', runTest);
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -108,8 +108,9 @@ disabled = bug 930449
 [test_browserElement_oop_CloseFromOpener.html]
 disabled = bug 924771
 [test_browserElement_oop_CloseApp.html]
 disabled = bug 924771
 [test_browserElement_oop_ExposableURI.html]
 disabled = bug 924771
 [test_browserElement_oop_GetContentDimensions.html]
 [test_browserElement_oop_AudioChannel.html]
+[test_browserElement_oop_SetNFCFocus.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -56,16 +56,17 @@ support-files =
   browserElement_Reload.js
   browserElement_ReloadPostRequest.js
   browserElement_RemoveBrowserElement.js
   browserElement_ScrollEvent.js
   browserElement_SecurityChange.js
   browserElement_SendEvent.js
   browserElement_SelectionStateBlur.js
   browserElement_SetInputMethodActive.js
+  browserElement_SetNFCFocus.js
   browserElement_SetVisible.js
   browserElement_SetVisibleFrames.js
   browserElement_SetVisibleFrames2.js
   browserElement_Stop.js
   browserElement_TargetBlank.js
   browserElement_TargetTop.js
   browserElement_Titlechange.js
   browserElement_TopBarrier.js
@@ -225,8 +226,9 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_XFrameOptionsSameOrigin.html]
 [test_browserElement_oop_NextPaint.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 [test_browserElement_inproc_Reload.html]
 disabled = bug 774100
 [test_browserElement_inproc_GetContentDimensions.html]
 [test_browserElement_inproc_AudioChannel.html]
+[test_browserElement_inproc_SetNFCFocus.html]
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_SetNFCFocus.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1188639</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_SetNFCFocus.js">
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_SetNFCFocus.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1188639</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_SetNFCFocus.js">
+</script>
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -7208,17 +7208,17 @@ HTMLInputElement::SetFilePickerFiltersFr
       filterMask = nsIFilePicker::filterAudio;
       filterBundle->GetStringFromName(MOZ_UTF16("audioFilter"),
                                       getter_Copies(extensionListStr));
     } else if (token.EqualsLiteral("video/*")) {
       filterMask = nsIFilePicker::filterVideo;
       filterBundle->GetStringFromName(MOZ_UTF16("videoFilter"),
                                       getter_Copies(extensionListStr));
     } else if (token.First() == '.') {
-      if (token.FindChar(';') >= 0  || token.FindChar('*') >= 0) {
+      if (token.Contains(';') || token.Contains('*')) {
         // Ignore this filter as it contains reserved characters
         continue;
       }
       extensionListStr = NS_LITERAL_STRING("*") + token;
       filterName = extensionListStr;
       atLeastOneFileExtensionFilter = true;
     } else {
       //... if no image/audio/video filter is found, check mime types filters
--- a/dom/media/webspeech/recognition/PocketSphinxSpeechRecognitionService.cpp
+++ b/dom/media/webspeech/recognition/PocketSphinxSpeechRecognitionService.cpp
@@ -49,23 +49,26 @@ public:
                                    // thread!
 
     // Declare javascript result events
     nsRefPtr<SpeechEvent> event = new SpeechEvent(
       mRecognition, SpeechRecognition::EVENT_RECOGNITIONSERVICE_FINAL_RESULT);
     SpeechRecognitionResultList* resultList =
       new SpeechRecognitionResultList(mRecognition);
     SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
-    SpeechRecognitionAlternative* alternative =
-      new SpeechRecognitionAlternative(mRecognition);
+    ErrorResult rv;
+    if (0 < mRecognition->GetMaxAlternatives(rv)) { // GetMaxAlternatives can't fail
+      SpeechRecognitionAlternative* alternative =
+        new SpeechRecognitionAlternative(mRecognition);
 
-    alternative->mTranscript = mResult;
-    alternative->mConfidence = 100;
+      alternative->mTranscript = mResult;
+      alternative->mConfidence = 100;
 
-    result->mItems.AppendElement(alternative);
+      result->mItems.AppendElement(alternative);
+    }
     resultList->mItems.AppendElement(result);
 
     event->mRecognitionResultList = resultList;
     NS_DispatchToMainThread(event);
 
     // If we don't destroy the thread when we're done with it, it will hang
     // around forever... bad!
     // But thread->Shutdown must be called from the main thread, not from the
@@ -325,21 +328,24 @@ PocketSphinxSpeechRecognitionService::Ob
 }
 
 SpeechRecognitionResultList*
 PocketSphinxSpeechRecognitionService::BuildMockResultList()
 {
   SpeechRecognitionResultList* resultList =
     new SpeechRecognitionResultList(mRecognition);
   SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
-  SpeechRecognitionAlternative* alternative =
-    new SpeechRecognitionAlternative(mRecognition);
+  ErrorResult rv;
+  if (0 < mRecognition->GetMaxAlternatives(rv)) { // GetMaxAlternatives can't fail
+    SpeechRecognitionAlternative* alternative =
+      new SpeechRecognitionAlternative(mRecognition);
 
-  alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
-  alternative->mConfidence = 0.0f;
+    alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
+    alternative->mConfidence = 0.0f;
 
-  result->mItems.AppendElement(alternative);
+    result->mItems.AppendElement(alternative);
+  }
   resultList->mItems.AppendElement(result);
 
   return resultList;
 }
 
 } // namespace mozilla
--- a/dom/media/webspeech/recognition/SpeechGrammarList.cpp
+++ b/dom/media/webspeech/recognition/SpeechGrammarList.cpp
@@ -18,40 +18,32 @@ namespace dom {
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SpeechGrammarList, mParent, mItems)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechGrammarList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechGrammarList)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechGrammarList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-SpeechGrammarList::SpeechGrammarList(nsISupports* aParent, nsISpeechRecognitionService* aRecognitionService)
+SpeechGrammarList::SpeechGrammarList(nsISupports* aParent)
   : mParent(aParent)
 {
-  this->mRecognitionService = aRecognitionService;
 }
 
 SpeechGrammarList::~SpeechGrammarList()
 {
 }
 
 already_AddRefed<SpeechGrammarList>
 SpeechGrammarList::Constructor(const GlobalObject& aGlobal,
                                ErrorResult& aRv)
 {
-  nsCOMPtr<nsISpeechRecognitionService> recognitionService;
-  recognitionService = GetSpeechRecognitionService();
-  if (!recognitionService) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return nullptr;
-  } else {
-    nsRefPtr<SpeechGrammarList> speechGrammarList =
-      new SpeechGrammarList(aGlobal.GetAsSupports(), recognitionService);
-    return speechGrammarList.forget();
-  }
+  nsRefPtr<SpeechGrammarList> speechGrammarList =
+    new SpeechGrammarList(aGlobal.GetAsSupports());
+  return speechGrammarList.forget();
 }
 
 JSObject*
 SpeechGrammarList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return SpeechGrammarListBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -86,17 +78,16 @@ SpeechGrammarList::AddFromURI(const nsAS
 void
 SpeechGrammarList::AddFromString(const nsAString& aString,
                                  const Optional<float>& aWeight,
                                  ErrorResult& aRv)
 {
   SpeechGrammar* speechGrammar = new SpeechGrammar(mParent);
   speechGrammar->SetSrc(aString, aRv);
   mItems.AppendElement(speechGrammar);
-  mRecognitionService->ValidateAndSetGrammarList(speechGrammar, nullptr);
   return;
 }
 
 already_AddRefed<SpeechGrammar>
 SpeechGrammarList::IndexedGetter(uint32_t aIndex, bool& aPresent,
                                  ErrorResult& aRv)
 {
   if (aIndex >= Length()) {
--- a/dom/media/webspeech/recognition/SpeechGrammarList.h
+++ b/dom/media/webspeech/recognition/SpeechGrammarList.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_SpeechGrammarList_h
 #define mozilla_dom_SpeechGrammarList_h
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "nsISpeechRecognitionService.h"
 
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
@@ -24,17 +23,17 @@ namespace dom {
 class GlobalObject;
 class SpeechGrammar;
 template<typename> class Optional;
 
 class SpeechGrammarList final : public nsISupports,
                                 public nsWrapperCache
 {
 public:
-  explicit SpeechGrammarList(nsISupports* aParent, nsISpeechRecognitionService* aRecognitionService);
+  explicit SpeechGrammarList(nsISupports* aParent);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SpeechGrammarList)
 
   static already_AddRefed<SpeechGrammarList> Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
   nsISupports* GetParentObject() const;
 
@@ -45,18 +44,16 @@ public:
   already_AddRefed<SpeechGrammar> Item(uint32_t aIndex, ErrorResult& aRv);
 
   void AddFromURI(const nsAString& aSrc, const Optional<float>& aWeight, ErrorResult& aRv);
 
   void AddFromString(const nsAString& aString, const Optional<float>& aWeight, ErrorResult& aRv);
 
   already_AddRefed<SpeechGrammar> IndexedGetter(uint32_t aIndex, bool& aPresent, ErrorResult& aRv);
 
-  nsCOMPtr<nsISpeechRecognitionService> mRecognitionService;
-
 private:
   ~SpeechGrammarList();
 
   nsCOMPtr<nsISupports> mParent;
 
   nsTArray<nsRefPtr<SpeechGrammar>> mItems;
 };
 
--- a/dom/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp
@@ -5,42 +5,46 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SpeechRecognition.h"
 
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Element.h"
 #include "mozilla/dom/SpeechRecognitionBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
 
 #include "AudioSegment.h"
 #include "endpointer.h"
 
 #include "mozilla/dom/SpeechRecognitionEvent.h"
+#include "nsIDocument.h"
 #include "nsIObserverService.h"
+#include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsQueryObject.h"
 
 #include <algorithm>
 
 // Undo the windows.h damage
 #if defined(XP_WIN) && defined(GetMessage)
 #undef GetMessage
 #endif
 
 namespace mozilla {
 namespace dom {
 
 #define PREFERENCE_DEFAULT_RECOGNITION_SERVICE "media.webspeech.service.default"
-#define DEFAULT_RECOGNITION_SERVICE "pocketsphinx"
+#define DEFAULT_RECOGNITION_SERVICE_PREFIX "pocketsphinx-"
+#define DEFAULT_RECOGNITION_SERVICE "pocketsphinx-en-US"
 
 #define PREFERENCE_ENDPOINTER_SILENCE_LENGTH "media.webspeech.silence_length"
 #define PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH "media.webspeech.long_silence_length"
 #define PREFERENCE_ENDPOINTER_LONG_SPEECH_LENGTH "media.webspeech.long_speech_length"
 
 static const uint32_t kSAMPLE_RATE = 16000;
 static const uint32_t kSPEECH_DETECTION_TIMEOUT_MS = 10000;
 
@@ -57,59 +61,68 @@ GetSpeechRecognitionLog()
     sLog = PR_NewLogModule("SpeechRecognition");
   }
 
   return sLog;
 }
 #define SR_LOG(...) MOZ_LOG(GetSpeechRecognitionLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 already_AddRefed<nsISpeechRecognitionService>
-GetSpeechRecognitionService()
+GetSpeechRecognitionService(const nsAString& aLang)
 {
   nsAutoCString speechRecognitionServiceCID;
 
   nsAdoptingCString prefValue =
   Preferences::GetCString(PREFERENCE_DEFAULT_RECOGNITION_SERVICE);
   nsAutoCString speechRecognitionService;
 
-  if (!prefValue.get() || prefValue.IsEmpty()) {
+  if (!aLang.IsEmpty()) {
+    speechRecognitionService =
+      NS_LITERAL_CSTRING(DEFAULT_RECOGNITION_SERVICE_PREFIX) +
+      NS_ConvertUTF16toUTF8(aLang);
+  } else if (!prefValue.IsEmpty()) {
+    speechRecognitionService = prefValue;
+  } else {
     speechRecognitionService = DEFAULT_RECOGNITION_SERVICE;
-  } else {
-    speechRecognitionService = prefValue;
   }
 
-  if (!SpeechRecognition::mTestConfig.mFakeRecognitionService){
+  if (SpeechRecognition::mTestConfig.mFakeRecognitionService) {
+    speechRecognitionServiceCID =
+      NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake";
+  } else {
     speechRecognitionServiceCID =
       NS_LITERAL_CSTRING(NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX) +
       speechRecognitionService;
-  } else {
-    speechRecognitionServiceCID =
-      NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake";
   }
 
   nsresult rv;
   nsCOMPtr<nsISpeechRecognitionService> recognitionService;
   recognitionService = do_GetService(speechRecognitionServiceCID.get(), &rv);
   return recognitionService.forget();
 }
 
-NS_INTERFACE_MAP_BEGIN(SpeechRecognition)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(SpeechRecognition, DOMEventTargetHelper, mDOMStream, mSpeechGrammarList)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SpeechRecognition)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 
 struct SpeechRecognition::TestConfig SpeechRecognition::mTestConfig;
 
 SpeechRecognition::SpeechRecognition(nsPIDOMWindow* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mEndpointer(kSAMPLE_RATE)
   , mAudioSamplesPerChunk(mEndpointer.FrameSize())
   , mSpeechDetectionTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
+  , mSpeechGrammarList(new SpeechGrammarList(GetParentObject()))
+  , mInterimResults(false)
+  , mMaxAlternatives(1)
 {
   SR_LOG("created SpeechRecognition");
 
   mTestConfig.Init();
   if (mTestConfig.mEnableTests) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     obs->AddObserver(this, SPEECH_RECOGNITION_TEST_EVENT_REQUEST_TOPIC, false);
     obs->AddObserver(this, SPEECH_RECOGNITION_TEST_END_TOPIC, false);
@@ -614,41 +627,38 @@ SpeechRecognition::ProcessTestEventReque
 
     // let the fake recognition service handle the request
   }
 
   return;
 }
 
 already_AddRefed<SpeechGrammarList>
-SpeechRecognition::GetGrammars(ErrorResult& aRv) const
+SpeechRecognition::Grammars() const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
+  nsRefPtr<SpeechGrammarList> speechGrammarList = mSpeechGrammarList;
+  return speechGrammarList.forget();
 }
 
 void
-SpeechRecognition::SetGrammars(SpeechGrammarList& aArg, ErrorResult& aRv)
+SpeechRecognition::SetGrammars(SpeechGrammarList& aArg)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return;
+  mSpeechGrammarList = &aArg;
 }
 
 void
-SpeechRecognition::GetLang(nsString& aRetVal, ErrorResult& aRv) const
+SpeechRecognition::GetLang(nsString& aRetVal) const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return;
+  aRetVal = mLang;
 }
 
 void
-SpeechRecognition::SetLang(const nsAString& aArg, ErrorResult& aRv)
+SpeechRecognition::SetLang(const nsAString& aArg)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return;
+  mLang = aArg;
 }
 
 bool
 SpeechRecognition::GetContinuous(ErrorResult& aRv) const
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return false;
 }
@@ -656,40 +666,38 @@ SpeechRecognition::GetContinuous(ErrorRe
 void
 SpeechRecognition::SetContinuous(bool aArg, ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return;
 }
 
 bool
-SpeechRecognition::GetInterimResults(ErrorResult& aRv) const
+SpeechRecognition::InterimResults() const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return false;
+  return mInterimResults;
 }
 
 void
-SpeechRecognition::SetInterimResults(bool aArg, ErrorResult& aRv)
+SpeechRecognition::SetInterimResults(bool aArg)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  mInterimResults = aArg;
   return;
 }
 
 uint32_t
 SpeechRecognition::GetMaxAlternatives(ErrorResult& aRv) const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return 0;
+  return mMaxAlternatives;
 }
 
 void
 SpeechRecognition::SetMaxAlternatives(uint32_t aArg, ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  mMaxAlternatives = aArg;
   return;
 }
 
 void
 SpeechRecognition::GetServiceURI(nsString& aRetVal, ErrorResult& aRv) const
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return;
@@ -705,18 +713,21 @@ SpeechRecognition::SetServiceURI(const n
 void
 SpeechRecognition::Start(const Optional<NonNull<DOMMediaStream>>& aStream, ErrorResult& aRv)
 {
   if (mCurrentState != STATE_IDLE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  mRecognitionService = GetSpeechRecognitionService();
-  if (NS_WARN_IF(!mRecognitionService)) {
+  if (!SetRecognitionService(aRv)) {
+    return;
+  }
+
+  if (!ValidateAndSetGrammarList(aRv)) {
     return;
   }
 
   nsresult rv;
   rv = mRecognitionService->Initialize(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
@@ -734,16 +745,87 @@ SpeechRecognition::Start(const Optional<
                           new GetUserMediaSuccessCallback(this),
                           new GetUserMediaErrorCallback(this));
   }
 
   nsRefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_START);
   NS_DispatchToMainThread(event);
 }
 
+bool
+SpeechRecognition::SetRecognitionService(ErrorResult& aRv)
+{
+  // See: https://dvcs.w3.org/hg/speech-api/raw-file/tip/webspeechapi.html#dfn-lang
+  if (!mLang.IsEmpty()) {
+    mRecognitionService = GetSpeechRecognitionService(mLang);
+
+    if (!mRecognitionService) {
+      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return false;
+    }
+
+    return true;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if(!window) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+  nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
+  if(!document) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+  nsCOMPtr<Element> element = document->GetRootElement();
+  if(!element) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  nsAutoString lang;
+  element->GetLang(lang);
+  mRecognitionService = GetSpeechRecognitionService(lang);
+
+  if (!mRecognitionService) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+SpeechRecognition::ValidateAndSetGrammarList(ErrorResult& aRv)
+{
+  if (!mSpeechGrammarList) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  uint32_t grammarListLength = mSpeechGrammarList->Length();
+  if (0 == grammarListLength) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  for (uint32_t count = 0; count < grammarListLength; ++count) {
+    nsRefPtr<SpeechGrammar> speechGrammar = mSpeechGrammarList->Item(count, aRv);
+    if (aRv.Failed()) {
+      return false;
+    }
+    if (NS_FAILED(mRecognitionService->ValidateAndSetGrammarList(speechGrammar.get(), nullptr))) {
+      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void
 SpeechRecognition::Stop()
 {
   nsRefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_STOP);
   NS_DispatchToMainThread(event);
 }
 
 void
--- a/dom/media/webspeech/recognition/SpeechRecognition.h
+++ b/dom/media/webspeech/recognition/SpeechRecognition.h
@@ -44,54 +44,53 @@ namespace dom {
 #define SPEECH_RECOGNITION_TEST_END_TOPIC "SpeechRecognitionTest:End"
 
 class GlobalObject;
 class SpeechEvent;
 
 PRLogModuleInfo* GetSpeechRecognitionLog();
 #define SR_LOG(...) MOZ_LOG(GetSpeechRecognitionLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
-already_AddRefed<nsISpeechRecognitionService> GetSpeechRecognitionService();
-
 class SpeechRecognition final : public DOMEventTargetHelper,
                                 public nsIObserver,
                                 public SupportsWeakPtr<SpeechRecognition>
 {
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SpeechRecognition)
   explicit SpeechRecognition(nsPIDOMWindow* aOwnerWindow);
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 
   NS_DECL_NSIOBSERVER
 
   nsISupports* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static bool IsAuthorized(JSContext* aCx, JSObject* aGlobal);
 
   static already_AddRefed<SpeechRecognition>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
-  already_AddRefed<SpeechGrammarList> GetGrammars(ErrorResult& aRv) const;
+  already_AddRefed<SpeechGrammarList> Grammars() const;
 
-  void SetGrammars(mozilla::dom::SpeechGrammarList& aArg, ErrorResult& aRv);
+  void SetGrammars(mozilla::dom::SpeechGrammarList& aArg);
 
-  void GetLang(nsString& aRetVal, ErrorResult& aRv) const;
+  void GetLang(nsString& aRetVal) const;
 
-  void SetLang(const nsAString& aArg, ErrorResult& aRv);
+  void SetLang(const nsAString& aArg);
 
   bool GetContinuous(ErrorResult& aRv) const;
 
   void SetContinuous(bool aArg, ErrorResult& aRv);
 
-  bool GetInterimResults(ErrorResult& aRv) const;
+  bool InterimResults() const;
 
-  void SetInterimResults(bool aArg, ErrorResult& aRv);
+  void SetInterimResults(bool aArg);
 
   uint32_t GetMaxAlternatives(ErrorResult& aRv) const;
 
   void SetMaxAlternatives(uint32_t aArg, ErrorResult& aRv);
 
   void GetServiceURI(nsString& aRetVal, ErrorResult& aRv) const;
 
   void SetServiceURI(const nsAString& aArg, ErrorResult& aRv);
@@ -171,16 +170,19 @@ private:
     STATE_RECOGNIZING,
     STATE_WAITING_FOR_RESULT,
     STATE_COUNT
   };
 
   void SetState(FSMState state);
   bool StateBetween(FSMState begin, FSMState end);
 
+  bool SetRecognitionService(ErrorResult& aRv);
+  bool ValidateAndSetGrammarList(ErrorResult& aRv);
+
   class GetUserMediaSuccessCallback : public nsIDOMGetUserMediaSuccessCallback
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIDOMGETUSERMEDIASUCCESSCALLBACK
 
     explicit GetUserMediaSuccessCallback(SpeechRecognition* aRecognition)
       : mRecognition(aRecognition)
@@ -244,16 +246,42 @@ private:
   // buffer holds one chunk of mAudioSamplesPerChunk
   // samples before feeding it to mEndpointer
   nsRefPtr<SharedBuffer> mAudioSamplesBuffer;
   uint32_t mBufferedSamples;
 
   nsCOMPtr<nsITimer> mSpeechDetectionTimer;
   bool mAborted;
 
+  nsString mLang;
+
+  nsRefPtr<SpeechGrammarList> mSpeechGrammarList;
+
+  // WebSpeechAPI (http://bit.ly/1gIl7DC) states:
+  //
+  // 1. Default value MUST be false
+  // 2. If true, interim results SHOULD be returned
+  // 3. If false, interim results MUST NOT be returned
+  //
+  // Pocketsphinx does not return interm results; so, defaulting
+  // mInterimResults to false, then ignoring its subsequent value
+  // is a conforming implementation.
+  bool mInterimResults;
+
+  // WebSpeechAPI (http://bit.ly/1JAiqeo) states:
+  //
+  // 1. Default value is 1
+  // 2. Subsequent value is the "maximum number of SpeechRecognitionAlternatives per result"
+  //
+  // Pocketsphinx can only return at maximum a single SpeechRecognitionAlternative
+  // per SpeechRecognitionResult. So defaulting mMaxAlternatives to 1, for all non
+  // zero values ignoring mMaxAlternatives while for a 0 value returning no
+  // SpeechRecognitionAlternative per result is a conforming implementation.
+  uint32_t mMaxAlternatives;
+
   void ProcessTestEventRequest(nsISupports* aSubject, const nsAString& aEventName);
 
   const char* GetName(FSMState aId);
   const char* GetName(SpeechEvent* aId);
 };
 
 class SpeechEvent : public nsRunnable
 {
--- a/dom/media/webspeech/recognition/test/FakeSpeechRecognitionService.cpp
+++ b/dom/media/webspeech/recognition/test/FakeSpeechRecognitionService.cpp
@@ -97,20 +97,23 @@ FakeSpeechRecognitionService::Observe(ns
   return NS_OK;
 }
 
 SpeechRecognitionResultList*
 FakeSpeechRecognitionService::BuildMockResultList()
 {
   SpeechRecognitionResultList* resultList = new SpeechRecognitionResultList(mRecognition);
   SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
-  SpeechRecognitionAlternative* alternative = new SpeechRecognitionAlternative(mRecognition);
+  ErrorResult rv;
+  if (0 < mRecognition->GetMaxAlternatives(rv)) { // GetMaxAlternatives can't fail
+    SpeechRecognitionAlternative* alternative = new SpeechRecognitionAlternative(mRecognition);
 
-  alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
-  alternative->mConfidence = 0.0f;
+    alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
+    alternative->mConfidence = 0.0f;
 
-  result->mItems.AppendElement(alternative);
+    result->mItems.AppendElement(alternative);
+  }
   resultList->mItems.AppendElement(result);
 
   return resultList;
 }
 
 } // namespace mozilla
--- a/dom/media/webspeech/recognition/test/head.js
+++ b/dom/media/webspeech/recognition/test/head.js
@@ -39,16 +39,21 @@ function EventManager(sr) {
   var eventDependencies = {
     "speechend": "speechstart",
     "soundend": "soundstart",
     "audioend": "audiostart"
   };
 
   var isDone = false;
 
+  // set up grammar
+  var sgl = new SpeechGrammarList();
+  sgl.addFromString("#JSGF V1.0; grammar test; public <simple> = hello ;", 1);
+  sr.grammars = sgl;
+
   // AUDIO_DATA events are asynchronous,
   // so we queue events requested while they are being
   // issued to make them seem synchronous
   var isSendingAudioData = false;
   var queuedEventRequests = [];
 
   // register default handlers
   for (var i = 0; i < allEvents.length; i++) {
--- a/dom/media/webspeech/recognition/test/test_audio_capture_error.html
+++ b/dom/media/webspeech/recognition/test/test_audio_capture_error.html
@@ -18,18 +18,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="text/javascript">
   SimpleTest.waitForExplicitFinish();
 
   performTest({
     eventsToRequest: ['EVENT_AUDIO_ERROR'],
     expectedEvents: {
+      'start': null,
+      'audiostart': null,
+      'speechstart': null,
+      'speechend': null,
+      'audioend': null,
       'error': buildErrorCallback(errorCodes.AUDIO_CAPTURE),
       'end': null
     },
     doneFunc: SimpleTest.finish,
-    prefs: [["media.webspeech.test.fake_fsm_events", true]]
+    prefs: [["media.webspeech.test.fake_fsm_events", true], ["media.webspeech.test.fake_recognition_service", true]]
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/nfc/gonk/NfcService.cpp
+++ b/dom/nfc/gonk/NfcService.cpp
@@ -2,121 +2,186 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NfcService.h"
 #include <binder/Parcel.h>
 #include <cutils/properties.h>
-#include "mozilla/ModuleUtils.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/NfcOptionsBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/ipc/ListenSocket.h"
+#include "mozilla/ipc/ListenSocketConsumer.h"
 #include "mozilla/ipc/NfcConnector.h"
+#include "mozilla/ipc/StreamSocket.h"
+#include "mozilla/ipc/StreamSocketConsumer.h"
+#include "mozilla/ModuleUtils.h"
 #include "mozilla/unused.h"
+#include "NfcMessageHandler.h"
+#include "NfcOptions.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsXULAppAPI.h"
-#include "NfcOptions.h"
 
 #define NS_NFCSERVICE_CID \
   { 0x584c9a21, 0x4e17, 0x43b7, {0xb1, 0x6a, 0x87, 0xa0, 0x42, 0xef, 0xd4, 0x64} }
 #define NS_NFCSERVICE_CONTRACTID "@mozilla.org/nfc/service;1"
 
 using namespace android;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
-namespace {
-
-class SendNfcSocketDataTask final : public nsRunnable
-{
-public:
-  SendNfcSocketDataTask(StreamSocket* aSocket, UnixSocketRawData* aRawData)
-    : mSocket(aSocket)
-    , mRawData(aRawData)
-  { }
-
-  NS_IMETHOD Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (!mSocket || mSocket->GetConnectionStatus() != SOCKET_CONNECTED) {
-      // Probably shutting down.
-      return NS_OK;
-    }
-
-    mSocket->SendSocketData(mRawData.forget());
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<StreamSocket> mSocket;
-  nsAutoPtr<UnixSocketRawData> mRawData;
-};
-
-} // namespace
-
 namespace mozilla {
 
-static NfcService* gNfcService;
+static StaticRefPtr<NfcService> gNfcService;
 
 NS_IMPL_ISUPPORTS(NfcService, nsINfcService)
 
-void
-assertIsNfcServiceThread()
+//
+// NfcConsumer
+//
+
+/**
+ * |NfcConsumer| implements the details of the connection to an NFC daemon
+ * as well as the message passing.
+ */
+class NfcConsumer
+  : public ListenSocketConsumer
+  , public StreamSocketConsumer
 {
-  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
-  MOZ_ASSERT(thread == gNfcService->GetThread());
+public:
+  NfcConsumer(NfcService* aNfcService);
+
+  nsresult Start();
+  void Shutdown();
+
+  nsresult Send(const CommandOptions& aCommandOptions);
+  nsresult Receive(UnixSocketBuffer* aBuffer);
+
+  // Methods for |StreamSocketConsumer| and |ListenSocketConsumer|
+  //
+
+  void ReceiveSocketData(
+    int aIndex, nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer) override;
+
+  void OnConnectSuccess(int aIndex) override;
+  void OnConnectError(int aIndex) override;
+  void OnDisconnect(int aIndex) override;
+
+private:
+  class DispatchNfcEventRunnable;
+  class ShutdownServiceRunnable;
+
+  enum SocketType {
+    LISTEN_SOCKET,
+    STREAM_SOCKET
+  };
+
+  bool IsNfcServiceThread() const;
+
+  nsRefPtr<NfcService> mNfcService;
+  nsCOMPtr<nsIThread> mThread;
+  nsRefPtr<mozilla::ipc::ListenSocket> mListenSocket;
+  nsRefPtr<mozilla::ipc::StreamSocket> mStreamSocket;
+  nsAutoPtr<NfcMessageHandler> mHandler;
+  nsCString mListenSocketName;
+};
+
+NfcConsumer::NfcConsumer(NfcService* aNfcService)
+  : mNfcService(aNfcService)
+{
+  MOZ_ASSERT(mNfcService);
 }
 
-// Runnable used to call Marshall on the NFC thread.
-class NfcCommandRunnable final : public nsRunnable
+nsresult
+NfcConsumer::Start()
 {
-public:
-  NfcCommandRunnable(NfcMessageHandler* aHandler,
-                     NfcService* aService,
-                     CommandOptions aOptions)
-    : mHandler(aHandler)
-    , mService(aService)
-    , mOptions(aOptions)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
+  static const char BASE_SOCKET_NAME[] = "nfcd";
+
+  MOZ_ASSERT(!mThread); // already started
+
+  // Store a pointer of the consumer's NFC thread for
+  // use with |IsNfcServiceThread|.
+  mThread = do_GetCurrentThread();
+
+  // If we could not cleanup properly before and an old
+  // instance of the daemon is still running, we kill it
+  // here.
+  unused << NS_WARN_IF(property_set("ctl.stop", "nfcd") < 0);
+
+  mHandler = new NfcMessageHandler();
+
+  mStreamSocket = new StreamSocket(this, STREAM_SOCKET);
+
+  mListenSocketName = BASE_SOCKET_NAME;
+
+  mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
+  nsresult rv = mListenSocket->Listen(new NfcConnector(mListenSocketName),
+                                      mStreamSocket);
+  if (NS_FAILED(rv)) {
+    mStreamSocket = nullptr;
+    mHandler = nullptr;
+    mThread = nullptr;
+    return rv;
   }
 
-  NS_IMETHOD Run()
-  {
-    assertIsNfcServiceThread();
+  return NS_OK;
+}
+
+void
+NfcConsumer::Shutdown()
+{
+  MOZ_ASSERT(IsNfcServiceThread());
+
+  mListenSocket->Close();
+  mListenSocket = nullptr;
+  mStreamSocket->Close();
+  mStreamSocket = nullptr;
 
-    Parcel parcel;
-    parcel.writeInt32(0); // Parcel Size.
-    mHandler->Marshall(parcel, mOptions);
-    parcel.setDataPosition(0);
-    uint32_t sizeBE = htonl(parcel.dataSize() - sizeof(int));
-    parcel.writeInt32(sizeBE);
-    mService->PostToNfcDaemon(parcel.data(), parcel.dataSize());
-    return NS_OK;
+  mHandler = nullptr;
+
+  mThread = nullptr;
+}
+
+nsresult
+NfcConsumer::Send(const CommandOptions& aOptions)
+{
+  MOZ_ASSERT(IsNfcServiceThread());
+
+  if (NS_WARN_IF(!mStreamSocket) ||
+      NS_WARN_IF(mStreamSocket->GetConnectionStatus() != SOCKET_CONNECTED)) {
+    return NS_OK; // Probably shutting down.
   }
 
-private:
-   NfcMessageHandler* mHandler;
-   NfcService* mService;
-   CommandOptions mOptions;
-};
+  Parcel parcel;
+  parcel.writeInt32(0); // Parcel Size.
+  mHandler->Marshall(parcel, aOptions);
+  parcel.setDataPosition(0);
+  uint32_t sizeBE = htonl(parcel.dataSize() - sizeof(int));
+  parcel.writeInt32(sizeBE);
+
+  // TODO: Zero-copy buffer transfers
+  mStreamSocket->SendSocketData(
+    new UnixSocketRawData(parcel.data(), parcel.dataSize()));
+
+  return NS_OK;
+}
 
 // Runnable used dispatch the NfcEventOptions on the main thread.
-class NfcEventDispatcher : public nsRunnable
+class NfcConsumer::DispatchNfcEventRunnable final : public nsRunnable
 {
 public:
-  NfcEventDispatcher(EventOptions& aEvent)
-    : mEvent(aEvent)
+  DispatchNfcEventRunnable(NfcService* aNfcService, const EventOptions& aEvent)
+    : mNfcService(aNfcService)
+    , mEvent(aEvent)
   {
-    assertIsNfcServiceThread();
+    MOZ_ASSERT(mNfcService);
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mozilla::AutoSafeJSContext cx;
     RootedDictionary<NfcEventOptions> event(cx);
@@ -245,64 +310,132 @@ public:
       event.mResponse.Construct();
       event.mResponse.Value().Init(
         Uint8Array::Create(cx, mEvent.mResponse.Length(), mEvent.mResponse.Elements()));
     }
 
 #undef COPY_FIELD
 #undef COPY_OPT_FIELD
 
-    gNfcService->DispatchNfcEvent(event);
+    mNfcService->DispatchNfcEvent(event);
     return NS_OK;
   }
 
 private:
+  nsRefPtr<NfcService> mNfcService;
   EventOptions mEvent;
 };
 
-// Runnable used to call Unmarshall on the NFC thread.
-class NfcEventRunnable : public nsRunnable
+nsresult
+NfcConsumer::Receive(UnixSocketBuffer* aBuffer)
 {
-public:
-  NfcEventRunnable(NfcMessageHandler* aHandler, UnixSocketBuffer* aData)
-    : mHandler(aHandler), mData(aData)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(IsNfcServiceThread());
+  MOZ_ASSERT(mHandler);
+  MOZ_ASSERT(aBuffer);
+
+  while (aBuffer->GetSize()) {
+    const uint8_t* data = aBuffer->GetData();
+    uint32_t parcelSize = ((data[0] & 0xff) << 24) |
+                          ((data[1] & 0xff) << 16) |
+                          ((data[2] & 0xff) <<  8) |
+                           (data[3] & 0xff);
+    MOZ_ASSERT(parcelSize <= aBuffer->GetSize());
+
+    // TODO: Zero-copy buffer transfers
+    Parcel parcel;
+    parcel.setData(aBuffer->GetData(), parcelSize + sizeof(parcelSize));
+    aBuffer->Consume(parcelSize + sizeof(parcelSize));
+
+    EventOptions event;
+    mHandler->Unmarshall(parcel, event);
+
+    NS_DispatchToMainThread(new DispatchNfcEventRunnable(mNfcService, event));
   }
 
-  NS_IMETHOD Run()
-  {
-    assertIsNfcServiceThread();
+  return NS_OK;
+}
+
+bool
+NfcConsumer::IsNfcServiceThread() const
+{
+  return nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread;
+}
+
+// |StreamSocketConsumer|, |ListenSocketConsumer|
+
+void
+NfcConsumer::ReceiveSocketData(
+  int aIndex, nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer)
+{
+  MOZ_ASSERT(IsNfcServiceThread());
+  MOZ_ASSERT(aIndex == STREAM_SOCKET);
+
+  Receive(aBuffer);
+}
+
+void
+NfcConsumer::OnConnectSuccess(int aIndex)
+{
+  MOZ_ASSERT(IsNfcServiceThread());
 
-    while (mData->GetSize()) {
-      EventOptions event;
-      const uint8_t* data = mData->GetData();
-      uint32_t parcelSize = ((data[0] & 0xff) << 24) |
-                            ((data[1] & 0xff) << 16) |
-                            ((data[2] & 0xff) <<  8) |
-                             (data[3] & 0xff);
-      MOZ_ASSERT(parcelSize <= mData->GetSize());
+  switch (aIndex) {
+    case LISTEN_SOCKET: {
+      nsCString value("nfcd:-S -a ");
+      value.Append(mListenSocketName);
+      if (NS_WARN_IF(property_set("ctl.start", value.get()) < 0)) {
+        OnConnectError(STREAM_SOCKET);
+      }
+      break;
+    }
+    case STREAM_SOCKET:
+      /* nothing to do */
+      break;
+  }
+}
 
-      Parcel parcel;
-      parcel.setData(mData->GetData(), parcelSize + sizeof(parcelSize));
-      mHandler->Unmarshall(parcel, event);
-      nsCOMPtr<nsIRunnable> runnable = new NfcEventDispatcher(event);
-      NS_DispatchToMainThread(runnable);
+class NfcConsumer::ShutdownServiceRunnable final : public nsRunnable
+{
+public:
+  ShutdownServiceRunnable(NfcService* aNfcService)
+    : mNfcService(aNfcService)
+  {
+    MOZ_ASSERT(mNfcService);
+  }
 
-      mData->Consume(parcelSize + sizeof(parcelSize));
-    }
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mNfcService->Shutdown();
 
     return NS_OK;
   }
 
 private:
-  NfcMessageHandler* mHandler;
-  nsAutoPtr<UnixSocketBuffer> mData;
+  nsRefPtr<NfcService> mNfcService;
 };
 
+void
+NfcConsumer::OnConnectError(int aIndex)
+{
+  MOZ_ASSERT(IsNfcServiceThread());
+
+  NS_DispatchToMainThread(new ShutdownServiceRunnable(mNfcService));
+}
+
+void
+NfcConsumer::OnDisconnect(int aIndex)
+{
+  MOZ_ASSERT(IsNfcServiceThread());
+}
+
+//
+// NfcService
+//
+
 NfcService::NfcService()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!gNfcService);
 }
 
 NfcService::~NfcService()
 {
@@ -318,173 +451,226 @@ NfcService::FactoryCreate()
 
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gNfcService) {
     gNfcService = new NfcService();
     ClearOnShutdown(&gNfcService);
   }
 
-  nsRefPtr<NfcService> service = gNfcService;
+  nsRefPtr<NfcService> service(gNfcService);
   return service.forget();
 }
 
+/**
+ * |StartConsumerRunnable| calls |NfcConsumer::Start| on the NFC thread.
+ */
+class NfcService::StartConsumerRunnable final : public nsRunnable
+{
+public:
+  StartConsumerRunnable(NfcConsumer* aNfcConsumer)
+    : mNfcConsumer(aNfcConsumer)
+  {
+    MOZ_ASSERT(mNfcConsumer);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    mNfcConsumer->Start();
+
+    return NS_OK;
+  }
+
+private:
+  NfcConsumer* mNfcConsumer;
+};
+
 NS_IMETHODIMP
 NfcService::Start(nsINfcGonkEventListener* aListener)
 {
-  static const char BASE_SOCKET_NAME[] = "nfcd";
-
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
   MOZ_ASSERT(!mThread);
-  MOZ_ASSERT(!mListenSocket);
+  MOZ_ASSERT(!mNfcConsumer);
 
-  // If we could not cleanup properly before and an old
-  // instance of the daemon is still running, we kill it
-  // here.
-  unused << NS_WARN_IF(property_set("ctl.stop", "nfcd") < 0);
+  nsAutoPtr<NfcConsumer> nfcConsumer(new NfcConsumer(this));
 
-  mListener = aListener;
-  mHandler = new NfcMessageHandler();
-  mStreamSocket = new StreamSocket(this, STREAM_SOCKET);
-
-  mListenSocketName = BASE_SOCKET_NAME;
-
-  mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
-  nsresult rv = mListenSocket->Listen(new NfcConnector(mListenSocketName),
-                                      mStreamSocket);
+  nsresult rv = NS_NewNamedThread("NfcThread", getter_AddRefs(mThread));
   if (NS_FAILED(rv)) {
-    mStreamSocket = nullptr;
+    NS_WARNING("Can't create Nfc worker thread.");
     return rv;
   }
 
-  rv = NS_NewNamedThread("NfcThread", getter_AddRefs(mThread));
+  rv = mThread->Dispatch(new StartConsumerRunnable(nfcConsumer),
+                         nsIEventTarget::DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
-    NS_WARNING("Can't create Nfc worker thread.");
-    mListenSocket->Close();
-    mListenSocket = nullptr;
-    mStreamSocket->Close();
-    mStreamSocket = nullptr;
-    return NS_ERROR_FAILURE;
+    return rv;
   }
 
+  mListener = aListener;
+  mNfcConsumer = nfcConsumer.forget();
+
   return NS_OK;
 }
 
+/**
+ * |CleanupRunnable| deletes instances of the NFC consumer and
+ * thread on the main thread. This has to be down after shutting
+ * down the NFC consumer on the NFC thread.
+ */
+class NfcService::CleanupRunnable final : public nsRunnable
+{
+public:
+  CleanupRunnable(NfcConsumer* aNfcConsumer,
+                  already_AddRefed<nsIThread> aThread)
+    : mNfcConsumer(aNfcConsumer)
+    , mThread(aThread)
+  {
+    MOZ_ASSERT(mNfcConsumer);
+    MOZ_ASSERT(mThread);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mNfcConsumer = nullptr; // deletes NFC consumer
+
+    mThread->Shutdown();
+    mThread = nullptr; // deletes NFC worker thread
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<NfcConsumer> mNfcConsumer;
+  nsCOMPtr<nsIThread> mThread;
+};
+
+/**
+ * |ShutdownConsumerRunnable| calls |NfcConsumer::Shutdown| on the
+ * NFC thread. Optionally, it can dispatch a |CleanupRunnable| to
+ * the main thread for cleaning up the NFC resources.
+ */
+class NfcService::ShutdownConsumerRunnable final : public nsRunnable
+{
+public:
+  ShutdownConsumerRunnable(NfcConsumer* aNfcConsumer, bool aCleanUp)
+    : mNfcConsumer(aNfcConsumer)
+    , mCleanUp(aCleanUp)
+  {
+    MOZ_ASSERT(mNfcConsumer);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    mNfcConsumer->Shutdown();
+
+    if (mCleanUp) {
+      NS_DispatchToMainThread(
+        new CleanupRunnable(mNfcConsumer, do_GetCurrentThread()));
+    }
+
+    return NS_OK;
+  }
+
+private:
+  NfcConsumer* mNfcConsumer;
+  bool mCleanUp;
+};
+
 NS_IMETHODIMP
 NfcService::Shutdown()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mThread) {
-    mThread->Shutdown();
-    mThread = nullptr;
+  if (!mNfcConsumer) {
+    return NS_OK; // NFC was shut down meanwhile; not an error
   }
 
-  mListenSocket->Close();
-  mListenSocket = nullptr;
-  mStreamSocket->Close();
-  mStreamSocket = nullptr;
+  nsresult rv = mThread->Dispatch(new ShutdownConsumerRunnable(mNfcConsumer,
+                                                               true),
+                                  nsIEventTarget::DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // |CleanupRunnable| will take care of these pointers
+  unused << mNfcConsumer.forget();
+  unused << mThread.forget();
 
   return NS_OK;
 }
 
-bool
-NfcService::PostToNfcDaemon(const uint8_t* aData, size_t aSize)
+/**
+ * |SendRunnable| calls |NfcConsumer::Send| on the NFC thread.
+ */
+class NfcService::SendRunnable final : public nsRunnable
 {
-  MOZ_ASSERT(!NS_IsMainThread());
+public:
+  SendRunnable(NfcConsumer* aNfcConsumer, const CommandOptions& aOptions)
+    : mNfcConsumer(aNfcConsumer)
+    , mOptions(aOptions)
+  {
+    MOZ_ASSERT(mNfcConsumer);
+  }
 
-  UnixSocketRawData* raw = new UnixSocketRawData(aData, aSize);
-  nsRefPtr<SendNfcSocketDataTask> task =
-    new SendNfcSocketDataTask(mStreamSocket, raw);
-  NS_DispatchToMainThread(task);
-  return true;
-}
+  NS_IMETHOD Run() override
+  {
+    mNfcConsumer->Send(mOptions);
+
+    return NS_OK;
+  }
+
+private:
+   NfcConsumer* mNfcConsumer;
+   CommandOptions mOptions;
+};
 
 NS_IMETHODIMP
 NfcService::SendCommand(JS::HandleValue aOptions, JSContext* aCx)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NfcCommandOptions options;
 
   if (!options.Init(aCx, aOptions)) {
     NS_WARNING("Bad dictionary passed to NfcService::SendCommand");
     return NS_ERROR_FAILURE;
   }
 
   // Dispatch the command to the NFC thread.
-  CommandOptions commandOptions(options);
-  nsCOMPtr<nsIRunnable> runnable = new NfcCommandRunnable(mHandler, this,
-                                                          commandOptions);
-  mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
+  nsresult rv = mThread->Dispatch(new SendRunnable(mNfcConsumer,
+                                                   CommandOptions(options)),
+                                  nsIEventTarget::DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   return NS_OK;
 }
 
 void
 NfcService::DispatchNfcEvent(const mozilla::dom::NfcEventOptions& aOptions)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mListener);
+
+  if (!mNfcConsumer) {
+    return; // NFC has been shutdown meanwhile; not en error
+  }
 
   mozilla::AutoSafeJSContext cx;
   JS::RootedValue val(cx);
 
   if (!ToJSValue(cx, aOptions, &val)) {
     return;
   }
 
   mListener->OnEvent(val);
 }
 
-// |StreamSocketConsumer|, |ListenSocketConsumer|
-
-void
-NfcService::ReceiveSocketData(
-  int aIndex, nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer)
-{
-  MOZ_ASSERT(mHandler);
-  nsCOMPtr<nsIRunnable> runnable =
-    new NfcEventRunnable(mHandler, aBuffer.forget());
-  mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
-}
-
-void
-NfcService::OnConnectSuccess(int aIndex)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  switch (aIndex) {
-    case LISTEN_SOCKET: {
-        nsCString value("nfcd:-S -a ");
-        value.Append(mListenSocketName);
-        if (NS_WARN_IF(property_set("ctl.start", value.get()) < 0)) {
-          OnConnectError(STREAM_SOCKET);
-        }
-      }
-      break;
-    case STREAM_SOCKET:
-      /* nothing to do */
-      break;
-  }
-}
-
-void
-NfcService::OnConnectError(int aIndex)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  Shutdown();
-}
-
-void
-NfcService::OnDisconnect(int aIndex)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-}
-
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(NfcService,
                                          NfcService::FactoryCreate)
 
 NS_DEFINE_NAMED_CID(NS_NFCSERVICE_CID);
 
 static const mozilla::Module::CIDEntry kNfcServiceCIDs[] = {
   { &kNS_NFCSERVICE_CID, false, nullptr, NfcServiceConstructor },
   { nullptr }
--- a/dom/nfc/gonk/NfcService.h
+++ b/dom/nfc/gonk/NfcService.h
@@ -2,71 +2,47 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NfcService_h
 #define NfcService_h
 
-#include <mozilla/ipc/ListenSocket.h>
-#include "mozilla/ipc/ListenSocketConsumer.h"
-#include <mozilla/ipc/StreamSocket.h>
-#include "mozilla/ipc/StreamSocketConsumer.h"
 #include "nsCOMPtr.h"
 #include "nsINfcService.h"
-#include "NfcMessageHandler.h"
 
 class nsIThread;
 
 namespace mozilla {
 namespace dom {
 class NfcEventOptions;
 } // namespace dom
 
-class NfcService final
-  : public nsINfcService
-  , public mozilla::ipc::StreamSocketConsumer
-  , public mozilla::ipc::ListenSocketConsumer
+class NfcConsumer;
+
+class NfcService final : public nsINfcService
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSINFCSERVICE
 
   static already_AddRefed<NfcService> FactoryCreate();
 
   void DispatchNfcEvent(const mozilla::dom::NfcEventOptions& aOptions);
 
-  bool PostToNfcDaemon(const uint8_t* aData, size_t aSize);
-
-  nsCOMPtr<nsIThread> GetThread() {
-    return mThread;
-  }
-
-  // Methods for |StreamSocketConsumer| and |ListenSocketConsumer|
-  //
-
-  void ReceiveSocketData(
-    int aIndex, nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer) override;
-  void OnConnectSuccess(int aIndex) override;
-  void OnConnectError(int aIndex) override;
-  void OnDisconnect(int aIndex) override;
-
 private:
-  enum SocketType {
-    LISTEN_SOCKET,
-    STREAM_SOCKET
-  };
+  class CleanupRunnable;
+  class SendRunnable;
+  class ShutdownConsumerRunnable;
+  class StartConsumerRunnable;
 
   NfcService();
   ~NfcService();
 
   nsCOMPtr<nsIThread> mThread;
   nsCOMPtr<nsINfcGonkEventListener> mListener;
-  nsRefPtr<mozilla::ipc::ListenSocket> mListenSocket;
-  nsRefPtr<mozilla::ipc::StreamSocket> mStreamSocket;
-  nsAutoPtr<NfcMessageHandler> mHandler;
-  nsCString mListenSocketName;
+  nsAutoPtr<NfcConsumer> mNfcConsumer;
 };
 
 } // namespace mozilla
 
 #endif // NfcService_h
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -459,23 +459,19 @@ public:
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
     nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
-      if (mEndpoint.IsEmpty()) {
-        promise->MaybeResolve(JS::NullHandleValue);
-      } else {
-        nsRefPtr<WorkerPushSubscription> sub =
-          new WorkerPushSubscription(mEndpoint, mScope);
-        promise->MaybeResolve(sub);
-      }
+      nsRefPtr<WorkerPushSubscription> sub =
+        new WorkerPushSubscription(mEndpoint, mScope);
+      promise->MaybeResolve(sub);
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
     proxy->CleanUp(aCx);
     return true;
   }
 private:
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -16,10 +16,8 @@ skip-if = os == "android" || toolkit == 
 [test_multiple_register_during_service_activation.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_unregister.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register_different_scope.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_try_registering_offline_disabled.html]
 skip-if = os == "android" || toolkit == "gonk"
-[test_push_manager_worker.html]
-skip-if = os == "android" || toolkit == "gonk"
deleted file mode 100644
--- a/dom/push/test/test_push_manager_worker.html
+++ /dev/null
@@ -1,101 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-Bug 1184574: Expose PushManager to workers.
-
-Any copyright is dedicated to the Public Domain.
-http://creativecommons.org/licenses/publicdomain/
-
--->
-<head>
-  <title>Test for Bug 1184574</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
-</head>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1184574">Mozilla Bug 1184574</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-
-<script class="testbody" type="text/javascript">
-
-  var registration;
-
-  function start() {
-    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
-    .then(swr => { registration = swr; return swr; });
-  }
-
-  function unregisterSW() {
-    return registration.unregister().then(function(result) {
-      ok(result, "Unregister should return true.");
-    }, function(e) {
-      dump("Unregistering the SW failed with " + e + "\n");
-    });
-  }
-
-  function setupPushNotification(swr) {
-    return swr.pushManager.subscribe().then(
-      pushSubscription => {
-        ok(true, "successful registered for push notification");
-        return pushSubscription;
-      }, error => {
-        ok(false, "could not register for push notification");
-      });
-  }
-
-  function getNewEndpointFromWorker(pushSubscription) {
-    return new Promise((resolve, reject) => {
-      var channel = new MessageChannel();
-      channel.port1.onmessage = e => {
-        (e.data.error ? reject : resolve)(e.data);
-      };
-      registration.active.postMessage({
-        endpoint: pushSubscription.endpoint,
-      }, [channel.port2]);
-    }).then(data => {
-      return registration.pushManager.getSubscription().then(
-        pushSubscription => {
-          is(data.endpoint, pushSubscription.endpoint,
-             "Wrong push endpoint in parent");
-          return pushSubscription;
-      });
-    });
-  }
-
-  function unregisterPushNotification(pushSubscription) {
-    return pushSubscription.unsubscribe().then(
-      result => {
-      ok(result, "unsubscribe() on existing subscription should return true.");
-      return pushSubscription;
-    }, error => {
-      ok(false, "unsubscribe() should never fail.");
-    });
-  }
-
-  function runTest() {
-    start()
-    .then(setupPushNotification)
-    .then(getNewEndpointFromWorker)
-    .then(unregisterPushNotification)
-    .then(unregisterSW)
-    .catch(function(e) {
-      ok(false, "Some test failed with error " + e);
-    }).then(SimpleTest.finish);
-  }
-
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.push.enabled", true],
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
-    ]}, runTest);
-  SpecialPowers.addPermission('push', true, document);
-  SimpleTest.waitForExplicitFinish();
-</script>
-</body>
-</html>
--- a/dom/push/test/worker.js
+++ b/dom/push/test/worker.js
@@ -1,19 +1,12 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
-// This worker is used for two types of tests. `handlePush` sends messages to
-// `frame.html`, which verifies that the worker can receive push messages.
-
-// `handleMessage` receives a message from `test_push_manager_worker.html`, and
-// verifies that `PushManager` can be used from the worker.
-
 this.onpush = handlePush;
-this.onmessage = handleMessage;
 
 function handlePush(event) {
 
   self.clients.matchAll().then(function(result) {
     if (event instanceof PushEvent &&
       event.data instanceof PushMessageData &&
       event.data.text === undefined &&
       event.data.json === undefined &&
@@ -21,31 +14,8 @@ function handlePush(event) {
       event.data.blob === undefined) {
 
       result[0].postMessage({type: "finished", okay: "yes"});
       return;
     }
     result[0].postMessage({type: "finished", okay: "no"});
   });
 }
-
-function handleMessage(event) {
-  self.registration.pushManager.getSubscription().then(subscription => {
-    if (subscription.endpoint != event.data.endpoint) {
-      throw new Error("Wrong push endpoint in worker");
-    }
-    return subscription.unsubscribe();
-  }).then(result => {
-    if (!result) {
-      throw new Error("Error dropping subscription in worker");
-    }
-    return self.registration.pushManager.getSubscription();
-  }).then(subscription => {
-    if (subscription) {
-      throw new Error("Subscription not dropped in worker");
-    }
-    return self.registration.pushManager.subscribe();
-  }).then(subscription => {
-    event.ports[0].postMessage({endpoint: subscription.endpoint});
-  }).catch(error => {
-    event.ports[0].postMessage({error: error});
-  });
-}
--- a/dom/svg/SVGContentUtils.h
+++ b/dom/svg/SVGContentUtils.h
@@ -34,16 +34,18 @@ class Element;
 class SVGSVGElement;
 } // namespace dom
 
 namespace gfx {
 class Matrix;
 } // namespace gfx
 } // namespace mozilla
 
+#define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512
+
 inline bool
 IsSVGWhitespace(char aChar)
 {
   return aChar == '\x20' || aChar == '\x9' ||
          aChar == '\xD'  || aChar == '\xA';
 }
 
 inline bool
--- a/dom/svg/SVGLineElement.cpp
+++ b/dom/svg/SVGLineElement.cpp
@@ -32,16 +32,33 @@ nsSVGElement::LengthInfo SVGLineElement:
 //----------------------------------------------------------------------
 // Implementation
 
 SVGLineElement::SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : SVGLineElementBase(aNodeInfo)
 {
 }
 
+void
+SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1,
+                                         float& aX2, float aY2)
+{
+  if (aX1 == aX2 && aY1 == aY2) {
+    SVGContentUtils::AutoStrokeOptions strokeOptions;
+    SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr,
+                                      SVGContentUtils::eIgnoreStrokeDashing);
+
+    if (strokeOptions.mLineCap != CapStyle::BUTT) {
+      float tinyLength =
+        strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
+      aX2 += tinyLength;
+    }
+  }
+}
+
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement)
 
 //----------------------------------------------------------------------
 
 already_AddRefed<SVGAnimatedLength>
@@ -107,25 +124,28 @@ SVGLineElement::GetMarkPoints(nsTArray<n
   aMarks->AppendElement(nsSVGMark(x2, y2, angle, nsSVGMark::eEnd));
 }
 
 void
 SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath)
 {
   float x1, y1, x2, y2;
   GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
+
+  MaybeAdjustForZeroLength(x1, y1, x2, y2);
   aSimplePath->SetLine(x1, y1, x2, y2);
 }
 
 already_AddRefed<Path>
 SVGLineElement::BuildPath(PathBuilder* aBuilder)
 {
   float x1, y1, x2, y2;
   GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
 
+  MaybeAdjustForZeroLength(x1, y1, x2, y2);
   aBuilder->MoveTo(Point(x1, y1));
   aBuilder->LineTo(Point(x2, y2));
 
   return aBuilder->Finish();
 }
 
 bool
 SVGLineElement::GetGeometryBounds(
--- a/dom/svg/SVGLineElement.h
+++ b/dom/svg/SVGLineElement.h
@@ -20,16 +20,20 @@ typedef nsSVGPathGeometryElement SVGLine
 
 class SVGLineElement final : public SVGLineElementBase
 {
 protected:
   explicit SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
   friend nsresult (::NS_NewSVGLineElement(nsIContent **aResult,
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
+  // If the input line has length zero and linecaps aren't butt, adjust |aX2| by
+  // a tiny amount to a barely-nonzero-length line that all of our draw targets
+  // will render
+  void MaybeAdjustForZeroLength(float aX1, float aY1, float& aX2, float aY2);
 
 public:
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
 
   // nsSVGPathGeometryElement methods:
   virtual bool IsMarkable() override { return true; }
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) override;
--- a/dom/svg/SVGPathData.cpp
+++ b/dom/svg/SVGPathData.cpp
@@ -250,17 +250,17 @@ ApproximateZeroLengthSubpathSquareCaps(P
   // not to.
   MOZ_ASSERT(aStrokeWidth > 0.0f,
              "Make the caller check for this, or check it here");
 
   // The fraction of the stroke width that we choose for the length of the
   // line is rather arbitrary, other than being chosen to meet the requirements
   // described in the comment above.
 
-  Float tinyLength = aStrokeWidth / 512;
+  Float tinyLength = aStrokeWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
 
   aPB->LineTo(aPoint + Point(tinyLength, 0));
   aPB->MoveTo(aPoint);
 }
 
 #define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT               \
   do {                                                                        \
     if (!subpathHasLength && hasLineCaps && aStrokeWidth > 0 &&               \
--- a/dom/webidl/BrowserElement.webidl
+++ b/dom/webidl/BrowserElement.webidl
@@ -146,17 +146,17 @@ interface BrowserElementPrivileged {
 
   [Throws,
    Pref="dom.mozBrowserFramesEnabled",
    CheckAllPermissions="browser input-manage"]
   DOMRequest setInputMethodActive(boolean isActive);
 
   [Throws,
    Pref="dom.mozBrowserFramesEnabled",
-   CheckAllPermissions="browser setNFCFocus"]
+   CheckAllPermissions="browser nfc-manager"]
   void setNFCFocus(boolean isFocus);
 
   [Throws,
    Pref="dom.mozBrowserFramesEnabled",
    CheckAnyPermissions="browser"]
   void findAll(DOMString searchString, BrowserFindCaseSensitivity caseSensitivity);
 
   [Throws,
--- a/dom/webidl/SpeechRecognition.webidl
+++ b/dom/webidl/SpeechRecognition.webidl
@@ -10,23 +10,20 @@
  * liability, trademark and document use rules apply.
  */
 
 [Constructor,
  Pref="media.webspeech.recognition.enable",
  Func="SpeechRecognition::IsAuthorized"]
 interface SpeechRecognition : EventTarget {
     // recognition parameters
-    [Throws]
     attribute SpeechGrammarList grammars;
-    [Throws]
     attribute DOMString lang;
     [Throws]
     attribute boolean continuous;
-    [Throws]
     attribute boolean interimResults;
     [Throws]
     attribute unsigned long maxAlternatives;
     [Throws]
     attribute DOMString serviceURI;
 
     // methods to drive the speech interaction
     [Throws, UnsafeInPrerendering]
--- a/embedding/browser/nsWebBrowser.cpp
+++ b/embedding/browser/nsWebBrowser.cpp
@@ -1681,17 +1681,17 @@ nsWebBrowser::EnsureDocShellTreeOwner()
 
   return NS_OK;
 }
 
 static void
 DrawPaintedLayer(PaintedLayer* aLayer,
                  gfxContext* aContext,
                  const nsIntRegion& aRegionToDraw,
-                 const nsIntRegion& aDirtyRegion,
+                 const nsIntRegion* aDirtyRegion,
                  DrawRegionClip aClip,
                  const nsIntRegion& aRegionToInvalidate,
                  void* aCallbackData)
 {
   DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
 
   ColorPattern color(ToDeviceColor(*static_cast<nscolor*>(aCallbackData)));
   nsIntRect dirtyRect = aRegionToDraw.GetBounds();
--- a/gfx/2d/BasePoint.h
+++ b/gfx/2d/BasePoint.h
@@ -64,16 +64,20 @@ struct BasePoint {
   Sub operator/(T aScale) const {
     return Sub(x / aScale, y / aScale);
   }
 
   Sub operator-() const {
     return Sub(-x, -y);
   }
 
+  T DotProduct(const Sub& aPoint) const {
+      return x * aPoint.x + y * aPoint.y;
+  }
+
   T Length() const {
     return hypot(x, y);
   }
 
   // Round() is *not* rounding to nearest integer if the values are negative.
   // They are always rounding as floor(n + 0.5).
   // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
   Sub& Round() {
--- a/gfx/2d/BaseSize.h
+++ b/gfx/2d/BaseSize.h
@@ -25,16 +25,20 @@ struct BaseSize {
   MOZ_CONSTEXPR BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
 
   void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
 
   bool IsEmpty() const {
     return width == 0 || height == 0;
   }
 
+  bool IsSquare() const {
+    return width == height;
+  }
+
   // Note that '=' isn't defined so we'll get the
   // compiler generated default assignment operator
 
   bool operator==(const Sub& aSize) const {
     return width == aSize.width && height == aSize.height;
   }
   bool operator!=(const Sub& aSize) const {
     return width != aSize.width || height != aSize.height;
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -8,16 +8,90 @@
 
 #include "2D.h"
 #include "mozilla/Constants.h"
 #include "UserData.h"
 
 namespace mozilla {
 namespace gfx {
 
+// Kappa constant for 90-degree angle
+const Float kKappaFactor = 0.55191497064665766025f;
+
+// Calculate kappa constant for partial curve. The sign of angle in the
+// tangent will actually ensure this is negative for a counter clockwise
+// sweep, so changing signs later isn't needed.
+inline Float ComputeKappaFactor(Float aAngle)
+{
+  return (4.0f / 3.0f) * tanf(aAngle / 4.0f);
+}
+
+/**
+ * Draws a partial arc <= 90 degrees given exact start and end points.
+ * Assumes that it is continuing from an already specified start point.
+ */
+template <typename T>
+inline void PartialArcToBezier(T* aSink,
+                               const Size& aRadius,
+                               const Point& aStartPoint, const Point& aEndPoint,
+                               const Point& aStartOffset, const Point& aEndOffset,
+                               Float aKappaFactor = kKappaFactor)
+{
+  Float kappaX = aKappaFactor * aRadius.width;
+  Float kappaY = aKappaFactor * aRadius.height;
+
+  Point cp1 =
+    aStartPoint + Point(-aStartOffset.y * kappaX, aStartOffset.x * kappaY);
+
+  Point cp2 =
+    aEndPoint + Point(aEndOffset.y * kappaX, -aEndOffset.x * kappaY);
+
+  aSink->BezierTo(cp1, cp2, aEndPoint);
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ * Specialized version avoiding kappa calculation.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+                             const Point& aOrigin, const Size& aRadius,
+                             const Point& aStartPoint, const Point& aEndPoint,
+                             Float aKappaFactor = kKappaFactor)
+{
+  aSink->LineTo(aStartPoint);
+  if (!aRadius.IsEmpty()) {
+    Point startOffset = aStartPoint - aOrigin;
+    startOffset.x /= aRadius.width;
+    startOffset.y /= aRadius.height;
+    Point endOffset = aEndPoint - aOrigin;
+    endOffset.x /= aRadius.width;
+    endOffset.y /= aRadius.height;
+    PartialArcToBezier(aSink, aRadius,
+                       aStartPoint, aEndPoint,
+                       startOffset, endOffset,
+                       aKappaFactor);
+  } else if (aEndPoint != aStartPoint) {
+    aSink->LineTo(aEndPoint);
+  }
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+                             const Point& aOrigin, const Size& aRadius,
+                             const Point& aStartPoint, const Point& aEndPoint,
+                             Float aStartAngle, Float aEndAngle)
+{
+  AcuteArcToBezier(aSink, aOrigin, aRadius, aStartPoint, aEndPoint,
+                   ComputeKappaFactor(aEndAngle - aStartAngle));
+}
+
 template <typename T>
 void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
                  float aStartAngle, float aEndAngle, bool aAntiClockwise)
 {
   Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f;
 
   // Calculate the total arc we're going to sweep.
   Float arcSweepLeft = (aEndAngle - aStartAngle) * sweepDirection;
@@ -30,95 +104,70 @@ void ArcToBezier(T* aSink, const Point &
     // Recalculate the start angle to land closer to end angle.
     aStartAngle = aEndAngle - arcSweepLeft * sweepDirection;
   } else if (arcSweepLeft > Float(2.0f * M_PI)) {
     // Sweeping more than 2 * pi is a full circle.
     arcSweepLeft = Float(2.0f * M_PI);
   }
 
   Float currentStartAngle = aStartAngle;
-  Point currentStartPoint(aOrigin.x + cosf(aStartAngle) * aRadius.width,
-                          aOrigin.y + sinf(aStartAngle) * aRadius.height);
+  Point currentStartOffset(cosf(aStartAngle), sinf(aStartAngle));
+  Point currentStartPoint(aOrigin.x + currentStartOffset.x * aRadius.width,
+                          aOrigin.y + currentStartOffset.y * aRadius.height);
 
   aSink->LineTo(currentStartPoint);
 
   while (arcSweepLeft > 0) {
-    // We guarantee here the current point is the start point of the next
-    // curve segment.
     Float currentEndAngle =
       currentStartAngle + std::min(arcSweepLeft, Float(M_PI / 2.0f)) * sweepDirection;
 
-    Point currentEndPoint(aOrigin.x + cosf(currentEndAngle) * aRadius.width,
-                          aOrigin.y + sinf(currentEndAngle) * aRadius.height);
-
-    // Calculate kappa constant for partial curve. The sign of angle in the
-    // tangent will actually ensure this is negative for a counter clockwise
-    // sweep, so changing signs later isn't needed.
-    Float kappaFactor = (4.0f / 3.0f) * tanf((currentEndAngle - currentStartAngle) / 4.0f);
-    Float kappaX = kappaFactor * aRadius.width;
-    Float kappaY = kappaFactor * aRadius.height;
+    Point currentEndOffset(cosf(currentEndAngle), sinf(currentEndAngle));
+    Point currentEndPoint(aOrigin.x + currentEndOffset.x * aRadius.width,
+                          aOrigin.y + currentEndOffset.y * aRadius.height);
 
-    Point tangentStart(-sinf(currentStartAngle), cosf(currentStartAngle));
-    Point cp1 = currentStartPoint;
-    cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
+    PartialArcToBezier(aSink, aRadius,
+                       currentStartPoint, currentEndPoint,
+                       currentStartOffset, currentEndOffset,
+                       ComputeKappaFactor(currentEndAngle - currentStartAngle));
 
-    Point revTangentEnd(sinf(currentEndAngle), -cosf(currentEndAngle));
-    Point cp2 = currentEndPoint;
-    cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
-
-    aSink->BezierTo(cp1, cp2, currentEndPoint);
-
+    // We guarantee here the current point is the start point of the next
+    // curve segment.
     arcSweepLeft -= Float(M_PI / 2.0f);
     currentStartAngle = currentEndAngle;
+    currentStartOffset = currentEndOffset;
     currentStartPoint = currentEndPoint;
   }
 }
 
 /* This is basically the ArcToBezier with the parameters for drawing a circle
  * inlined which vastly simplifies it and avoids a bunch of transcedental function
  * calls which should make it faster. */
 template <typename T>
 void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
 {
-  Point startPoint(aOrigin.x + aRadius.width,
-                   aOrigin.y);
+  Point currentStartOffset(1, 0);
+  Point currentStartPoint(aOrigin.x + aRadius.width, aOrigin.y);
 
-  aSink->LineTo(startPoint);
+  aSink->LineTo(currentStartPoint);
 
-  // Calculate kappa constant for partial curve. The sign of angle in the
-  // tangent will actually ensure this is negative for a counter clockwise
-  // sweep, so changing signs later isn't needed.
-  Float kappaFactor = (4.0f / 3.0f) * tan((M_PI/2.0f) / 4.0f);
-  Float kappaX = kappaFactor * aRadius.width;
-  Float kappaY = kappaFactor * aRadius.height;
-  Float cosStartAngle = 1;
-  Float sinStartAngle = 0;
   for (int i = 0; i < 4; i++) {
+    // cos(x+pi/2) == -sin(x)
+    // sin(x+pi/2) == cos(x)
+    Point currentEndOffset(-currentStartOffset.y, currentStartOffset.x);
+    Point currentEndPoint(aOrigin.x + currentEndOffset.x * aRadius.width,
+                          aOrigin.y + currentEndOffset.y * aRadius.height);
+
+    PartialArcToBezier(aSink, aRadius,
+                       currentStartPoint, currentEndPoint,
+                       currentStartOffset, currentEndOffset);
+
     // We guarantee here the current point is the start point of the next
     // curve segment.
-    Point currentStartPoint(aOrigin.x + cosStartAngle * aRadius.width,
-                            aOrigin.y + sinStartAngle * aRadius.height);
-    Point currentEndPoint(aOrigin.x + -sinStartAngle * aRadius.width,
-                          aOrigin.y + cosStartAngle * aRadius.height);
-
-    Point tangentStart(-sinStartAngle, cosStartAngle);
-    Point cp1 = currentStartPoint;
-    cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
-
-    Point revTangentEnd(cosStartAngle, sinStartAngle);
-    Point cp2 = currentEndPoint;
-    cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
-
-    aSink->BezierTo(cp1, cp2, currentEndPoint);
-
-    // cos(x+pi/2) == -sin(x)
-    // sin(x+pi/2) == cos(x)
-    Float tmp = cosStartAngle;
-    cosStartAngle = -sinStartAngle;
-    sinStartAngle = tmp;
+    currentStartOffset = currentEndOffset;
+    currentStartPoint = currentEndPoint;
   }
 }
 
 /**
  * Appends a path represending a rectangle to the path being built by
  * aPathBuilder.
  *
  * aRect           The rectangle to append.
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -261,16 +261,24 @@ public:
   }
 
   uint32_t ToARGB() const
   {
     return uint32_t(b * 255.0f) | uint32_t(g * 255.0f) << 8 |
            uint32_t(r * 255.0f) << 16 | uint32_t(a * 255.0f) << 24;
   }
 
+  bool operator==(const Color& aColor) const {
+    return r == aColor.r && g == aColor.g && b == aColor.b && a == aColor.a;
+  }
+
+  bool operator!=(const Color& aColor) const {
+    return !(*this == aColor);
+  }
+
   Float r, g, b, a;
 };
 
 struct GradientStop
 {
   bool operator<(const GradientStop& aOther) const {
     return offset < aOther.offset;
   }
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -9,17 +9,17 @@ CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
 
 # The following warning options are useful for debugging: -Wpadded
 #AM_CXXFLAGS =
 
 # Convenience targets:
-lib: libharfbuzz.la
+lib: $(BUILT_SOURCES) libharfbuzz.la
 
 lib_LTLIBRARIES = libharfbuzz.la
 
 HBCFLAGS =
 HBLIBS =
 HBSOURCES =  \
 	hb-atomic-private.hh \
 	hb-blob.cc \
@@ -99,20 +99,22 @@ HBSOURCES += \
 	hb-ot-shape-complex-hangul.cc \
 	hb-ot-shape-complex-hebrew.cc \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.cc \
 	hb-ot-shape-complex-myanmar.cc \
 	hb-ot-shape-complex-myanmar-machine.hh \
-	hb-ot-shape-complex-sea.cc \
-	hb-ot-shape-complex-sea-machine.hh \
 	hb-ot-shape-complex-thai.cc \
 	hb-ot-shape-complex-tibetan.cc \
+	hb-ot-shape-complex-use.cc \
+	hb-ot-shape-complex-use-machine.hh \
+	hb-ot-shape-complex-use-private.hh \
+	hb-ot-shape-complex-use-table.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
 	hb-ot-shape-fallback-private.hh \
 	hb-ot-shape-fallback.cc \
 	hb-ot-shape-private.hh \
 	$(NULL)
 HBHEADERS += \
@@ -271,47 +273,52 @@ harfbuzz.def: $(HBHEADERS) $(HBNODISTHEA
 	) >"$@"
 	@ ! grep -q hb_ERROR "$@" \
 	|| ($(RM) "$@"; false)
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
+	gen-use-table.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
-unicode-tables: arabic-table indic-table
-
-indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
-	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \
-	|| ($(RM) hb-ot-shape-complex-indic-table.cc; false)
+unicode-tables: arabic-table indic-table use-table
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh \
 	|| ($(RM) hb-ot-shape-complex-arabic-table.hh; false)
 
+indic-table: gen-indic-table.py IndicSyllabicCategory-7.0.0.txt IndicMatraCategory-7.0.0.txt Blocks.txt
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \
+	|| ($(RM) hb-ot-shape-complex-indic-table.cc; false)
+
+use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-use-table.cc \
+	|| ($(RM) hb-ot-shape-complex-use-table.cc; false)
+
 built-sources: $(BUILT_SOURCES)
 
-.PHONY: unicode-tables arabic-table indic-table built-sources
+.PHONY: unicode-tables arabic-table indic-table use-table built-sources
 
 RAGEL_GENERATED = \
 	$(srcdir)/hb-buffer-deserialize-json.hh \
 	$(srcdir)/hb-buffer-deserialize-text.hh \
 	$(srcdir)/hb-ot-shape-complex-indic-machine.hh \
 	$(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \
-	$(srcdir)/hb-ot-shape-complex-sea-machine.hh \
+	$(srcdir)/hb-ot-shape-complex-use-machine.hh \
 	$(NULL)
 BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
 	hb-ot-shape-complex-indic-machine.rl \
 	hb-ot-shape-complex-myanmar-machine.rl \
-	hb-ot-shape-complex-sea-machine.rl \
+	hb-ot-shape-complex-use-machine.rl \
 	$(NULL)
 MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
 $(srcdir)/%.hh: $(srcdir)/%.rl
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
 
 noinst_PROGRAMS = \
 	main \
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -0,0 +1,476 @@
+#!/usr/bin/python
+
+import sys
+
+if len (sys.argv) != 5:
+	print >>sys.stderr, "usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt"
+	sys.exit (1)
+
+BLACKLISTED_BLOCKS = ["Thai", "Lao", "Tibetan"]
+
+files = [file (x) for x in sys.argv[1:]]
+
+headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 2]
+headers.append (["UnicodeData.txt does not have a header."])
+
+data = [{} for f in files]
+values = [{} for f in files]
+for i, f in enumerate (files):
+	for line in f:
+
+		j = line.find ('#')
+		if j >= 0:
+			line = line[:j]
+
+		fields = [x.strip () for x in line.split (';')]
+		if len (fields) == 1:
+			continue
+
+		uu = fields[0].split ('..')
+		start = int (uu[0], 16)
+		if len (uu) == 1:
+			end = start
+		else:
+			end = int (uu[1], 16)
+
+		t = fields[1 if i != 2 else 2]
+
+		for u in range (start, end + 1):
+			data[i][u] = t
+		values[i][t] = values[i].get (t, 0) + end - start + 1
+
+defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')
+
+# TODO Characters that are not in Unicode Indic files, but used in USE
+data[0][0x034F] = defaults[0]
+data[0][0x2060] = defaults[0]
+for u in range (0xFE00, 0xFE0F + 1):
+	data[0][u] = defaults[0]
+
+# Merge data into one dict:
+for i,v in enumerate (defaults):
+	values[i][v] = values[i].get (v, 0) + 1
+combined = {}
+for i,d in enumerate (data):
+	for u,v in d.items ():
+		if i >= 2 and not u in combined:
+			continue
+		if not u in combined:
+			combined[u] = list (defaults)
+		combined[u][i] = v
+combined = {k:v for k,v in combined.items() if v[3] not in BLACKLISTED_BLOCKS}
+data = combined
+del combined
+num = len (data)
+
+
+property_names = [
+	# General_Category
+	'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc',
+	'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po',
+	'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',
+	# Indic_Syllabic_Category
+	'Other',
+	'Bindu',
+	'Visarga',
+	'Avagraha',
+	'Nukta',
+	'Virama',
+	'Pure_Killer',
+	'Invisible_Stacker',
+	'Vowel_Independent',
+	'Vowel_Dependent',
+	'Vowel',
+	'Consonant_Placeholder',
+	'Consonant',
+	'Consonant_Dead',
+	'Consonant_With_Stacker',
+	'Consonant_Prefixed',
+	'Consonant_Preceding_Repha',
+	'Consonant_Succeeding_Repha',
+	'Consonant_Subjoined',
+	'Consonant_Medial',
+	'Consonant_Final',
+	'Consonant_Head_Letter',
+	'Modifying_Letter',
+	'Tone_Letter',
+	'Tone_Mark',
+	'Gemination_Mark',
+	'Cantillation_Mark',
+	'Register_Shifter',
+	'Syllable_Modifier',
+	'Consonant_Killer',
+	'Non_Joiner',
+	'Joiner',
+	'Number_Joiner',
+	'Number',
+	'Brahmi_Joining_Number',
+	# Indic_Positional_Category
+	'Not_Applicable',
+	'Right',
+	'Left',
+	'Visual_Order_Left',
+	'Left_And_Right',
+	'Top',
+	'Bottom',
+	'Top_And_Bottom',
+	'Top_And_Right',
+	'Top_And_Left',
+	'Top_And_Left_And_Right',
+	'Bottom_And_Right',
+	'Top_And_Bottom_And_Right',
+	'Overstruck',
+]
+
+class PropertyValue(object):
+	def __init__(self, name_):
+		self.name = name_
+	def __str__(self):
+		return self.name
+	def __eq__(self, other):
+		return self.name == (other if isinstance(other, basestring) else other.name)
+	def __ne__(self, other):
+		return not (self == other)
+
+property_values = {}
+
+for name in property_names:
+	value = PropertyValue(name)
+	assert value not in property_values
+	assert value not in globals()
+	property_values[name] = value
+globals().update(property_values)
+
+
+def is_BASE(U, UISC, UGC):
+	return (UISC in [Number, Consonant, Consonant_Head_Letter,
+			#SPEC-OUTDATED Consonant_Placeholder,
+			Tone_Letter] or
+		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
+					Consonant_Subjoined, Vowel, Vowel_Dependent]))
+def is_BASE_VOWEL(U, UISC, UGC):
+	return UISC == Vowel_Independent
+def is_BASE_IND(U, UISC, UGC):
+	#SPEC-BROKEN return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
+	return (UISC in [Consonant_Dead, Modifying_Letter] or
+		(UGC == Po and not is_BASE_OTHER(U, UISC, UGC))) # for 104E
+def is_BASE_NUM(U, UISC, UGC):
+	return UISC == Brahmi_Joining_Number
+def is_BASE_OTHER(U, UISC, UGC):
+	if UISC == Consonant_Placeholder: return True #SPEC-OUTDATED
+	return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC,
+		     0x25FB, 0x25FC, 0x25FD, 0x25FE]
+def is_CGJ(U, UISC, UGC):
+	return U == 0x034F
+def is_CONS_FINAL(U, UISC, UGC):
+	return ((UISC == Consonant_Final and UGC != Lo) or
+		UISC == Consonant_Succeeding_Repha)
+def is_CONS_FINAL_MOD(U, UISC, UGC):
+	#SPEC-OUTDATED return  UISC in [Consonant_Final_Modifier, Syllable_Modifier]
+	return  UISC == Syllable_Modifier
+def is_CONS_MED(U, UISC, UGC):
+	return UISC == Consonant_Medial and UGC != Lo
+def is_CONS_MOD(U, UISC, UGC):
+	return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
+def is_CONS_SUB(U, UISC, UGC):
+	#SPEC-OUTDATED return UISC == Consonant_Subjoined
+	return UISC == Consonant_Subjoined and UGC != Lo
+def is_HALANT(U, UISC, UGC):
+	return UISC in [Virama, Invisible_Stacker]
+def is_HALANT_NUM(U, UISC, UGC):
+	return UISC == Number_Joiner
+def is_ZWNJ(U, UISC, UGC):
+	return UISC == Non_Joiner
+def is_ZWJ(U, UISC, UGC):
+	return UISC == Joiner
+def is_Word_Joiner(U, UISC, UGC):
+	return U == 0x2060
+def is_OTHER(U, UISC, UGC):
+	#SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters
+	return (UISC == Other
+		and not is_SYM_MOD(U, UISC, UGC)
+		and not is_CGJ(U, UISC, UGC)
+		and not is_Word_Joiner(U, UISC, UGC)
+		and not is_VARIATION_SELECTOR(U, UISC, UGC)
+	)
+def is_Reserved(U, UISC, UGC):
+	return UGC == 'Cn'
+def is_REPHA(U, UISC, UGC):
+	#return UISC == Consonant_Preceding_Repha
+	#SPEC-OUTDATED hack to categorize Consonant_With_Stacker and Consonant_Prefixed
+	return UISC in [Consonant_Preceding_Repha, Consonant_With_Stacker, Consonant_Prefixed]
+def is_SYM(U, UISC, UGC):
+	if U == 0x25CC: return False #SPEC-OUTDATED
+	#SPEC-OUTDATED return UGC in [So, Sc] or UISC == Symbol_Letter
+	return UGC in [So, Sc]
+def is_SYM_MOD(U, UISC, UGC):
+	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
+def is_VARIATION_SELECTOR(U, UISC, UGC):
+	return 0xFE00 <= U <= 0xFE0F
+def is_VOWEL(U, UISC, UGC):
+	return (UISC == Pure_Killer or
+		(UGC != Lo and UISC in [Vowel, Vowel_Dependent]))
+def is_VOWEL_MOD(U, UISC, UGC):
+	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
+		(UGC != Lo and UISC == Bindu))
+
+use_mapping = {
+	'B':	is_BASE,
+	'IV':	is_BASE_VOWEL,
+	'IND':	is_BASE_IND,
+	'N':	is_BASE_NUM,
+	'GB':	is_BASE_OTHER,
+	'CGJ':	is_CGJ,
+	'F':	is_CONS_FINAL,
+	'FM':	is_CONS_FINAL_MOD,
+	'M':	is_CONS_MED,
+	'CM':	is_CONS_MOD,
+	'SUB':	is_CONS_SUB,
+	'H':	is_HALANT,
+	'HN':	is_HALANT_NUM,
+	'ZWNJ':	is_ZWNJ,
+	'ZWJ':	is_ZWJ,
+	'WJ':	is_Word_Joiner,
+	'O':	is_OTHER,
+	'Rsv':	is_Reserved,
+	'R':	is_REPHA,
+	'S':	is_SYM,
+	'SM':	is_SYM_MOD,
+	'VS':	is_VARIATION_SELECTOR,
+	'V':	is_VOWEL,
+	'VM':	is_VOWEL_MOD,
+}
+
+use_positions = {
+	'F': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+		'Pst': [Right],
+	},
+	'M': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+		'Pst': [Right],
+		'Pre': [Left],
+	},
+	'CM': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+	},
+	'V': {
+		'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right],
+		'Blw': [Bottom, Overstruck, Bottom_And_Right],
+		'Pst': [Right],
+		'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
+	},
+	'VM': {
+		'Abv': [Top],
+		'Blw': [Bottom, Overstruck],
+		'Pst': [Right],
+		'Pre': [Left],
+	},
+	'SM': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+	},
+	'H': None,
+	'B': None,
+	'FM': None,
+	'SUB': None,
+}
+
+def map_to_use(data):
+	out = {}
+	items = use_mapping.items()
+	for U,(UISC,UIPC,UGC,UBlock) in data.items():
+
+		# Resolve Indic_Syllabic_Category
+
+		# TODO: These don't have UISC assigned in Unicode 8.0, but
+		# have UIPC
+		if U == 0x17DD: UISC = Vowel_Dependent
+		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
+
+		# TODO: U+1CED should only be allowed after some of
+		# the nasalization marks, maybe only for U+1CE9..U+1CF1.
+		if U == 0x1CED: UISC = Tone_Mark
+
+		evals = [(k, v(U,UISC,UGC)) for k,v in items]
+		values = [k for k,v in evals if v]
+		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
+		USE = values[0]
+
+		# Resolve Indic_Positional_Category
+
+		# TODO: Not in Unicode 8.0 yet, but in spec.
+		if U == 0x1B6C: UIPC = Bottom
+
+		# TODO: These should die, but have UIPC in Unicode 8.0
+		if U in [0x953, 0x954]: UIPC = Not_Applicable
+
+		# TODO: In USE's override list but not in Unicode 8.0
+		if U == 0x103C: UIPC = Left
+
+		# TODO: These are not in USE's override list that we have, nor are they in Unicode 8.0
+		if 0xA926 <= U <= 0xA92A: UIPC = Top
+		if U == 0x111CA: UIPC = Bottom
+		if U == 0x11300: UIPC = Top
+		if U == 0x1133C: UIPC = Bottom
+		if U == 0x1171E: UIPC = Left # Correct?!
+		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
+		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
+
+		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
+			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
+
+		pos_mapping = use_positions.get(USE, None)
+		if pos_mapping:
+			values = [k for k,v in pos_mapping.items() if v and UIPC in v]
+			assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC, values)
+			USE = USE + values[0]
+
+		out[U] = (USE, UBlock)
+	return out
+
+defaults = ('O', 'No_Block')
+data = map_to_use(data)
+
+# Remove the outliers
+singles = {}
+for u in [0x034F, 0x25CC, 0x1107F]:
+	singles[u] = data[u]
+	del data[u]
+
+print "/* == Start of generated table == */"
+print "/*"
+print " * The following table is generated by running:"
+print " *"
+print " *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt"
+print " *"
+print " * on files with these headers:"
+print " *"
+for h in headers:
+	for l in h:
+		print " * %s" % (l.strip())
+print " */"
+print
+print '#include "hb-ot-shape-complex-use-private.hh"'
+print
+
+total = 0
+used = 0
+last_block = None
+def print_block (block, start, end, data):
+	global total, used, last_block
+	if block and block != last_block:
+		print
+		print
+		print "  /* %s */" % block
+		if start % 16:
+			print ' ' * (20 + (start % 16 * 6)),
+	num = 0
+	assert start % 8 == 0
+	assert (end+1) % 8 == 0
+	for u in range (start, end+1):
+		if u % 16 == 0:
+			print
+			print "  /* %04X */" % u,
+		if u in data:
+			num += 1
+		d = data.get (u, defaults)
+		sys.stdout.write ("%6s," % d[0])
+
+	total += end - start + 1
+	used += num
+	if block:
+		last_block = block
+
+uu = data.keys ()
+uu.sort ()
+
+last = -100000
+num = 0
+offset = 0
+starts = []
+ends = []
+for k,v in sorted(use_mapping.items()):
+	if k in use_positions and use_positions[k]: continue
+	print "#define %s	USE_%s	/* %s */" % (k, k, v.__name__[3:])
+for k,v in sorted(use_positions.items()):
+	if not v: continue
+	for suf in v.keys():
+		tag = k + suf
+		print "#define %s	USE_%s" % (tag, tag)
+print ""
+print "static const USE_TABLE_ELEMENT_TYPE use_table[] = {"
+for u in uu:
+	if u <= last:
+		continue
+	block = data[u][1]
+
+	start = u//8*8
+	end = start+1
+	while end in uu and block == data[end][1]:
+		end += 1
+	end = (end-1)//8*8 + 7
+
+	if start != last + 1:
+		if start - last <= 1+16*3:
+			print_block (None, last+1, start-1, data)
+			last = start-1
+		else:
+			if last >= 0:
+				ends.append (last + 1)
+				offset += ends[-1] - starts[-1]
+			print
+			print
+			print "#define use_offset_0x%04xu %d" % (start, offset)
+			starts.append (start)
+
+	print_block (block, start, end, data)
+	last = end
+ends.append (last + 1)
+offset += ends[-1] - starts[-1]
+print
+print
+occupancy = used * 100. / total
+page_bits = 12
+print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)
+print
+print "USE_TABLE_ELEMENT_TYPE"
+print "hb_use_get_categories (hb_codepoint_t u)"
+print "{"
+print "  switch (u >> %d)" % page_bits
+print "  {"
+pages = set([u>>page_bits for u in starts+ends+singles.keys()])
+for p in sorted(pages):
+	print "    case 0x%0Xu:" % p
+	for (start,end) in zip (starts, ends):
+		if p not in [start>>page_bits, end>>page_bits]: continue
+		offset = "use_offset_0x%04xu" % start
+		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
+	for u,d in singles.items ():
+		if p != u>>page_bits: continue
+		print "      if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0])
+	print "      break;"
+	print ""
+print "    default:"
+print "      break;"
+print "  }"
+print "  return USE_O;"
+print "}"
+print
+for k in sorted(use_mapping.keys()):
+	if k in use_positions and use_positions[k]: continue
+	print "#undef %s" % k
+for k,v in sorted(use_positions.items()):
+	if not v: continue
+	for suf in v.keys():
+		tag = k + suf
+		print "#undef %s" % tag
+print
+print "/* == End of generated table == */"
+
+# Maintain at least 50% occupancy in the table */
+if occupancy < 50:
+	raise Exception ("Table too sparse, please investigate: ", occupancy)
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -0,0 +1,13 @@
+prefix=/usr/local
+exec_prefix=/usr/local
+libdir=/usr/local/lib
+includedir=/usr/local/include
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library ICU integration
+Version: 1.0.1
+
+Requires: harfbuzz
+Requires.private: icu-uc
+Libs: -L${libdir} -lharfbuzz-icu
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -0,0 +1,11 @@
+prefix=/usr/local
+exec_prefix=/usr/local
+libdir=/usr/local/lib
+includedir=/usr/local/include
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library
+Version: 1.0.1
+
+Libs: -L${libdir} -lharfbuzz
+Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -45,16 +45,17 @@ ASSERT_STATIC (sizeof (hb_glyph_info_t) 
 
 struct hb_buffer_t {
   hb_object_header_t header;
   ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_buffer_flags_t flags; /* BOT / EOT / etc. */
+  hb_buffer_cluster_level_t cluster_level;
   hb_codepoint_t replacement; /* U+FFFD or something else. */
 
   /* Buffer contents */
   hb_buffer_content_type_t content_type;
   hb_segment_properties_t props; /* Script, language, direction */
 
   bool in_error; /* Allocation failed */
   bool have_output; /* Whether we have an output buffer going on */
@@ -166,19 +167,28 @@ struct hb_buffer_t {
       info[j].mask |= mask;
   }
   HB_INTERNAL void set_masks (hb_mask_t value,
 			      hb_mask_t mask,
 			      unsigned int cluster_start,
 			      unsigned int cluster_end);
 
   HB_INTERNAL void merge_clusters (unsigned int start,
-				   unsigned int end);
+				   unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    merge_clusters_impl (start, end);
+  }
+  HB_INTERNAL void merge_clusters_impl (unsigned int start,
+					unsigned int end);
   HB_INTERNAL void merge_out_clusters (unsigned int start,
 				       unsigned int end);
+  /* Merge clusters for deleting current glyph, and skip it. */
+  HB_INTERNAL void delete_glyph (void);
 
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
   { return likely (!size || size < allocated) ? true : enlarge (size); }
 
   inline bool ensure_inplace (unsigned int size)
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -31,28 +31,34 @@
 #include "hb-utf-private.hh"
 
 
 #ifndef HB_DEBUG_BUFFER
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
 
+/**
+ * Since: 0.9.7
+ **/
 hb_bool_t
 hb_segment_properties_equal (const hb_segment_properties_t *a,
 			     const hb_segment_properties_t *b)
 {
   return a->direction == b->direction &&
 	 a->script    == b->script    &&
 	 a->language  == b->language  &&
 	 a->reserved1 == b->reserved1 &&
 	 a->reserved2 == b->reserved2;
 
 }
 
+/**
+ * Since: 0.9.7
+ **/
 unsigned int
 hb_segment_properties_hash (const hb_segment_properties_t *p)
 {
   return (unsigned int) p->direction ^
 	 (unsigned int) p->script ^
 	 (intptr_t) (p->language);
 }
 
@@ -493,24 +499,20 @@ hb_buffer_t::reverse_clusters (void)
       start = i;
       last_cluster = info[i].cluster;
     }
   }
   reverse_range (start, i);
 }
 
 void
-hb_buffer_t::merge_clusters (unsigned int start,
-			     unsigned int end)
+hb_buffer_t::merge_clusters_impl (unsigned int start,
+				  unsigned int end)
 {
-#ifdef HB_NO_MERGE_CLUSTERS
-  return;
-#endif
-
-  if (unlikely (end - start < 2))
+  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     return;
 
   unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, info[i].cluster);
 
   /* Extend end */
@@ -518,29 +520,28 @@ hb_buffer_t::merge_clusters (unsigned in
     end++;
 
   /* Extend start */
   while (idx < start && info[start - 1].cluster == info[start].cluster)
     start--;
 
   /* If we hit the start of buffer, continue in out-buffer. */
   if (idx == start)
-    for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
+    for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
       out_info[i - 1].cluster = cluster;
 
   for (unsigned int i = start; i < end; i++)
     info[i].cluster = cluster;
 }
 void
 hb_buffer_t::merge_out_clusters (unsigned int start,
 				 unsigned int end)
 {
-#ifdef HB_NO_MERGE_CLUSTERS
-  return;
-#endif
+  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+    return;
 
   if (unlikely (end - start < 2))
     return;
 
   unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, out_info[i].cluster);
@@ -550,22 +551,54 @@ hb_buffer_t::merge_out_clusters (unsigne
     start--;
 
   /* Extend end */
   while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
     end++;
 
   /* If we hit the end of out-buffer, continue in buffer. */
   if (end == out_len)
-    for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
+    for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
       info[i].cluster = cluster;
 
   for (unsigned int i = start; i < end; i++)
     out_info[i].cluster = cluster;
 }
+void
+hb_buffer_t::delete_glyph ()
+{
+  unsigned int cluster = info[idx].cluster;
+  if (idx + 1 < len && cluster == info[idx + 1].cluster)
+  {
+    /* Cluster survives; do nothing. */
+    goto done;
+  }
+
+  if (out_len)
+  {
+    /* Merge cluster backward. */
+    if (cluster < out_info[out_len - 1].cluster)
+    {
+      unsigned int old_cluster = out_info[out_len - 1].cluster;
+      for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--)
+	out_info[i - 1].cluster = cluster;
+    }
+    goto done;
+  }
+
+  if (idx + 1 < len)
+  {
+    /* Merge cluster forward. */
+    merge_clusters (idx, idx + 2);
+    goto done;
+  }
+
+done:
+  skip_glyph ();
+}
 
 void
 hb_buffer_t::guess_segment_properties (void)
 {
   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
 	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   /* If script is set to INVALID, guess from buffer contents */
@@ -698,16 +731,17 @@ hb_buffer_create (void)
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
   static const hb_buffer_t _hb_buffer_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     HB_BUFFER_FLAG_DEFAULT,
+    HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
     HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
     HB_SEGMENT_PROPERTIES_DEFAULT,
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
 
@@ -799,34 +833,34 @@ hb_buffer_get_user_data (hb_buffer_t    
 
 /**
  * hb_buffer_set_content_type:
  * @buffer: a buffer.
  * @content_type: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 void
 hb_buffer_set_content_type (hb_buffer_t              *buffer,
 			    hb_buffer_content_type_t  content_type)
 {
   buffer->content_type = content_type;
 }
 
 /**
  * hb_buffer_get_content_type:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 hb_buffer_content_type_t
 hb_buffer_get_content_type (hb_buffer_t *buffer)
 {
   return buffer->content_type;
 }
 
 
@@ -979,17 +1013,17 @@ hb_buffer_get_language (hb_buffer_t *buf
 
 /**
  * hb_buffer_set_segment_properties:
  * @buffer: a buffer.
  * @props: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
 				  const hb_segment_properties_t *props)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -998,34 +1032,34 @@ hb_buffer_set_segment_properties (hb_buf
 
 /**
  * hb_buffer_get_segment_properties:
  * @buffer: a buffer.
  * @props: (out):
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_get_segment_properties (hb_buffer_t *buffer,
 				  hb_segment_properties_t *props)
 {
   *props = buffer->props;
 }
 
 
 /**
  * hb_buffer_set_flags:
  * @buffer: a buffer.
  * @flags: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -1035,33 +1069,68 @@ hb_buffer_set_flags (hb_buffer_t       *
 /**
  * hb_buffer_get_flags:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer)
 {
   return buffer->flags;
 }
 
+/**
+ * hb_buffer_set_cluster_level:
+ * @buffer: a buffer.
+ * @cluster_level: 
+ *
+ * 
+ *
+ * Since: 0.9.42
+ **/
+void
+hb_buffer_set_cluster_level (hb_buffer_t       *buffer,
+		     hb_buffer_cluster_level_t  cluster_level)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->cluster_level = cluster_level;
+}
+
+/**
+ * hb_buffer_get_cluster_level:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 0.9.42
+ **/
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (hb_buffer_t *buffer)
+{
+  return buffer->cluster_level;
+}
+
 
 /**
  * hb_buffer_set_replacement_codepoint:
  * @buffer: a buffer.
  * @replacement: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.31
  **/
 void
 hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
 				     hb_codepoint_t  replacement)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -1071,17 +1140,17 @@ hb_buffer_set_replacement_codepoint (hb_
 /**
  * hb_buffer_get_replacement_codepoint:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.31
  **/
 hb_codepoint_t
 hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer)
 {
   return buffer->replacement;
 }
 
 
@@ -1100,17 +1169,17 @@ hb_buffer_reset (hb_buffer_t *buffer)
 }
 
 /**
  * hb_buffer_clear_contents:
  * @buffer: a buffer.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.11
  **/
 void
 hb_buffer_clear_contents (hb_buffer_t *buffer)
 {
   buffer->clear ();
 }
 
 /**
@@ -1278,16 +1347,33 @@ hb_buffer_get_glyph_positions (hb_buffer
  **/
 void
 hb_buffer_reverse (hb_buffer_t *buffer)
 {
   buffer->reverse ();
 }
 
 /**
+ * hb_buffer_reverse_range:
+ * @buffer: a buffer.
+ * @start: start index.
+ * @end: end index.
+ *
+ * Reverses buffer contents between  start to end.
+ *
+ * Since: 1.0
+ **/
+void
+hb_buffer_reverse_range (hb_buffer_t *buffer,
+			 unsigned int start, unsigned int end)
+{
+  buffer->reverse_range (start, end);
+}
+
+/**
  * hb_buffer_reverse_clusters:
  * @buffer: a buffer.
  *
  * Reverses buffer clusters.  That is, the buffer contents are
  * reversed, then each cluster (consecutive items having the
  * same cluster number) are reversed again.
  *
  * Since: 1.0
@@ -1315,17 +1401,17 @@ hb_buffer_reverse_clusters (hb_buffer_t 
  * it will be set to the natural horizontal direction of the
  * buffer script as returned by hb_script_get_horizontal_direction().
  *
  * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
  * it will be set to the process's default language as returned by
  * hb_language_get_default().  This may change in the future by
  * taking buffer script into consideration when choosing a language.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
 {
   buffer->guess_segment_properties ();
 }
 
 template <typename utf_t>
@@ -1468,17 +1554,17 @@ hb_buffer_add_utf32 (hb_buffer_t    *buf
  * @buffer: a buffer.
  * @text: (array length=text_length) (element-type uint8_t):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.39
  **/
 void
 hb_buffer_add_latin1 (hb_buffer_t   *buffer,
 		      const uint8_t *text,
 		      int            text_length,
 		      unsigned int   item_offset,
 		      int            item_length)
 {
@@ -1490,17 +1576,17 @@ hb_buffer_add_latin1 (hb_buffer_t   *buf
  * @buffer: a buffer.
  * @text: (array length=text_length):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.31
  **/
 void
 hb_buffer_add_codepoints (hb_buffer_t          *buffer,
 			  const hb_codepoint_t *text,
 			  int                   text_length,
 			  unsigned int          item_offset,
 			  int                   item_length)
 {
@@ -1564,17 +1650,17 @@ normalize_glyphs_cluster (hb_buffer_t *b
 }
 
 /**
  * hb_buffer_normalize_glyphs:
  * @buffer: a buffer.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
 {
   assert (buffer->have_positions);
   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -180,17 +180,29 @@ typedef enum { /*< flags >*/
 
 void
 hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags);
 
 hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer);
 
+typedef enum {
+  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES	= 0,
+  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS	= 1,
+  HB_BUFFER_CLUSTER_LEVEL_CHARACTERS		= 2,
+  HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES
+} hb_buffer_cluster_level_t;
 
+void
+hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
+			     hb_buffer_cluster_level_t  cluster_level);
+
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (hb_buffer_t *buffer);
 
 #define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu
 
 /* Sets codepoint used to replace invalid UTF-8/16/32 entries.
  * Default is 0xFFFDu. */
 void
 hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
 				     hb_codepoint_t  replacement);
@@ -217,16 +229,20 @@ hb_buffer_pre_allocate (hb_buffer_t  *bu
 /* Returns false if allocation has failed before */
 hb_bool_t
 hb_buffer_allocation_successful (hb_buffer_t  *buffer);
 
 void
 hb_buffer_reverse (hb_buffer_t *buffer);
 
 void
+hb_buffer_reverse_range (hb_buffer_t *buffer,
+			 unsigned int start, unsigned int end);
+
+void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer);
 
 
 /* Filling the buffer in */
 
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -87,17 +87,17 @@ hb_tag_from_string (const char *str, int
 
 /**
  * hb_tag_to_string:
  * @tag: 
  * @buf: (array fixed-size=4): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 void
 hb_tag_to_string (hb_tag_t tag, char *buf)
 {
   buf[0] = (char) (uint8_t) (tag >> 24);
   buf[1] = (char) (uint8_t) (tag >> 16);
   buf[2] = (char) (uint8_t) (tag >>  8);
   buf[3] = (char) (uint8_t) (tag >>  0);
@@ -329,17 +329,17 @@ hb_language_to_string (hb_language_t lan
   return language->s;
 }
 
 /**
  * hb_language_get_default:
  *
  * 
  *
- * Return value: 
+ * Return value: (transfer none):
  *
  * Since: 1.0
  **/
 hb_language_t
 hb_language_get_default (void)
 {
   static hb_language_t default_language = HB_LANGUAGE_INVALID;
 
@@ -488,16 +488,19 @@ hb_script_get_horizontal_direction (hb_s
     /* Unicode-7.0 additions */
     case HB_SCRIPT_MANICHAEAN:
     case HB_SCRIPT_MENDE_KIKAKUI:
     case HB_SCRIPT_NABATAEAN:
     case HB_SCRIPT_OLD_NORTH_ARABIAN:
     case HB_SCRIPT_PALMYRENE:
     case HB_SCRIPT_PSALTER_PAHLAVI:
 
+    /* Unicode-8.0 additions */
+    case HB_SCRIPT_OLD_HUNGARIAN:
+
       return HB_DIRECTION_RTL;
   }
 
   return HB_DIRECTION_LTR;
 }
 
 
 /* hb_user_data_array_t */
@@ -574,17 +577,17 @@ hb_version_string (void)
  * @major: 
  * @minor: 
  * @micro: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.30
  **/
 hb_bool_t
 hb_version_atleast (unsigned int major,
 		    unsigned int minor,
 		    unsigned int micro)
 {
   return HB_VERSION_ATLEAST (major, minor, micro);
 }
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -291,22 +291,22 @@ typedef enum
   /*7.0*/ HB_SCRIPT_PAHAWH_HMONG		= HB_TAG ('H','m','n','g'),
   /*7.0*/ HB_SCRIPT_PALMYRENE			= HB_TAG ('P','a','l','m'),
   /*7.0*/ HB_SCRIPT_PAU_CIN_HAU			= HB_TAG ('P','a','u','c'),
   /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI		= HB_TAG ('P','h','l','p'),
   /*7.0*/ HB_SCRIPT_SIDDHAM			= HB_TAG ('S','i','d','d'),
   /*7.0*/ HB_SCRIPT_TIRHUTA			= HB_TAG ('T','i','r','h'),
   /*7.0*/ HB_SCRIPT_WARANG_CITI			= HB_TAG ('W','a','r','a'),
 
-  /*8.0*/ HB_SCRIPT_AHOM			= HB_TAG ('A','h','o','m'),
-  /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS	= HB_TAG ('H','l','u','w'),
-  /*8.0*/ HB_SCRIPT_HATRAN			= HB_TAG ('H','a','t','r'),
-  /*8.0*/ HB_SCRIPT_MULTANI			= HB_TAG ('M','u','l','t'),
-  /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN		= HB_TAG ('H','u','n','g'),
-  /*8.0*/ HB_SCRIPT_SIGNWRITING			= HB_TAG ('S','g','n','w'),
+  /*8.0*/ HB_SCRIPT_AHOM                        = HB_TAG ('A','h','o','m'),
+  /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS       = HB_TAG ('H','l','u','w'),
+  /*8.0*/ HB_SCRIPT_HATRAN                      = HB_TAG ('H','a','t','r'),
+  /*8.0*/ HB_SCRIPT_MULTANI                     = HB_TAG ('M','u','l','t'),
+  /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN               = HB_TAG ('H','u','n','g'),
+  /*8.0*/ HB_SCRIPT_SIGNWRITING                 = HB_TAG ('S','g','n','w'),
 
   /* No script set. */
   HB_SCRIPT_INVALID				= HB_TAG_NONE,
 
   /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t
    * without risking undefined behavior.  Include both a signed and unsigned max,
    * since technically enums are int, and indeed, hb_script_t ends up being signed.
    * See this thread for technicalities:
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -342,32 +342,32 @@ hb_face_reference_table (hb_face_t *face
 /**
  * hb_face_reference_blob:
  * @face: a face.
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_face_reference_blob (hb_face_t *face)
 {
   return face->reference_table (HB_TAG_NONE);
 }
 
 /**
  * hb_face_set_index:
  * @face: a face.
  * @index: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
   if (face->immutable)
     return;
 
@@ -377,32 +377,32 @@ hb_face_set_index (hb_face_t    *face,
 /**
  * hb_face_get_index:
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_face_get_index (hb_face_t    *face)
 {
   return face->index;
 }
 
 /**
  * hb_face_set_upem:
  * @face: a face.
  * @upem: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem)
 {
   if (face->immutable)
     return;
 
@@ -436,17 +436,17 @@ hb_face_t::load_upem (void) const
 
 /**
  * hb_face_set_glyph_count:
  * @face: a face.
  * @glyph_count: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_face_set_glyph_count (hb_face_t    *face,
 			 unsigned int  glyph_count)
 {
   if (face->immutable)
     return;
 
@@ -456,17 +456,17 @@ hb_face_set_glyph_count (hb_face_t    *f
 /**
  * hb_face_get_glyph_count:
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 unsigned int
 hb_face_get_glyph_count (hb_face_t *face)
 {
   return face->get_num_glyphs ();
 }
 
 void
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -599,17 +599,17 @@ hb_font_get_glyph_contour_point (hb_font
  * @glyph: 
  * @name: (array length=size): 
  * @size: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_name (hb_font_t *font,
 			hb_codepoint_t glyph,
 			char *name, unsigned int size)
 {
   return font->get_glyph_name (glyph, name, size);
 }
@@ -620,17 +620,17 @@ hb_font_get_glyph_name (hb_font_t *font,
  * @name: (array length=len): 
  * @len: 
  * @glyph: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_from_name (hb_font_t *font,
 			     const char *name, int len, /* -1 means nul-terminated */
 			     hb_codepoint_t *glyph)
 {
   return font->get_glyph_from_name (name, len, glyph);
 }
@@ -795,17 +795,17 @@ hb_font_get_glyph_contour_point_for_orig
  * hb_font_glyph_to_string:
  * @font: a font.
  * @glyph: 
  * @s: (array length=size): 
  * @size: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_glyph_to_string (hb_font_t *font,
 			 hb_codepoint_t glyph,
 			 char *s, unsigned int size)
 {
   font->glyph_to_string (glyph, s, size);
 }
@@ -817,17 +817,17 @@ hb_font_glyph_to_string (hb_font_t *font
  * @s: (array length=len) (element-type uint8_t): 
  * @len: 
  * @glyph: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_glyph_from_string (hb_font_t *font,
 			   const char *s, int len, /* -1 means nul-terminated */
 			   hb_codepoint_t *glyph)
 {
   return font->glyph_from_string (s, len, glyph);
 }
@@ -1073,17 +1073,17 @@ hb_font_get_parent (hb_font_t *font)
 /**
  * hb_font_get_face:
  * @font: a font.
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_font_get_face (hb_font_t *font)
 {
   return font->face;
 }
 
 
@@ -1091,17 +1091,17 @@ hb_font_get_face (hb_font_t *font)
  * hb_font_set_funcs:
  * @font: a font.
  * @klass: (closure font_data) (destroy destroy) (scope notified):
  * @font_data: 
  * @destroy: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_set_funcs (hb_font_t         *font,
 		   hb_font_funcs_t   *klass,
 		   void              *font_data,
 		   hb_destroy_func_t  destroy)
 {
   if (font->immutable) {
@@ -1126,17 +1126,17 @@ hb_font_set_funcs (hb_font_t         *fo
 /**
  * hb_font_set_funcs_data:
  * @font: a font.
  * @font_data: (destroy destroy) (scope notified):
  * @destroy: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_set_funcs_data (hb_font_t         *font,
 		        void              *font_data,
 		        hb_destroy_func_t  destroy)
 {
   /* Destroy user_data? */
   if (font->immutable) {
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -287,33 +287,33 @@ hb_font_funcs_set_glyph_contour_point_fu
  * hb_font_funcs_set_glyph_name_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs,
 				   hb_font_get_glyph_name_func_t func,
 				   void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_from_name_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_from_name_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 
 /* func dispatch */
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -70,25 +70,29 @@ static hb_bool_t
 hb_ft_get_glyph (hb_font_t *font HB_UNUSED,
 		 void *font_data,
 		 hb_codepoint_t unicode,
 		 hb_codepoint_t variation_selector,
 		 hb_codepoint_t *glyph,
 		 void *user_data HB_UNUSED)
 
 {
+  unsigned int g;
   FT_Face ft_face = (FT_Face) font_data;
 
-  if (unlikely (variation_selector)) {
-    *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
-    return *glyph != 0;
-  }
+  if (likely (!variation_selector))
+    g = FT_Get_Char_Index (ft_face, unicode);
+  else
+    g = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
 
-  *glyph = FT_Get_Char_Index (ft_face, unicode);
-  return *glyph != 0;
+  if (unlikely (!g))
+    return false;
+
+  *glyph = g;
+  return true;
 }
 
 static hb_position_t
 hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
@@ -372,17 +376,17 @@ hb_ft_face_create (FT_Face           ft_
 
 /**
  * hb_ft_face_create_referenced:
  * @ft_face:
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.38
  **/
 hb_face_t *
 hb_ft_face_create_referenced (FT_Face ft_face)
 {
   FT_Reference_Face (ft_face);
   return hb_ft_face_create (ft_face, (hb_destroy_func_t) FT_Done_Face);
 }
 
@@ -459,17 +463,17 @@ hb_ft_font_create (FT_Face           ft_
 
 /**
  * hb_ft_font_create_referenced:
  * @ft_face:
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.38
  **/
 hb_font_t *
 hb_ft_font_create_referenced (FT_Face ft_face)
 {
   FT_Reference_Face (ft_face);
   return hb_ft_font_create (ft_face, (hb_destroy_func_t) FT_Done_Face);
 }
 
--- a/gfx/harfbuzz/src/hb-glib.cc
+++ b/gfx/harfbuzz/src/hb-glib.cc
@@ -377,16 +377,19 @@ hb_glib_get_unicode_funcs (void)
       HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
     }
   };
 
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
+/**
+ * Since: 0.9.38
+ **/
 hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes)
 {
   gsize size = 0;
   gconstpointer data = g_bytes_get_data (gbytes, &size);
   return hb_blob_create ((const char *) data,
 			 size,
 			 HB_MEMORY_MODE_READONLY,
--- a/gfx/harfbuzz/src/hb-gobject-structs.cc
+++ b/gfx/harfbuzz/src/hb-gobject-structs.cc
@@ -49,72 +49,32 @@ hb_gobject_##name##_get_type (void) \
       g_once_init_leave (&type_id, id); \
    } \
    return type_id; \
 }
 
 #define HB_DEFINE_OBJECT_TYPE(name) \
 	HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy);
 
+#define HB_DEFINE_VALUE_TYPE(name) \
+	static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \
+	{ \
+	  hb_##name##_t *c = (hb_##name##_t *) calloc (1, sizeof (hb_##name##_t)); \
+	  if (unlikely (!c)) return NULL; \
+	  *c = *l; \
+	  return c; \
+	} \
+	static void _hb_##name##_destroy (hb_##name##_t *l) { free (l); } \
+	HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy);
+
 HB_DEFINE_OBJECT_TYPE (buffer)
 HB_DEFINE_OBJECT_TYPE (blob)
 HB_DEFINE_OBJECT_TYPE (face)
 HB_DEFINE_OBJECT_TYPE (font)
 HB_DEFINE_OBJECT_TYPE (font_funcs)
 HB_DEFINE_OBJECT_TYPE (set)
 HB_DEFINE_OBJECT_TYPE (shape_plan)
 HB_DEFINE_OBJECT_TYPE (unicode_funcs)
-
-
-static hb_feature_t *feature_reference (hb_feature_t *g)
-{
-  hb_feature_t *c = (hb_feature_t *) calloc (1, sizeof (hb_feature_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void feature_destroy (hb_feature_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (feature, feature_reference, feature_destroy)
-
-static hb_glyph_info_t *glyph_info_reference (hb_glyph_info_t *g)
-{
-  hb_glyph_info_t *c = (hb_glyph_info_t *) calloc (1, sizeof (hb_glyph_info_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void glyph_info_destroy (hb_glyph_info_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (glyph_info, glyph_info_reference, glyph_info_destroy)
-
-static hb_glyph_position_t *glyph_position_reference (hb_glyph_position_t *g)
-{
-  hb_glyph_position_t *c = (hb_glyph_position_t *) calloc (1, sizeof (hb_glyph_position_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void glyph_position_destroy (hb_glyph_position_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (glyph_position, glyph_position_reference, glyph_position_destroy)
-
-static hb_segment_properties_t *segment_properties_reference (hb_segment_properties_t *g)
-{
-  hb_segment_properties_t *c = (hb_segment_properties_t *) calloc (1, sizeof (hb_segment_properties_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void segment_properties_destroy (hb_segment_properties_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (segment_properties, segment_properties_reference, segment_properties_destroy)
-
-static hb_user_data_key_t user_data_key_reference (hb_user_data_key_t l) { return l; }
-static void user_data_key_destroy (hb_user_data_key_t l) { }
-HB_DEFINE_BOXED_TYPE (user_data_key, user_data_key_reference, user_data_key_destroy)
-
-
-static hb_language_t *language_reference (hb_language_t *l)
-{
-  hb_language_t *c = (hb_language_t *) calloc (1, sizeof (hb_language_t));
-  if (unlikely (!c)) return NULL;
-  *c = *l;
-  return c;
-}
-static void language_destroy (hb_language_t *l) { free (l); }
-HB_DEFINE_BOXED_TYPE (language, language_reference, language_destroy)
+HB_DEFINE_VALUE_TYPE (feature)
+HB_DEFINE_VALUE_TYPE (glyph_info)
+HB_DEFINE_VALUE_TYPE (glyph_position)
+HB_DEFINE_VALUE_TYPE (segment_properties)
+HB_DEFINE_VALUE_TYPE (user_data_key)
--- a/gfx/harfbuzz/src/hb-gobject-structs.h
+++ b/gfx/harfbuzz/src/hb-gobject-structs.h
@@ -35,37 +35,55 @@
 
 #include <glib-object.h>
 
 HB_BEGIN_DECLS
 
 
 /* Object types */
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_blob_get_type (void);
 #define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_buffer_get_type (void);
 #define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_face_get_type (void);
 #define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_font_get_type (void);
 #define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_font_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ())
 
 GType hb_gobject_set_get_type (void);
 #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())
 
 GType hb_gobject_shape_plan_get_type (void);
 #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_unicode_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ())
 
 /* Value types */
 
 GType hb_gobject_feature_get_type (void);
 #define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ())
 
@@ -76,20 +94,12 @@ GType hb_gobject_glyph_position_get_type
 #define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ())
 
 GType hb_gobject_segment_properties_get_type (void);
 #define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ())
 
 GType hb_gobject_user_data_key_get_type (void);
 #define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ())
 
-/* Currently gobject-introspection doesn't understand that hb_language_t
- * can be passed by-value.  As such we box it up.  May remove in the
- * future.
- *
- *   https://bugzilla.gnome.org/show_bug.cgi?id=707656
- */
-GType hb_gobject_language_get_type (void);
-#define HB_GOBJECT_TYPE_LANGUAGE (hb_gobject_language_get_type ())
 
 HB_END_DECLS
 
 #endif /* HB_GOBJECT_H */
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -223,37 +223,38 @@ hb_bool_t
   gr_face *grface = HB_SHAPER_DATA_GET (face)->grface;
   gr_font *grfont = HB_SHAPER_DATA_GET (font);
 
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
   const char *lang_end = lang ? strchr (lang, '-') : NULL;
   int lang_len = lang_end ? lang_end - lang : -1;
   gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
 
-  while (num_features--)
+  for (unsigned int i = 0; i < num_features; i++)
   {
-    const gr_feature_ref *fref = gr_face_find_fref (grface, features->tag);
+    const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag);
     if (fref)
-      gr_fref_set_feature_value (fref, features->value, feats);
-    features++;
+      gr_fref_set_feature_value (fref, features[i].value, feats);
   }
 
   gr_segment *seg = NULL;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
 
   unsigned int scratch_size;
   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
 
   uint32_t *chars = (uint32_t *) scratch;
 
   for (unsigned int i = 0; i < buffer->len; ++i)
     chars[i] = buffer->info[i].codepoint;
 
+  /* TODO ensure_native_direction. */
+
   hb_tag_t script_tag[2];
   hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
 
   seg = gr_make_seg (grfont, grface,
 		     script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
 		     feats,
 		     gr_utf32, chars, buffer->len,
 		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
@@ -262,19 +263,21 @@ hb_bool_t
     if (feats) gr_featureval_destroy (feats);
     return false;
   }
 
   unsigned int glyph_count = gr_seg_n_slots (seg);
   if (unlikely (!glyph_count)) {
     if (feats) gr_featureval_destroy (feats);
     gr_seg_destroy (seg);
-    return false;
+    buffer->len = 0;
+    return true;
   }
 
+  buffer->ensure (glyph_count);
   scratch = buffer->get_scratch_buffer (&scratch_size);
   while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) +
 	  DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size)
   {
     if (unlikely (!buffer->ensure (buffer->allocated * 2)))
     {
       if (feats) gr_featureval_destroy (feats);
       gr_seg_destroy (seg);
@@ -326,51 +329,75 @@ hb_bool_t
     }
     clusters[ci].num_glyphs++;
 
     if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
 	clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
   }
   ci++;
 
-  //buffer->clear_output ();
   for (unsigned int i = 0; i < ci; ++i)
   {
     for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
     {
       hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
       info->codepoint = gids[clusters[i].base_glyph + j];
       info->cluster = clusters[i].cluster;
     }
   }
   buffer->len = glyph_count;
-  //buffer->swap_buffers ();
 
-  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
-    curradvx = gr_seg_advance_X(seg);
-
-  hb_glyph_position_t *pPos;
-  for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
-       is; pPos++, is = gr_slot_next_in_segment (is))
+  /* Positioning. */
+  if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+  {
+    hb_glyph_position_t *pPos;
+    for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
+         is; pPos++, is = gr_slot_next_in_segment (is))
+    {
+      pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+      pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
+      pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+      pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
+      curradvx += pPos->x_advance;
+      curradvy += pPos->y_advance;
+    }
+    pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
+  }
+  else
   {
-    pPos->x_offset = gr_slot_origin_X (is) - curradvx;
-    pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
-    pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
-    pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
-    if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL) + buffer->len - 1;
+    const hb_glyph_info_t *info = buffer->info + buffer->len - 1;
+    const hb_glyph_info_t *tinfo;
+    const gr_slot *tis;
+    int currclus = -1;
+    float clusx = 0., clusy = 0.;
+    for (is = gr_seg_last_slot (seg); is; pPos--, info--, is = gr_slot_prev_in_segment (is))
+    {
+      if (info->cluster != currclus)
+      {
+        curradvx += clusx;
+        curradvy += clusy;
+        currclus = info->cluster;
+        clusx = 0.;
+        clusy = 0.;
+        for (tis = is, tinfo = info; tis && tinfo->cluster == currclus; tis = gr_slot_prev_in_segment (tis), tinfo--)
+        {
+          clusx += gr_slot_advance_X (tis, grface, grfont);
+          clusy += gr_slot_advance_Y (tis, grface, grfont);
+        }
+        curradvx += clusx;
+        curradvy += clusy;
+      }
+      pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+      pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
       curradvx -= pPos->x_advance;
-    pPos->x_offset = gr_slot_origin_X (is) - curradvx;
-    if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-      curradvx += pPos->x_advance;
-    pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
-    curradvy += pPos->y_advance;
+      curradvy -= pPos->y_advance;
+      pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+      pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
+    }
+    hb_buffer_reverse_clusters (buffer);
   }
-  if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-    pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
-
-  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-    hb_buffer_reverse_clusters (buffer);
 
   if (feats) gr_featureval_destroy (feats);
   gr_seg_destroy (seg);
 
   return true;
 }
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -215,17 +215,17 @@ hb_ot_get_glyph_h_advance (hb_font_t *fo
 
 static hb_position_t
 hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_y (-ot_font->v_metrics.get_advance (glyph));
+  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph));
 }
 
 static hb_bool_t
 hb_ot_get_glyph_h_origin (hb_font_t *font HB_UNUSED,
 			  void *font_data HB_UNUSED,
 			  hb_codepoint_t glyph HB_UNUSED,
 			  hb_position_t *x HB_UNUSED,
 			  hb_position_t *y HB_UNUSED,
@@ -330,16 +330,19 @@ static hb_font_funcs_t *
 #undef HB_FONT_FUNC_IMPLEMENT
     }
   };
 
   return const_cast<hb_font_funcs_t *> (&ot_ffuncs);
 }
 
 
+/**
+ * Since: 0.9.28
+ **/
 void
 hb_ot_font_set_funcs (hb_font_t *font)
 {
   hb_ot_font_t *ot_font = _hb_ot_font_create (font);
   if (unlikely (!ot_font))
     return;
 
   hb_font_set_funcs (font,
--- a/gfx/harfbuzz/src/hb-ot-font.h
+++ b/gfx/harfbuzz/src/hb-ot-font.h
@@ -19,16 +19,20 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
 #ifndef HB_OT_FONT_H
 #define HB_OT_FONT_H
 
 #include "hb.h"
 
 HB_BEGIN_DECLS
 
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -323,18 +323,17 @@ struct hb_apply_context_t
     may_skip (const hb_apply_context_t *c,
 	      const hb_glyph_info_t    &info) const
     {
       if (!c->check_glyph_property (&info, lookup_props))
 	return SKIP_YES;
 
       if (unlikely (_hb_glyph_info_is_default_ignorable (&info) &&
 		    (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) &&
-		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) &&
-		    !_hb_glyph_info_ligated (&info)))
+		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info))))
 	return SKIP_MAYBE;
 
       return SKIP_NO;
     }
 
     protected:
     unsigned int lookup_props;
     bool ignore_zwnj;
@@ -715,17 +714,17 @@ static inline bool match_input (hb_apply
 				const void *match_data,
 				unsigned int *end_offset,
 				unsigned int match_positions[MAX_CONTEXT_LENGTH],
 				bool *p_is_mark_ligature = NULL,
 				unsigned int *p_total_component_count = NULL)
 {
   TRACE_APPLY (NULL);
 
-  if (unlikely (count > MAX_CONTEXT_LENGTH)) TRACE_RETURN (false);
+  if (unlikely (count > MAX_CONTEXT_LENGTH)) return TRACE_RETURN (false);
 
   hb_buffer_t *buffer = c->buffer;
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
   skippy_iter.reset (buffer->idx, count - 1);
   skippy_iter.set_match_func (match_func, match_data, input);
 
   /*
@@ -2160,17 +2159,17 @@ struct ExtensionFormat1
     return StructAtOffset<typename T::LookupSubTable> (this, offset);
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, format);
     if (unlikely (!c->may_dispatch (this, this))) TRACE_RETURN (c->default_return_value ());
-    return get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ());
+    return TRACE_RETURN (get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ()));
   }
 
   /* This is called from may_dispatch() above with hb_sanitize_context_t. */
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && extensionOffset != 0);
   }
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -31,16 +31,25 @@
 
 #include "hb-private.hh"
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-set-private.hh"
 
 
+/* Private API corresponding to hb-ot-layout.h: */
+
+HB_INTERNAL hb_bool_t
+hb_ot_layout_table_find_feature (hb_face_t    *face,
+				 hb_tag_t      table_tag,
+				 hb_tag_t      feature_tag,
+				 unsigned int *feature_index);
+
+
 /*
  * GDEF
  */
 
 typedef enum
 {
   /* The following three match LookupFlags::Ignore* numbers. */
   HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH	= 0x02u,
@@ -174,16 +183,40 @@ HB_INTERNAL void
 #define unicode_props0()	var2.u8[0]
 #define unicode_props1()	var2.u8[1]
 
 /* buffer var allocations, used during the GSUB/GPOS processing */
 #define glyph_props()		var1.u16[0] /* GDEF glyph properties */
 #define lig_props()		var1.u8[2] /* GSUB/GPOS ligature tracking */
 #define syllable()		var1.u8[3] /* GSUB/GPOS shaping boundaries */
 
+
+/* loop over syllables */
+
+#define foreach_syllable(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_syllable (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_syllable (buffer, start))
+
+static inline unsigned int
+_next_syllable (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  unsigned int syllable = info[start].syllable();
+  while (++start < count && syllable == info[start].syllable())
+    ;
+
+  return start;
+}
+
+
 /* unicode_props */
 
 enum {
   MASK0_ZWJ       = 0x20u,
   MASK0_ZWNJ      = 0x40u,
   MASK0_IGNORABLE = 0x80u,
   MASK0_GEN_CAT   = 0x1Fu
 };
@@ -220,20 +253,22 @@ static inline void
 }
 
 static inline unsigned int
 _hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
 {
   return info->unicode_props1();
 }
 
+static inline bool _hb_glyph_info_ligated (const hb_glyph_info_t *info);
+
 static inline hb_bool_t
 _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 {
-  return !!(info->unicode_props0() & MASK0_IGNORABLE);
+  return (info->unicode_props0() & MASK0_IGNORABLE) && !_hb_glyph_info_ligated (info);
 }
 
 static inline hb_bool_t
 _hb_glyph_info_is_zwnj (const hb_glyph_info_t *info)
 {
   return !!(info->unicode_props0() & MASK0_ZWNJ);
 }
 
@@ -401,16 +436,24 @@ static inline bool
 
 static inline void
 _hb_glyph_info_clear_ligated_and_multiplied (hb_glyph_info_t *info)
 {
   info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_LIGATED |
 			   HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED);
 }
 
+static inline void
+_hb_glyph_info_clear_substituted_and_ligated_and_multiplied (hb_glyph_info_t *info)
+{
+  info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED |
+			   HB_OT_LAYOUT_GLYPH_PROPS_LIGATED |
+			   HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED);
+}
+
 
 /* Allocation / deallocation. */
 
 static inline void
 _hb_buffer_allocate_unicode_vars (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props0);
   HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props1);
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -123,23 +123,29 @@ static inline const OT::GPOS&
  */
 
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face)
 {
   return _get_gdef (face).has_glyph_classes ();
 }
 
+/**
+ * Since: 0.9.7
+ **/
 hb_ot_layout_glyph_class_t
 hb_ot_layout_get_glyph_class (hb_face_t      *face,
 			      hb_codepoint_t  glyph)
 {
   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
 }
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
 				  hb_ot_layout_glyph_class_t  klass,
 				  hb_set_t                   *glyphs /* OUT */)
 {
   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
 }
 
@@ -280,16 +286,38 @@ hb_ot_layout_table_get_feature_tags (hb_
 				     unsigned int *feature_count /* IN/OUT */,
 				     hb_tag_t     *feature_tags /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 }
 
+hb_bool_t
+hb_ot_layout_table_find_feature (hb_face_t    *face,
+				 hb_tag_t      table_tag,
+				 hb_tag_t      feature_tag,
+				 unsigned int *feature_index)
+{
+  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  unsigned int num_features = g.get_feature_count ();
+  for (unsigned int i = 0; i < num_features; i++)
+  {
+    if (feature_tag == g.get_feature_tag (i)) {
+      if (feature_index) *feature_index = i;
+      return true;
+    }
+  }
+
+  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
+  return false;
+}
+
 
 unsigned int
 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
 				       hb_tag_t      table_tag,
 				       unsigned int  script_index,
 				       unsigned int  start_offset,
 				       unsigned int *language_count /* IN/OUT */,
 				       hb_tag_t     *language_tags /* OUT */)
@@ -330,16 +358,19 @@ hb_ot_layout_language_get_required_featu
   return hb_ot_layout_language_get_required_feature (face,
 						     table_tag,
 						     script_index,
 						     language_index,
 						     feature_index,
 						     NULL);
 }
 
+/**
+ * Since: 0.9.30
+ **/
 hb_bool_t
 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
 					    hb_tag_t      table_tag,
 					    unsigned int  script_index,
 					    unsigned int  language_index,
 					    unsigned int *feature_index,
 					    hb_tag_t     *feature_tag)
 {
@@ -414,30 +445,36 @@ hb_ot_layout_language_find_feature (hb_f
       return true;
     }
   }
 
   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
   return false;
 }
 
+/**
+ * Since: 0.9.7
+ **/
 unsigned int
 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
 				  hb_tag_t      table_tag,
 				  unsigned int  feature_index,
 				  unsigned int  start_offset,
 				  unsigned int *lookup_count /* IN/OUT */,
 				  unsigned int *lookup_indexes /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   const OT::Feature &f = g.get_feature (feature_index);
 
   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
 }
 
+/**
+ * Since: 0.9.22
+ **/
 unsigned int
 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 				     hb_tag_t      table_tag)
 {
   switch (table_tag)
   {
     case HB_OT_TAG_GSUB:
     {
@@ -585,16 +622,19 @@ static void
 						script_index,
 						language_index,
 						features,
 						lookup_indexes);
     }
   }
 }
 
+/**
+ * Since: 0.9.8
+ **/
 void
 hb_ot_layout_collect_lookups (hb_face_t      *face,
 			      hb_tag_t        table_tag,
 			      const hb_tag_t *scripts,
 			      const hb_tag_t *languages,
 			      const hb_tag_t *features,
 			      hb_set_t       *lookup_indexes /* OUT */)
 {
@@ -626,16 +666,19 @@ hb_ot_layout_collect_lookups (hb_face_t 
 						 script_index,
 						 languages,
 						 features,
 						 lookup_indexes);
     }
   }
 }
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  lookup_index,
 				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
 				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
 				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
 				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
@@ -671,16 +714,19 @@ hb_ot_layout_lookup_collect_glyphs (hb_f
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
   return &_get_gsub (face) != &OT::Null(OT::GSUB);
 }
 
+/**
+ * Since: 0.9.7
+ **/
 hb_bool_t
 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 				      unsigned int          lookup_index,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
 				      hb_bool_t             zero_context)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
@@ -709,16 +755,19 @@ hb_ot_layout_substitute_start (hb_font_t
 }
 
 void
 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GSUB::substitute_finish (font, buffer);
 }
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 				        unsigned int  lookup_index,
 				        hb_set_t     *glyphs)
 {
   OT::hb_closure_context_t c (face, glyphs);
 
   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
@@ -743,16 +792,19 @@ hb_ot_layout_position_start (hb_font_t *
 }
 
 void
 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GPOS::position_finish (font, buffer);
 }
 
+/**
+ * Since: 0.9.8
+ **/
 hb_bool_t
 hb_ot_layout_get_size_params (hb_face_t    *face,
 			      unsigned int *design_size,       /* OUT.  May be NULL */
 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
 			      unsigned int *range_start,       /* OUT.  May be NULL */
 			      unsigned int *range_end          /* OUT.  May be NULL */)
 {
@@ -870,17 +922,17 @@ apply_backward (OT::hb_apply_context_t *
 
   }
   while ((int) buffer->idx >= 0);
   return ret;
 }
 
 struct hb_apply_forward_context_t
 {
-  inline const char *get_name (void) { return "APPLY_FORWARD"; }
+  inline const char *get_name (void) { return "APPLY_FWD"; }
   static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
   typedef bool return_t;
   template <typename T, typename F>
   inline bool may_dispatch (const T *obj, const F *format) { return true; }
   template <typename T>
   inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; }
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -149,19 +149,20 @@ struct hb_ot_map_t
 
   hb_prealloced_array_t<feature_map_t, 8> features;
   hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<stage_map_t, 4> stages[2]; /* GSUB/GPOS */
 };
 
 enum hb_ot_map_feature_flags_t {
   F_NONE		= 0x0000u,
-  F_GLOBAL		= 0x0001u,
-  F_HAS_FALLBACK	= 0x0002u,
-  F_MANUAL_ZWJ		= 0x0004u
+  F_GLOBAL		= 0x0001u, /* Feature applies to all characters; results in no mask allocated for it. */
+  F_HAS_FALLBACK	= 0x0002u, /* Has fallback implementation, so include mask bit even if feature not found. */
+  F_MANUAL_ZWJ		= 0x0004u, /* Don't skip over ZWJ when matching. */
+  F_GLOBAL_SEARCH	= 0x0008u  /* If feature not found in LangSys, look for it in global feature list and pick one. */
 };
 /* Macro version for where const is desired. */
 #define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r)))
 static inline hb_ot_map_feature_flags_t
 operator | (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r)
 { return hb_ot_map_feature_flags_t ((unsigned int) l | (unsigned int) r); }
 static inline hb_ot_map_feature_flags_t
 operator & (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r)
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -211,16 +211,26 @@ hb_ot_map_builder_t::compile (hb_ot_map_
       }
       found |= hb_ot_layout_language_find_feature (face,
 						   table_tags[table_index],
 						   script_index[table_index],
 						   language_index[table_index],
 						   info->tag,
 						   &feature_index[table_index]);
     }
+    if (!found && (info->flags & F_GLOBAL_SEARCH))
+    {
+      for (unsigned int table_index = 0; table_index < 2; table_index++)
+      {
+	found |= hb_ot_layout_table_find_feature (face,
+						  table_tags[table_index],
+						  info->tag,
+						  &feature_index[table_index]);
+      }
+    }
     if (!found && !(info->flags & F_HAS_FALLBACK))
       continue;
 
 
     hb_ot_map_t::feature_map_t *map = m.features.push ();
     if (unlikely (!map))
       break;
 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+struct arabic_shape_plan_t;
+
+HB_INTERNAL void *
+data_create_arabic (const hb_ot_shape_plan_t *plan);
+
+HB_INTERNAL void
+data_destroy_arabic (void *data);
+
+HB_INTERNAL void
+setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
+			 hb_buffer_t               *buffer,
+			 hb_script_t                script);
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
@@ -1,20 +1,20 @@
 /* == Start of generated table == */
 /*
  * The following table is generated by running:
  *
  *   ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
  *
  * on files with these headers:
  *
- * # ArabicShaping-7.0.0.txt
- * # Date: 2014-02-14, 21:00:00 GMT [RP, KW, LI]
- * # Blocks-7.0.0.txt
- * # Date: 2014-04-03, 23:23:00 GMT [RP, KW]
+ * # ArabicShaping-8.0.0.txt
+ * # Date: 2015-02-17, 23:33:00 GMT [RP]
+ * # Blocks-8.0.0.txt
+ * # Date: 2014-11-10, 23:04:00 GMT [KW]
  * UnicodeData.txt does not have a header.
  */
 
 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
 #define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
 
 
 #define X	JOINING_TYPE_X
@@ -71,72 +71,72 @@ static const uint8_t joining_table[] =
   /* Mandaic */
 
   /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X,
   /* 0860 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 0880 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
 
   /* Arabic Extended-A */
 
-  /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,
+  /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,
 
-#define joining_offset_0x1806u 691
+#define joining_offset_0x1806u 693
 
   /* Mongolian */
 
   /* 1800 */             U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,
   /* 1880 */ U,U,U,U,U,U,U,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 18A0 */ D,D,D,D,D,D,D,D,D,X,D,
 
-#define joining_offset_0x200cu 856
+#define joining_offset_0x200cu 858
 
   /* General Punctuation */
 
   /* 2000 */                         U,C,
 
-#define joining_offset_0x2066u 858
+#define joining_offset_0x2066u 860
 
   /* General Punctuation */
 
   /* 2060 */             U,U,U,U,
 
-#define joining_offset_0xa840u 862
+#define joining_offset_0xa840u 864
 
   /* Phags-pa */
 
   /* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U,
 
-#define joining_offset_0x10ac0u 914
+#define joining_offset_0x10ac0u 916
 
   /* Manichaean */
 
   /* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D,
   /* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R,
 
-#define joining_offset_0x10b80u 962
+#define joining_offset_0x10b80u 964
 
   /* Psalter Pahlavi */
 
   /* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U,
 
-}; /* Table items: 1010; occupancy: 57% */
+}; /* Table items: 1012; occupancy: 57% */
 
 
 static unsigned int
 joining_type (hb_codepoint_t u)
 {
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0600u, 0x08B2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
+      if (hb_in_range (u, 0x0600u, 0x08B4u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
       break;
 
     case 0x1u:
       if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
       break;
 
     case 0x2u:
       if (hb_in_range (u, 0x200Cu, 0x200Du)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -137,26 +137,26 @@
 
 #define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \
 	OT_SUBLOOKUP(Name, 2, \
 		OT_OFFSET(Name, Name##Coverage) \
 		OT_LABEL_END \
 		OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \
 	) \
 	OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \
-	/* ASSERT_STATIC_EXPR len(FromGlyphs) == len(ToGlyphs) */
+	/* ASSERT_STATIC_EXPR_ZERO (len(FromGlyphs) == len(ToGlyphs)) */
 
 #define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \
 	OT_SUBLOOKUP(Name, 1, \
 		OT_OFFSET(Name, Name##Coverage) \
 		OT_LABEL_END \
 		OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \
 	) \
 	OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \
-	/* ASSERT_STATIC_EXPR len(FirstGlyphs) == len(LigatureSetOffsets) */
+	/* ASSERT_STATIC_EXPR_ZERO (len(FirstGlyphs) == len(LigatureSetOffsets)) */
 
 #define OT_LIGATURE_SET(Name, LigatureSetOffsets) \
 	OT_UARRAY(Name, OT_LIST(LigatureSetOffsets))
 
 #define OT_LIGATURE(Name, Components, LigGlyph) \
 	OT_LABEL_START(Name) \
 	LigGlyph \
 	OT_LABEL_END \
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -19,54 +19,54 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex-arabic-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 
 /*
+ * Joining types:
+ */
+
+/*
  * Bits used in the joining tables
  */
-enum {
+enum hb_arabic_joining_type_t {
   JOINING_TYPE_U		= 0,
   JOINING_TYPE_L		= 1,
   JOINING_TYPE_R		= 2,
   JOINING_TYPE_D		= 3,
   JOINING_TYPE_C		= JOINING_TYPE_D,
   JOINING_GROUP_ALAPH		= 4,
   JOINING_GROUP_DALATH_RISH	= 5,
   NUM_STATE_MACHINE_COLS	= 6,
 
   JOINING_TYPE_T = 7,
   JOINING_TYPE_X = 8  /* means: use general-category to choose between U or T. */
 };
 
-/*
- * Joining types:
- */
-
 #include "hb-ot-shape-complex-arabic-table.hh"
 
 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
 {
   unsigned int j_type = joining_type(u);
   if (likely (j_type != JOINING_TYPE_X))
     return j_type;
 
-  return (FLAG(gen_cat) &
+  return (FLAG_SAFE(gen_cat) &
 	  (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) |
 	   FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
 	   FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))
 	 ) ?  JOINING_TYPE_T : JOINING_TYPE_U;
 }
 
 #define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3')
 
@@ -207,17 +207,17 @@ struct arabic_shape_plan_t
    * having to do a "if (... < NONE) ..." and just rely on the fact that
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
 
   bool do_fallback;
   arabic_fallback_plan_t *fallback_plan;
 };
 
-static void *
+void *
 data_create_arabic (const hb_ot_shape_plan_t *plan)
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t));
   if (unlikely (!arabic_plan))
     return NULL;
 
   arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
@@ -225,17 +225,17 @@ data_create_arabic (const hb_ot_shape_pl
     arabic_plan->do_fallback = arabic_plan->do_fallback &&
 			       (FEATURE_IS_SYRIAC (arabic_features[i]) ||
 			        plan->map.needs_fallback (arabic_features[i]));
   }
 
   return arabic_plan;
 }
 
-static void
+void
 data_destroy_arabic (void *data)
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
   arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
 
   free (data);
 }
@@ -300,37 +300,44 @@ mongolian_variation_selectors (hb_buffer
   /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
     if (unlikely (hb_in_range (info[i].codepoint, 0x180Bu, 0x180Du)))
       info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action();
 }
 
-static void
-setup_masks_arabic (const hb_ot_shape_plan_t *plan,
-		    hb_buffer_t              *buffer,
-		    hb_font_t                *font HB_UNUSED)
+void
+setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
+			 hb_buffer_t               *buffer,
+			 hb_script_t                script)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
-  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
-
   arabic_joining (buffer);
-  if (plan->props.script == HB_SCRIPT_MONGOLIAN)
+  if (script == HB_SCRIPT_MONGOLIAN)
     mongolian_variation_selectors (buffer);
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
     info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()];
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
+static void
+setup_masks_arabic (const hb_ot_shape_plan_t *plan,
+		    hb_buffer_t              *buffer,
+		    hb_font_t                *font HB_UNUSED)
+{
+  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+  setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script);
+}
+
 
 static void
 nuke_joiners (const hb_ot_shape_plan_t *plan HB_UNUSED,
 	      hb_font_t *font HB_UNUSED,
 	      hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -204,23 +204,18 @@ preprocess_text_hangul (const hb_ot_shap
 	/* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
 	buffer->next_glyph ();
 	if (!is_zero_width_char (font, u))
 	{
 	  hb_glyph_info_t *info = buffer->out_info;
 	  hb_glyph_info_t tone = info[end];
 	  memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t));
 	  info[start] = tone;
+	  buffer->merge_out_clusters (start, end + 1);
 	}
-	/* Merge clusters across the (possibly reordered) syllable+tone.
-	 * We want to merge even in the zero-width tone mark case here,
-	 * so that clustering behavior isn't dependent on how the tone mark
-	 * is handled by the font.
-	 */
-	buffer->merge_out_clusters (start, end + 1);
       }
       else
       {
 	/* No valid syllable as base for tone mark; try to insert dotted circle. */
 	if (font->has_glyph (0x25CCu))
 	{
 	  hb_codepoint_t chars[2];
 	  if (!is_zero_width_char (font, u)) {
@@ -291,17 +286,18 @@ preprocess_text_hangul (const hb_ot_shap
 	if (t)
 	{
 	  buffer->cur().hangul_shaping_feature() = TJMO;
 	  buffer->next_glyph ();
 	  end = start + 3;
 	}
 	else
 	  end = start + 2;
-	buffer->merge_out_clusters (start, end);
+	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	  buffer->merge_out_clusters (start, end);
 	continue;
       }
     }
 
     else if (isCombinedS (u))
     {
       /* Have <LV>, <LVT>, or <LV,T> */
       hb_codepoint_t s = u;
@@ -363,17 +359,18 @@ preprocess_text_hangul (const hb_ot_shap
           }
           end = start + s_len;
 
 	  unsigned int i = start;
 	  info[i++].hangul_shaping_feature() = LJMO;
 	  info[i++].hangul_shaping_feature() = VJMO;
 	  if (i < end)
 	    info[i++].hangul_shaping_feature() = TJMO;
-	  buffer->merge_out_clusters (start, end);
+	  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	    buffer->merge_out_clusters (start, end);
 	  continue;
 	}
       }
 
       if (has_glyph)
       {
         /* We didn't decompose the S, so just advance past it. */
 	end = start + 1;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -156,18 +156,16 @@ enum indic_matra_category_t {
   INDIC_MATRA_CATEGORY_TOP_AND_LEFT			= INDIC_MATRA_CATEGORY_TOP,
   INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT		= INDIC_MATRA_CATEGORY_RIGHT,
   INDIC_MATRA_CATEGORY_TOP_AND_RIGHT			= INDIC_MATRA_CATEGORY_RIGHT,
 
   INDIC_MATRA_CATEGORY_OVERSTRUCK			= POS_AFTER_MAIN,
   INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT		= POS_PRE_M
 };
 
-/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
- * because gcc fails to optimize the latter and fills the table in at runtime. */
 #define INDIC_COMBINE_CATEGORIES(S,M) \
   (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || \
 			    ( \
 			     S == INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL || \
 			     S == INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK || \
 			     S == INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER || \
 			     S == INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA || \
 			     S == INDIC_SYLLABIC_CATEGORY_VIRAMA || \
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -137,17 +137,17 @@ is_ra (hb_codepoint_t u)
   return false;
 }
 
 static inline bool
 is_one_of (const hb_glyph_info_t &info, unsigned int flags)
 {
   /* If it ligated, all bets are off. */
   if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG (info.indic_category()) & flags);
+  return !!(FLAG_SAFE (info.indic_category()) & flags);
 }
 
 static inline bool
 is_joiner (const hb_glyph_info_t &info)
 {
   return is_one_of (info, JOINER_FLAGS);
 }
 
@@ -232,27 +232,27 @@ set_indic_properties (hb_glyph_info_t &i
   else if (unlikely (u == 0xA9BEu)) cat = OT_CM2; /* Javanese medial ya. */
   else if (unlikely (u == 0xA9BDu)) { cat = OT_M; pos = POS_POST_C; } /* Javanese vocalic r. */
 
 
   /*
    * Re-assign position.
    */
 
-  if ((FLAG (cat) & CONSONANT_FLAGS))
+  if ((FLAG_SAFE (cat) & CONSONANT_FLAGS))
   {
     pos = POS_BASE_C;
     if (is_ra (u))
       cat = OT_Ra;
   }
   else if (cat == OT_M)
   {
     pos = matra_position (u, pos);
   }
-  else if ((FLAG (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol))))
+  else if ((FLAG_SAFE (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol))))
   {
     pos = POS_SMVD;
   }
 
   if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
 
 
 
@@ -958,17 +958,17 @@ initial_reordering_consonant_syllable (c
       }
   }
 
   /* Attach misc marks to previous char to move with them. */
   {
     indic_position_t last_pos = POS_START;
     for (unsigned int i = start; i < end; i++)
     {
-      if ((FLAG (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS)))
+      if ((FLAG_SAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS)))
       {
 	info[i].indic_position() = last_pos;
 	if (unlikely (info[i].indic_category() == OT_H &&
 		      info[i].indic_position() == POS_PRE_M))
 	{
 	  /*
 	   * Uniscribe doesn't move the Halant with Left Matra.
 	   * TEST: U+092B,U+093F,U+094DE
@@ -1156,27 +1156,16 @@ initial_reordering_consonant_syllable (c
 	/* A ZWNJ disables HALF. */
 	if (non_joiner)
 	  info[j].mask &= ~indic_plan->mask_array[HALF];
 
       } while (j > start && !is_consonant (info[j]));
     }
 }
 
-
-static void
-initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We made the vowels look like consonants.  So let's call the consonant logic! */
-  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
-}
-
 static void
 initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
 				       hb_face_t *face,
 				       hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
   /* We treat placeholder/dotted-circle as if they are consonants, so we
    * should just chain.  Only if not in compatibility mode that is... */
@@ -1189,60 +1178,37 @@ initial_reordering_standalone_cluster (c
     if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
       return;
   }
 
   initial_reordering_consonant_syllable (plan, face, buffer, start, end);
 }
 
 static void
-initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We already inserted dotted-circles, so just call the standalone_cluster. */
-  initial_reordering_standalone_cluster (plan, face, buffer, start, end);
-}
-
-static void
-initial_reordering_symbol_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-				   hb_face_t *face HB_UNUSED,
-				   hb_buffer_t *buffer HB_UNUSED,
-				   unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-static void
-initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-				      hb_face_t *face HB_UNUSED,
-				      hb_buffer_t *buffer HB_UNUSED,
-				      unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-
-static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			     hb_face_t *face,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
-  switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable (plan, face, buffer, start, end); return;
-  case vowel_syllable:		initial_reordering_vowel_syllable     (plan, face, buffer, start, end); return;
-  case standalone_cluster:	initial_reordering_standalone_cluster (plan, face, buffer, start, end); return;
-  case symbol_cluster:		initial_reordering_symbol_cluster     (plan, face, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster     (plan, face, buffer, start, end); return;
-  case non_indic_cluster:	initial_reordering_non_indic_cluster  (plan, face, buffer, start, end); return;
+  switch (syllable_type)
+  {
+    case vowel_syllable: /* We made the vowels look like consonants.  So let's call the consonant logic! */
+    case consonant_syllable:
+     initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+     break;
+
+    case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */
+    case standalone_cluster:
+     initial_reordering_standalone_cluster (plan, face, buffer, start, end);
+     break;
+
+    case symbol_cluster:
+    case non_indic_cluster:
+      break;
   }
 }
 
 static inline void
 insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
@@ -1305,28 +1271,18 @@ insert_dotted_circles (const hb_ot_shape
 static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer)
 {
   update_consonant_positions (plan, font, buffer);
   insert_dotted_circles (plan, font, buffer);
 
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, font->face, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  initial_reordering_syllable (plan, font->face, buffer, last, count);
+  foreach_syllable (buffer, start, end)
+    initial_reordering_syllable (plan, font->face, buffer, start, end);
 }
 
 static void
 final_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t *buffer,
 			   unsigned int start, unsigned int end)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
@@ -1545,17 +1501,17 @@ final_reordering_syllable (const hb_ot_s
      *          consonant is found, the target position should be before the
      *          first matra, syllable modifier sign or vedic sign.
      */
     /* This is our take on what step 4 is trying to say (and failing, BADLY). */
     if (reph_pos == REPH_POS_AFTER_SUB)
     {
       new_reph_pos = base;
       while (new_reph_pos < end &&
-	     !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
+	     !( FLAG_SAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
 	new_reph_pos++;
       if (new_reph_pos < end)
         goto reph_move;
     }
 
     /*       5. If no consonant is found in steps 3 or 4, move reph to a position
      *          immediately before the first post-base matra, syllable modifier
      *          sign or vedic sign that has a reordering class after the intended
@@ -1696,17 +1652,17 @@ final_reordering_syllable (const hb_ot_s
         break;
       }
   }
 
 
   /* Apply 'init' to the Left Matra if it's a word start. */
   if (info[start].indic_position () == POS_PRE_M &&
       (!start ||
-       !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) &
+       !(FLAG_SAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
 	 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
     info[start].mask |= indic_plan->mask_array[INIT];
 
 
   /*
    * Finish off the clusters and go home!
    */
   if (hb_options ().uniscribe_bug_compatible)
@@ -1732,26 +1688,18 @@ final_reordering_syllable (const hb_ot_s
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font HB_UNUSED,
 		  hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
 
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      final_reordering_syllable (plan, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  final_reordering_syllable (plan, buffer, last, count);
+  foreach_syllable (buffer, start, end)
+    final_reordering_syllable (plan, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
 
 
 static void
 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -149,17 +149,17 @@ enum myanmar_category_t {
 };
 
 
 static inline bool
 is_one_of (const hb_glyph_info_t &info, unsigned int flags)
 {
   /* If it ligated, all bets are off. */
   if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG (info.myanmar_category()) & flags);
+  return !!(FLAG_SAFE (info.myanmar_category()) & flags);
 }
 
 static inline bool
 is_consonant (const hb_glyph_info_t &info)
 {
   return is_one_of (info, CONSONANT_FLAGS);
 }
 
@@ -299,19 +299,17 @@ compare_myanmar_order (const hb_glyph_in
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
 
 /* Rules from:
  * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm */
 
 static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
-				       hb_face_t *face,
-				       hb_buffer_t *buffer,
+initial_reordering_consonant_syllable (hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
   hb_glyph_info_t *info = buffer->info;
 
   unsigned int base = end;
   bool has_reph = false;
 
   {
@@ -394,58 +392,32 @@ initial_reordering_consonant_syllable (c
   }
 
   buffer->merge_clusters (start, end);
   /* Sit tight, rock 'n roll! */
   hb_bubble_sort (info + start, end - start, compare_myanmar_order);
 }
 
 static void
-initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We already inserted dotted-circles, so just call the consonant_syllable. */
-  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
-}
-
-static void
-initial_reordering_punctuation_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-					hb_face_t *face HB_UNUSED,
-					hb_buffer_t *buffer HB_UNUSED,
-					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-static void
-initial_reordering_non_myanmar_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-					hb_face_t *face HB_UNUSED,
-					hb_buffer_t *buffer HB_UNUSED,
-					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-
-static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			     hb_face_t *face,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
-  case punctuation_cluster:	initial_reordering_punctuation_cluster (plan, face, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
-  case non_myanmar_cluster:	initial_reordering_non_myanmar_cluster (plan, face, buffer, start, end); return;
+
+    case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
+    case consonant_syllable:
+      initial_reordering_consonant_syllable  (buffer, start, end);
+      break;
+
+    case punctuation_cluster:
+    case non_myanmar_cluster:
+      break;
   }
 }
 
 static inline void
 insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
@@ -500,28 +472,18 @@ insert_dotted_circles (const hb_ot_shape
 
 static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer)
 {
   insert_dotted_circles (plan, font, buffer);
 
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, font->face, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  initial_reordering_syllable (plan, font->face, buffer, last, count);
+  foreach_syllable (buffer, start, end)
+    initial_reordering_syllable (plan, font->face, buffer, start, end);
 }
 
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font HB_UNUSED,
 		  hb_buffer_t *buffer)
 {
   hb_glyph_info_t *info = buffer->info;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -54,19 +54,19 @@ enum hb_ot_shape_zero_width_marks_type_t
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (sea) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (use) \
   /* ^--- Add new shapers here */
 
 
 struct hb_ot_complex_shaper_t
 {
   char name[8];
 
   /* collect_features()
@@ -212,109 +212,40 @@ hb_ot_shape_complex_categorize (const hb
     /* Unicode-1.1 additions */
     case HB_SCRIPT_HEBREW:
 
       return &_hb_ot_complex_shaper_hebrew;
 
 
     /* ^--- Add new shapers here */
 
-
 #if 0
-    /* Note:
-     *
-     * These disabled scripts are listed in ucd/IndicSyllabicCategory.txt, but according
-     * to Martin Hosken and Jonathan Kew do not require complex shaping.
-     *
-     * TODO We should automate figuring out which scripts do not need complex shaping
-     *
-     * TODO We currently keep data for these scripts in our indic table.  Need to fix the
-     * generator to not do that.
-     */
-
-
-    /* Simple? */
-
-    /* Unicode-3.2 additions */
-    case HB_SCRIPT_BUHID:
-    case HB_SCRIPT_HANUNOO:
-
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_SAURASHTRA:
-
-    /* Unicode-6.0 additions */
-    case HB_SCRIPT_BATAK:
-    case HB_SCRIPT_BRAHMI:
-
-
-    /* Simple */
-
-    /* Unicode-1.1 additions */
-    /* These have their own shaper now. */
-    case HB_SCRIPT_LAO:
-    case HB_SCRIPT_THAI:
-
-    /* Unicode-3.2 additions */
-    case HB_SCRIPT_TAGALOG:
-    case HB_SCRIPT_TAGBANWA:
-
-    /* Unicode-4.0 additions */
-    case HB_SCRIPT_LIMBU:
-    case HB_SCRIPT_TAI_LE:
-
     /* Unicode-4.1 additions */
-    case HB_SCRIPT_KHAROSHTHI:
     case HB_SCRIPT_NEW_TAI_LUE:
-    case HB_SCRIPT_SYLOTI_NAGRI:
-
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_KAYAH_LI:
-
-    /* Unicode-5.2 additions */
-    case HB_SCRIPT_TAI_VIET:
-
-
 #endif
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_BENGALI:
     case HB_SCRIPT_DEVANAGARI:
     case HB_SCRIPT_GUJARATI:
     case HB_SCRIPT_GURMUKHI:
     case HB_SCRIPT_KANNADA:
     case HB_SCRIPT_MALAYALAM:
     case HB_SCRIPT_ORIYA:
     case HB_SCRIPT_TAMIL:
     case HB_SCRIPT_TELUGU:
 
     /* Unicode-3.0 additions */
     case HB_SCRIPT_SINHALA:
 
-    /* Unicode-5.0 additions */
-    case HB_SCRIPT_BALINESE:
-
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_LEPCHA:
-    case HB_SCRIPT_REJANG:
-    case HB_SCRIPT_SUNDANESE:
-
     /* Unicode-5.2 additions */
     case HB_SCRIPT_JAVANESE:
-    case HB_SCRIPT_KAITHI:
-    case HB_SCRIPT_MEETEI_MAYEK:
-
-    /* Unicode-6.0 additions */
-
-    /* Unicode-6.1 additions */
-    case HB_SCRIPT_CHAKMA:
-    case HB_SCRIPT_SHARADA:
-    case HB_SCRIPT_TAKRI:
 
       /* If the designer designed the font for the 'DFLT' script,
-       * use the default shaper.  Otherwise, use the Indic shaper.
+       * use the default shaper.  Otherwise, use the specific shaper.
        * Note that for some simple scripts, there may not be *any*
        * GSUB/GPOS needed, so there may be no scripts found! */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
 	return &_hb_ot_complex_shaper_default;
       else
 	return &_hb_ot_complex_shaper_indic;
 
     case HB_SCRIPT_KHMER:
@@ -336,30 +267,89 @@ hb_ot_shape_complex_categorize (const hb
     case HB_SCRIPT_MYANMAR:
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
 	return &_hb_ot_complex_shaper_myanmar;
       else if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
 	return &_hb_ot_complex_shaper_myanmar_old;
       else
 	return &_hb_ot_complex_shaper_default;
 
+
+    /* Unicode-2.0 additions */
+    //case HB_SCRIPT_TIBETAN:
+
+    /* Unicode-3.0 additions */
+    //case HB_SCRIPT_MONGOLIAN:
+    //case HB_SCRIPT_SINHALA:
+
+    /* Unicode-3.2 additions */
+    case HB_SCRIPT_BUHID:
+    case HB_SCRIPT_HANUNOO:
+    case HB_SCRIPT_TAGALOG:
+    case HB_SCRIPT_TAGBANWA:
+
+    /* Unicode-4.0 additions */
+    case HB_SCRIPT_LIMBU:
+    case HB_SCRIPT_TAI_LE:
+
     /* Unicode-4.1 additions */
     case HB_SCRIPT_BUGINESE:
+    case HB_SCRIPT_KHAROSHTHI:
+    case HB_SCRIPT_SYLOTI_NAGRI:
+    case HB_SCRIPT_TIFINAGH:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_BALINESE:
+    //case HB_SCRIPT_NKO:
+    //case HB_SCRIPT_PHAGS_PA:
 
     /* Unicode-5.1 additions */
     case HB_SCRIPT_CHAM:
+    case HB_SCRIPT_KAYAH_LI:
+    case HB_SCRIPT_LEPCHA:
+    case HB_SCRIPT_REJANG:
+    case HB_SCRIPT_SAURASHTRA:
+    case HB_SCRIPT_SUNDANESE:
 
     /* Unicode-5.2 additions */
+    case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS:
+    //case HB_SCRIPT_JAVANESE:
+    case HB_SCRIPT_KAITHI:
+    case HB_SCRIPT_MEETEI_MAYEK:
     case HB_SCRIPT_TAI_THAM:
+    case HB_SCRIPT_TAI_VIET:
+
+    /* Unicode-6.0 additions */
+    case HB_SCRIPT_BATAK:
+    case HB_SCRIPT_BRAHMI:
+    //case HB_SCRIPT_MANDAIC:
+
+    /* Unicode-6.1 additions */
+    case HB_SCRIPT_CHAKMA:
+    case HB_SCRIPT_SHARADA:
+    case HB_SCRIPT_TAKRI:
+
+    /* Unicode-7.0 additions */
+    case HB_SCRIPT_DUPLOYAN:
+    case HB_SCRIPT_GRANTHA:
+    case HB_SCRIPT_KHOJKI:
+    case HB_SCRIPT_KHUDAWADI:
+    case HB_SCRIPT_MAHAJANI:
+    //case HB_SCRIPT_MANICHAEAN:
+    case HB_SCRIPT_MODI:
+    case HB_SCRIPT_PAHAWH_HMONG:
+    //case HB_SCRIPT_PSALTER_PAHLAVI:
+    case HB_SCRIPT_SIDDHAM:
+    case HB_SCRIPT_TIRHUTA:
 
       /* If the designer designed the font for the 'DFLT' script,
-       * use the default shaper.  Otherwise, use the Indic shaper.
+       * use the default shaper.  Otherwise, use the specific shaper.
        * Note that for some simple scripts, there may not be *any*
        * GSUB/GPOS needed, so there may be no scripts found! */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
 	return &_hb_ot_complex_shaper_default;
       else
-	return &_hb_ot_complex_shaper_sea;
+	return &_hb_ot_complex_shaper_use;
   }
 }
 
 
 #endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.hh
+++ /dev/null
@@ -1,224 +0,0 @@
-
-#line 1 "hb-ot-shape-complex-sea-machine.rl"
-/*
- * Copyright © 2011,2012,2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-
-#include "hb-private.hh"
-
-
-#line 36 "hb-ot-shape-complex-sea-machine.hh"
-static const unsigned char _sea_syllable_machine_trans_keys[] = {
-	1u, 1u, 1u, 1u, 1u, 29u, 3u, 29u, 3u, 29u, 1u, 1u, 0
-};
-
-static const char _sea_syllable_machine_key_spans[] = {
-	1, 1, 29, 27, 27, 1
-};
-
-static const char _sea_syllable_machine_index_offsets[] = {
-	0, 2, 4, 34, 62, 90
-};
-
-static const char _sea_syllable_machine_indicies[] = {
-	1, 0, 3, 2, 1, 1, 3, 5, 
-	4, 4, 4, 4, 4, 3, 4, 1, 
-	4, 4, 4, 4, 3, 4, 4, 4, 
-	4, 3, 4, 4, 4, 3, 3, 3, 
-	3, 4, 1, 7, 6, 6, 6, 6, 
-	6, 1, 6, 6, 6, 6, 6, 6, 
-	1, 6, 6, 6, 6, 1, 6, 6, 
-	6, 1, 1, 1, 1, 6, 3, 9, 
-	8, 8, 8, 8, 8, 3, 8, 8, 
-	8, 8, 8, 8, 3, 8, 8, 8, 
-	8, 3, 8, 8, 8, 3, 3, 3, 
-	3, 8, 3, 10, 0
-};
-
-static const char _sea_syllable_machine_trans_targs[] = {
-	2, 3, 2, 4, 2, 5, 2, 0, 
-	2, 1, 2
-};
-
-static const char _sea_syllable_machine_trans_actions[] = {
-	1, 2, 3, 2, 6, 0, 7, 0, 
-	8, 0, 9
-};
-
-static const char _sea_syllable_machine_to_state_actions[] = {
-	0, 0, 4, 0, 0, 0
-};
-
-static const char _sea_syllable_machine_from_state_actions[] = {
-	0, 0, 5, 0, 0, 0
-};
-
-static const char _sea_syllable_machine_eof_trans[] = {
-	1, 3, 0, 7, 9, 11
-};
-
-static const int sea_syllable_machine_start = 2;
-static const int sea_syllable_machine_first_final = 2;
-static const int sea_syllable_machine_error = -1;
-
-static const int sea_syllable_machine_en_main = 2;
-
-
-#line 36 "hb-ot-shape-complex-sea-machine.rl"
-
-
-
-#line 67 "hb-ot-shape-complex-sea-machine.rl"
-
-
-#define found_syllable(syllable_type) \
-  HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
-
-static void
-find_syllables (hb_buffer_t *buffer)
-{
-  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  
-#line 117 "hb-ot-shape-complex-sea-machine.hh"
-	{
-	cs = sea_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 88 "hb-ot-shape-complex-sea-machine.rl"
-
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int last = 0;
-  unsigned int syllable_serial = 1;
-  
-#line 134 "hb-ot-shape-complex-sea-machine.hh"
-	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-_resume:
-	switch ( _sea_syllable_machine_from_state_actions[cs] ) {
-	case 5:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 148 "hb-ot-shape-complex-sea-machine.hh"
-	}
-
-	_keys = _sea_syllable_machine_trans_keys + (cs<<1);
-	_inds = _sea_syllable_machine_indicies + _sea_syllable_machine_index_offsets[cs];
-
-	_slen = _sea_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].sea_category()) &&
-		( info[p].sea_category()) <= _keys[1] ?
-		( info[p].sea_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _sea_syllable_machine_trans_targs[_trans];
-
-	if ( _sea_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _sea_syllable_machine_trans_actions[_trans] ) {
-	case 2:
-#line 1 "NONE"
-	{te = p+1;}
-	break;
-	case 6:
-#line 63 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p+1;{ found_syllable (non_sea_cluster); }}
-	break;
-	case 7:
-#line 61 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p;p--;{ found_syllable (consonant_syllable); }}
-	break;
-	case 8:
-#line 62 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p;p--;{ found_syllable (broken_cluster); }}
-	break;
-	case 9:
-#line 63 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p;p--;{ found_syllable (non_sea_cluster); }}
-	break;
-	case 1:
-#line 61 "hb-ot-shape-complex-sea-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
-	break;
-	case 3:
-#line 62 "hb-ot-shape-complex-sea-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
-	break;
-#line 194 "hb-ot-shape-complex-sea-machine.hh"
-	}
-
-_again:
-	switch ( _sea_syllable_machine_to_state_actions[cs] ) {
-	case 4:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 203 "hb-ot-shape-complex-sea-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _sea_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _sea_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 97 "hb-ot-shape-complex-sea-machine.rl"
-
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.rl
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright © 2011,2012,2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-
-#include "hb-private.hh"
-
-%%{
-  machine sea_syllable_machine;
-  alphtype unsigned char;
-  write data;
-}%%
-
-%%{
-
-# Same order as enum sea_category_t.  Not sure how to avoid duplication.
-C    = 1;
-GB   = 12; # Generic Base
-H    = 4;  # Halant
-IV   = 2;  # Independent Vowel
-MR   = 22; # Medial Ra
-CM   = 17; # Consonant Medial
-VAbv = 26;
-VBlw = 27;
-VPre = 28;
-VPst = 29;
-T    = 3;  # Tone Marks
-A    = 10; # Anusvara
-
-syllable_tail = (VPre|VAbv|VBlw|VPst|H.C|CM|MR|T|A)*;
-
-consonant_syllable =	(C|IV|GB) syllable_tail;
-broken_cluster =	syllable_tail;
-other =			any;
-
-main := |*
-	consonant_syllable	=> { found_syllable (consonant_syllable); };
-	broken_cluster		=> { found_syllable (broken_cluster); };
-	other			=> { found_syllable (non_sea_cluster); };
-*|;
-
-
-}%%
-
-#define found_syllable(syllable_type) \
-  HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
-
-static void
-find_syllables (hb_buffer_t *buffer)
-{
-  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  %%{
-    write init;
-    getkey info[p].sea_category();
-  }%%
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int last = 0;
-  unsigned int syllable_serial = 1;
-  %%{
-    write exec;
-  }%%
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright © 2011,2012,2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ot-shape-complex-indic-private.hh"
-
-/* buffer var allocations */
-#define sea_category() complex_var_u8_0() /* indic_category_t */
-#define sea_position() complex_var_u8_1() /* indic_position_t */
-
-
-/*
- * South-East Asian shaper.
- * Loosely based on the Myanmar spec / shaper.
- * There is no OpenType spec for this.
- */
-
-static const hb_tag_t
-basic_features[] =
-{
-  /*
-   * Basic features.
-   * These features are applied in order, one at a time, after initial_reordering.
-   */
-  HB_TAG('p','r','e','f'),
-  HB_TAG('a','b','v','f'),
-  HB_TAG('b','l','w','f'),
-  HB_TAG('p','s','t','f'),
-};
-static const hb_tag_t
-other_features[] =
-{
-  /*
-   * Other features.
-   * These features are applied all at once, after final_reordering.
-   */
-  HB_TAG('p','r','e','s'),
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('p','s','t','s'),
-  /* Positioning features, though we don't care about the types. */
-  HB_TAG('d','i','s','t'),
-};
-
-static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
-static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer);
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
-		  hb_buffer_t *buffer);
-
-static void
-collect_features_sea (hb_ot_shape_planner_t *plan)
-{
-  hb_ot_map_builder_t *map = &plan->map;
-
-  /* Do this before any lookups have been applied. */
-  map->add_gsub_pause (setup_syllables);
-
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
-  /* The Indic specs do not require ccmp, but we apply it here since if
-   * there is a use of it, it's typically at the beginning. */
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
-
-  map->add_gsub_pause (initial_reordering);
-  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
-  {
-    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
-    map->add_gsub_pause (NULL);
-  }
-  map->add_gsub_pause (final_reordering);
-  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
-    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
-}
-
-static void
-override_features_sea (hb_ot_shape_planner_t *plan)
-{
-  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
-}
-
-
-enum syllable_type_t {
-  consonant_syllable,
-  broken_cluster,
-  non_sea_cluster,
-};
-
-#include "hb-ot-shape-complex-sea-machine.hh"
-
-
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum sea_category_t {
-//  OT_C    = 1,
-  OT_GB   = 12, /* Generic Base XXX DOTTED CIRCLE only for now */
-//  OT_H    = 4,  /* Halant */
-  OT_IV   = 2,  /* Independent Vowel */
-  OT_MR   = 22, /* Medial Ra */
-//  OT_CM   = 17, /* Consonant Medial */
-  OT_VAbv = 26,
-  OT_VBlw = 27,
-  OT_VPre = 28,
-  OT_VPst = 29,
-  OT_T    = 3,  /* Tone Marks */
-//  OT_A    = 10, /* Anusvara */
-};
-
-static inline void
-set_sea_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
-  indic_position_t pos = (indic_position_t) (type >> 8);
-
-  /* Medial Ra */
-  if (u == 0x1A55u || u == 0xAA34u)
-    cat = (indic_category_t) OT_MR;
-
-  if (cat == OT_M)
-  {
-    switch ((int) pos)
-    {
-      case POS_PRE_C:	cat = (indic_category_t) OT_VPre; break;
-      case POS_ABOVE_C:	cat = (indic_category_t) OT_VAbv; break;
-      case POS_BELOW_C:	cat = (indic_category_t) OT_VBlw; break;
-      case POS_POST_C:	cat = (indic_category_t) OT_VPst; break;
-    }
-  }
-
-  info.sea_category() = (sea_category_t) cat;
-  info.sea_position() = pos;
-}
-
-
-static void
-setup_masks_sea (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_buffer_t              *buffer,
-		 hb_font_t                *font HB_UNUSED)
-{
-  HB_BUFFER_ALLOCATE_VAR (buffer, sea_category);
-  HB_BUFFER_ALLOCATE_VAR (buffer, sea_position);
-
-  /* We cannot setup masks here.  We save information about characters
-   * and setup masks later on in a pause-callback. */
-
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    set_sea_properties (info[i]);
-}
-
-static void
-setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
-{
-  find_syllables (buffer);
-}
-
-static int
-compare_sea_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
-{
-  int a = pa->sea_position();
-  int b = pb->sea_position();
-
-  return a < b ? -1 : a == b ? 0 : +1;
-}
-
-
-static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
-				       hb_face_t *face,
-				       hb_buffer_t *buffer,
-				       unsigned int start, unsigned int end)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int base = start;
-
-  /* Reorder! */
-  unsigned int i = start;
-  for (; i < base; i++)
-    info[i].sea_position() = POS_PRE_C;
-  if (i < end)
-  {
-    info[i].sea_position() = POS_BASE_C;
-    i++;
-  }
-  for (; i < end; i++)
-  {
-    if (info[i].sea_category() == OT_MR) /* Pre-base reordering */
-    {
-      info[i].sea_position() = POS_PRE_C;
-      continue;
-    }
-    if (info[i].sea_category() == OT_VPre) /* Left matra */
-    {
-      info[i].sea_position() = POS_PRE_M;
-      continue;
-    }
-
-    info[i].sea_position() = POS_AFTER_MAIN;
-  }
-
-  buffer->merge_clusters (start, end);
-  /* Sit tight, rock 'n roll! */
-  hb_bubble_sort (info + start, end - start, compare_sea_order);
-}
-
-static void
-initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We already inserted dotted-circles, so just call the consonant_syllable. */
-  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
-}
-
-static void
-initial_reordering_non_sea_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-				    hb_face_t *face HB_UNUSED,
-				    hb_buffer_t *buffer HB_UNUSED,
-				    unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-
-static void
-initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			     hb_face_t *face,
-			     hb_buffer_t *buffer,
-			     unsigned int start, unsigned int end)
-{
-  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
-  switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
-  case non_sea_cluster:		initial_reordering_non_sea_cluster     (plan, face, buffer, start, end); return;
-  }
-}
-
-static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		       hb_font_t *font,
-		       hb_buffer_t *buffer)
-{
-  /* Note: This loop is extra overhead, but should not be measurable. */
-  bool has_broken_syllables = false;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == broken_cluster)
-    {
-      has_broken_syllables = true;
-      break;
-    }
-  if (likely (!has_broken_syllables))
-    return;
-
-
-  hb_codepoint_t dottedcircle_glyph;
-  if (!font->get_glyph (0x25CCu, 0, &dottedcircle_glyph))
-    return;
-
-  hb_glyph_info_t dottedcircle = {0};
-  dottedcircle.codepoint = 0x25CCu;
-  set_sea_properties (dottedcircle);
-  dottedcircle.codepoint = dottedcircle_glyph;
-
-  buffer->clear_output ();
-
-  buffer->idx = 0;
-  unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len)
-  {
-    unsigned int syllable = buffer->cur().syllable();
-    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
-    {
-      last_syllable = syllable;
-
-      hb_glyph_info_t info = dottedcircle;
-      info.cluster = buffer->cur().cluster;
-      info.mask = buffer->cur().mask;
-      info.syllable() = buffer->cur().syllable();
-
-      buffer->output_info (info);
-    }
-    else
-      buffer->next_glyph ();
-  }
-
-  buffer->swap_buffers ();
-}
-
-static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer)
-{
-  insert_dotted_circles (plan, font, buffer);
-
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, font->face, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  initial_reordering_syllable (plan, font->face, buffer, last, count);
-}
-
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font HB_UNUSED,
-		  hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-
-  /* Zero syllables now... */
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
-
-  HB_BUFFER_DEALLOCATE_VAR (buffer, sea_category);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, sea_position);
-}
-
-
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea =
-{
-  "sea",
-  collect_features_sea,
-  override_features_sea,
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
-  NULL, /* decompose */
-  NULL, /* compose */
-  setup_masks_sea,
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
-  false, /* fallback_position */
-};
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
@@ -0,0 +1,548 @@
+
+#line 1 "hb-ot-shape-complex-use-machine.rl"
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+
+#include "hb-private.hh"
+
+
+#line 38 "hb-ot-shape-complex-use-machine.hh"
+static const unsigned char _use_syllable_machine_trans_keys[] = {
+	0u, 0u, 4u, 4u, 1u, 1u, 0u, 39u, 21u, 21u, 8u, 39u, 8u, 39u, 1u, 1u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 1u, 1u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 
+	8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 12u, 21u, 12u, 13u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 
+	8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 1u, 39u, 8u, 39u, 21u, 42u, 41u, 42u, 
+	42u, 42u, 0
+};
+
+static const char _use_syllable_machine_key_spans[] = {
+	0, 1, 1, 40, 1, 32, 32, 1, 
+	32, 32, 32, 19, 19, 19, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 1, 32, 32, 19, 19, 
+	19, 32, 32, 32, 32, 32, 32, 32, 
+	32, 10, 2, 32, 32, 32, 32, 19, 
+	19, 19, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 39, 32, 22, 2, 
+	1
+};
+
+static const short _use_syllable_machine_index_offsets[] = {
+	0, 0, 2, 4, 45, 47, 80, 113, 
+	115, 148, 181, 214, 234, 254, 274, 307, 
+	340, 373, 406, 439, 472, 505, 538, 571, 
+	604, 637, 670, 703, 705, 738, 771, 791, 
+	811, 831, 864, 897, 930, 963, 996, 1029, 
+	1062, 1095, 1106, 1109, 1142, 1175, 1208, 1241, 
+	1261, 1281, 1301, 1334, 1367, 1400, 1433, 1466, 
+	1499, 1532, 1565, 1598, 1631, 1671, 1704, 1727, 
+	1730
+};
+
+static const char _use_syllable_machine_indicies[] = {
+	1, 0, 3, 2, 4, 5, 6, 
+	4, 1, 5, 8, 8, 7, 8, 8, 
+	3, 9, 8, 8, 8, 4, 4, 10, 
+	11, 8, 8, 12, 13, 14, 15, 16, 
+	17, 18, 12, 19, 20, 21, 22, 23, 
+	24, 8, 25, 26, 27, 8, 29, 28, 
+	31, 30, 30, 32, 33, 30, 30, 30, 
+	30, 30, 30, 30, 30, 34, 35, 36, 
+	37, 38, 39, 40, 41, 35, 42, 34, 
+	43, 44, 45, 46, 30, 47, 48, 49, 
+	30, 31, 30, 30, 32, 33, 30, 30, 
+	30, 30, 30, 30, 30, 30, 50, 35, 
+	36, 37, 38, 39, 40, 41, 35, 42, 
+	43, 43, 44, 45, 46, 30, 47, 48, 
+	49, 30, 32, 51, 31, 30, 30, 32, 
+	33, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 35, 36, 37, 38, 39, 40, 
+	41, 35, 42, 43, 43, 44, 45, 46, 
+	30, 47, 48, 49, 30, 31, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 35, 36, 37, 38, 39, 
+	30, 30, 30, 30, 30, 30, 44, 45, 
+	46, 30, 47, 48, 49, 30, 31, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 36, 37, 38, 
+	39, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 47, 48, 49, 30, 31, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 37, 
+	38, 39, 30, 31, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 38, 39, 30, 31, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 39, 30, 31, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 37, 38, 39, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	47, 48, 49, 30, 31, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 37, 38, 39, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 48, 49, 30, 31, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 37, 38, 39, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 49, 30, 31, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 36, 37, 38, 
+	39, 30, 30, 30, 30, 30, 30, 44, 
+	45, 46, 30, 47, 48, 49, 30, 31, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 36, 37, 
+	38, 39, 30, 30, 30, 30, 30, 30, 
+	30, 45, 46, 30, 47, 48, 49, 30, 
+	31, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 36, 
+	37, 38, 39, 30, 30, 30, 30, 30, 
+	30, 30, 30, 46, 30, 47, 48, 49, 
+	30, 31, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 35, 
+	36, 37, 38, 39, 30, 41, 35, 30, 
+	30, 30, 44, 45, 46, 30, 47, 48, 
+	49, 30, 31, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	35, 36, 37, 38, 39, 30, 30, 35, 
+	30, 30, 30, 44, 45, 46, 30, 47, 
+	48, 49, 30, 31, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 35, 36, 37, 38, 39, 40, 41, 
+	35, 30, 30, 30, 44, 45, 46, 30, 
+	47, 48, 49, 30, 31, 30, 30, 32, 
+	33, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 35, 36, 37, 38, 39, 40, 
+	41, 35, 42, 30, 43, 44, 45, 46, 
+	30, 47, 48, 49, 30, 31, 30, 30, 
+	32, 33, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 35, 36, 37, 38, 39, 
+	40, 41, 35, 42, 34, 43, 44, 45, 
+	46, 30, 47, 48, 49, 30, 53, 52, 
+	52, 54, 55, 52, 52, 52, 52, 52, 
+	52, 52, 52, 56, 52, 57, 58, 59, 
+	60, 61, 62, 57, 63, 56, 64, 52, 
+	52, 52, 52, 65, 66, 67, 52, 53, 
+	52, 52, 54, 55, 52, 52, 52, 52, 
+	52, 52, 52, 52, 68, 52, 57, 58, 
+	59, 60, 61, 62, 57, 63, 64, 64, 
+	52, 52, 52, 52, 65, 66, 67, 52, 
+	54, 51, 53, 52, 52, 54, 55, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 57, 58, 59, 60, 61, 62, 57, 
+	63, 64, 64, 52, 52, 52, 52, 65, 
+	66, 67, 52, 53, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 57, 58, 59, 60, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	65, 66, 67, 52, 53, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 58, 59, 60, 52, 
+	53, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 59, 60, 52, 53, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 60, 52, 
+	53, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	58, 59, 60, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 65, 66, 67, 
+	52, 53, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 58, 59, 60, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 66, 
+	67, 52, 53, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 58, 59, 60, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 67, 52, 53, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 57, 58, 59, 60, 52, 62, 
+	57, 52, 52, 52, 52, 52, 52, 52, 
+	65, 66, 67, 52, 53, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 57, 58, 59, 60, 52, 
+	52, 57, 52, 52, 52, 52, 52, 52, 
+	52, 65, 66, 67, 52, 53, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 57, 58, 59, 60, 
+	61, 62, 57, 52, 52, 52, 52, 52, 
+	52, 52, 65, 66, 67, 52, 53, 52, 
+	52, 54, 55, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 57, 58, 59, 
+	60, 61, 62, 57, 63, 52, 64, 52, 
+	52, 52, 52, 65, 66, 67, 52, 53, 
+	52, 52, 54, 55, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 57, 58, 
+	59, 60, 61, 62, 57, 63, 56, 64, 
+	52, 52, 52, 52, 65, 66, 67, 52, 
+	70, 71, 69, 69, 69, 69, 69, 69, 
+	69, 72, 69, 70, 71, 69, 7, 73, 
+	73, 3, 9, 73, 73, 73, 73, 73, 
+	73, 73, 73, 74, 12, 13, 14, 15, 
+	16, 17, 18, 12, 19, 21, 21, 22, 
+	23, 24, 73, 25, 26, 27, 73, 7, 
+	73, 73, 3, 9, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 12, 13, 14, 
+	15, 16, 17, 18, 12, 19, 21, 21, 
+	22, 23, 24, 73, 25, 26, 27, 73, 
+	7, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 12, 13, 
+	14, 15, 16, 73, 73, 73, 73, 73, 
+	73, 22, 23, 24, 73, 25, 26, 27, 
+	73, 7, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	13, 14, 15, 16, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 25, 26, 
+	27, 73, 7, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 14, 15, 16, 73, 7, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 15, 
+	16, 73, 7, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 16, 73, 7, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 14, 15, 
+	16, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 25, 26, 27, 73, 7, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 14, 
+	15, 16, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 26, 27, 73, 
+	7, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	14, 15, 16, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 27, 
+	73, 7, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	13, 14, 15, 16, 73, 73, 73, 73, 
+	73, 73, 22, 23, 24, 73, 25, 26, 
+	27, 73, 7, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 13, 14, 15, 16, 73, 73, 73, 
+	73, 73, 73, 73, 23, 24, 73, 25, 
+	26, 27, 73, 7, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 13, 14, 15, 16, 73, 73, 
+	73, 73, 73, 73, 73, 73, 24, 73, 
+	25, 26, 27, 73, 7, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 12, 13, 14, 15, 16, 73, 
+	18, 12, 73, 73, 73, 22, 23, 24, 
+	73, 25, 26, 27, 73, 7, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 12, 13, 14, 15, 16, 
+	73, 73, 12, 73, 73, 73, 22, 23, 
+	24, 73, 25, 26, 27, 73, 7, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 12, 13, 14, 15, 
+	16, 17, 18, 12, 73, 73, 73, 22, 
+	23, 24, 73, 25, 26, 27, 73, 7, 
+	73, 73, 3, 9, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 12, 13, 14, 
+	15, 16, 17, 18, 12, 19, 73, 21, 
+	22, 23, 24, 73, 25, 26, 27, 73, 
+	5, 6, 73, 73, 5, 73, 73, 7, 
+	73, 73, 3, 9, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 12, 13, 14, 
+	15, 16, 17, 18, 12, 19, 20, 21, 
+	22, 23, 24, 73, 25, 26, 27, 73, 
+	7, 73, 73, 3, 9, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 12, 13, 
+	14, 15, 16, 17, 18, 12, 19, 20, 
+	21, 22, 23, 24, 73, 25, 26, 27, 
+	73, 76, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 75, 75, 76, 77, 75, 
+	76, 77, 75, 77, 75, 0
+};
+
+static const char _use_syllable_machine_trans_targs[] = {
+	3, 41, 3, 43, 4, 5, 25, 3, 
+	0, 2, 60, 62, 45, 46, 47, 48, 
+	49, 56, 57, 58, 61, 59, 53, 54, 
+	55, 50, 51, 52, 3, 3, 3, 3, 
+	6, 7, 24, 9, 10, 11, 12, 13, 
+	20, 21, 22, 23, 17, 18, 19, 14, 
+	15, 16, 8, 3, 3, 3, 26, 27, 
+	40, 29, 30, 31, 32, 36, 37, 38, 
+	39, 33, 34, 35, 28, 3, 3, 1, 
+	42, 3, 44, 3, 63, 64
+};
+
+static const char _use_syllable_machine_trans_actions[] = {
+	1, 2, 3, 4, 0, 0, 0, 7, 
+	0, 0, 4, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 4, 4, 0, 0, 
+	0, 0, 0, 0, 8, 9, 10, 11, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 12, 13, 14, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 15, 16, 0, 
+	2, 17, 4, 18, 0, 0
+};
+
+static const char _use_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 5, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const char _use_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 6, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const short _use_syllable_machine_eof_trans[] = {
+	0, 1, 3, 0, 29, 31, 31, 52, 
+	31, 31, 31, 31, 31, 31, 31, 31, 
+	31, 31, 31, 31, 31, 31, 31, 31, 
+	31, 53, 53, 52, 53, 53, 53, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
+	53, 70, 70, 74, 74, 74, 74, 74, 
+	74, 74, 74, 74, 74, 74, 74, 74, 
+	74, 74, 74, 74, 74, 74, 76, 76, 
+	76
+};
+
+static const int use_syllable_machine_start = 3;
+static const int use_syllable_machine_first_final = 3;
+static const int use_syllable_machine_error = 0;
+
+static const int use_syllable_machine_en_main = 3;
+
+
+#line 38 "hb-ot-shape-complex-use-machine.rl"
+
+
+
+#line 145 "hb-ot-shape-complex-use-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 388 "hb-ot-shape-complex-use-machine.hh"
+	{
+	cs = use_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 166 "hb-ot-shape-complex-use-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  
+#line 405 "hb-ot-shape-complex-use-machine.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	switch ( _use_syllable_machine_from_state_actions[cs] ) {
+	case 6:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 421 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+	_keys = _use_syllable_machine_trans_keys + (cs<<1);
+	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
+
+	_slen = _use_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].use_category()) &&
+		( info[p].use_category()) <= _keys[1] ?
+		( info[p].use_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _use_syllable_machine_trans_targs[_trans];
+
+	if ( _use_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _use_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 9:
+#line 134 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (independent_cluster); }}
+	break;
+	case 11:
+#line 136 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (consonant_cluster); }}
+	break;
+	case 14:
+#line 137 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (vowel_cluster); }}
+	break;
+	case 16:
+#line 138 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (number_joiner_terminated_cluster); }}
+	break;
+	case 7:
+#line 141 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (broken_cluster); }}
+	break;
+	case 8:
+#line 134 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (independent_cluster); }}
+	break;
+	case 12:
+#line 135 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (virama_terminated_cluster); }}
+	break;
+	case 10:
+#line 136 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (consonant_cluster); }}
+	break;
+	case 13:
+#line 137 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (vowel_cluster); }}
+	break;
+	case 15:
+#line 139 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (numeral_cluster); }}
+	break;
+	case 18:
+#line 140 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (symbol_cluster); }}
+	break;
+	case 17:
+#line 141 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 1:
+#line 139 "hb-ot-shape-complex-use-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (numeral_cluster); }}
+	break;
+	case 3:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 0:
+	{{cs = 0; goto _again;}}
+	break;
+	case 8:
+	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	break;
+	}
+	}
+	break;
+	case 4:
+#line 1 "NONE"
+	{te = p+1;}
+#line 141 "hb-ot-shape-complex-use-machine.rl"
+	{act = 8;}
+	break;
+#line 513 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+_again:
+	switch ( _use_syllable_machine_to_state_actions[cs] ) {
+	case 5:
+#line 1 "NONE"
+	{ts = 0;}
+#line 1 "NONE"
+	{act = 0;}
+	break;
+#line 524 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _use_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	_out: {}
+	}
+
+#line 175 "hb-ot-shape-complex-use-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+
+#include "hb-private.hh"
+
+%%{
+  machine use_syllable_machine;
+  alphtype unsigned char;
+  write data;
+}%%
+
+%%{
+
+# Same order as enum use_category_t.  Not sure how to avoid duplication.
+
+O	= 0; # OTHER
+
+B	= 1; # BASE
+IV	= 2; # BASE_VOWEL
+IND	= 3; # BASE_IND
+N	= 4; # BASE_NUM
+GB	= 5; # BASE_OTHER
+CGJ	= 6; # CGJ
+#F	= 7; # CONS_FINAL
+FM	= 8; # CONS_FINAL_MOD
+#M	= 9; # CONS_MED
+#CM	= 10; # CONS_MOD
+SUB	= 11; # CONS_SUB
+H	= 12; # HALANT
+
+HN	= 13; # HALANT_NUM
+ZWNJ	= 14; # Zero width non-joiner
+ZWJ	= 15; # Zero width joiner
+WJ	= 16; # Word joiner
+Rsv	= 17; # Reserved characters
+R	= 18; # REPHA
+S	= 19; # SYM
+#SM	= 20; # SYM_MOD
+VS	= 21; # VARIATION_SELECTOR
+#V	= 36; # VOWEL
+#VM	= 40; # VOWEL_MOD
+
+FAbv	= 24; # CONS_FINAL_ABOVE
+FBlw	= 25; # CONS_FINAL_BELOW
+FPst	= 26; # CONS_FINAL_POST
+MAbv	= 27; # CONS_MED_ABOVE
+MBlw	= 28; # CONS_MED_BELOW
+MPst	= 29; # CONS_MED_POST
+MPre	= 30; # CONS_MED_PRE
+CMAbv	= 31; # CONS_MOD_ABOVE
+CMBlw	= 32; # CONS_MOD_BELOW
+VAbv	= 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST
+VBlw	= 34; # VOWEL_BELOW / VOWEL_BELOW_POST
+VPst	= 35; # VOWEL_POST	UIPC = Right
+VPre	= 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST
+VMAbv	= 37; # VOWEL_MOD_ABOVE
+VMBlw	= 38; # VOWEL_MOD_BELOW
+VMPst	= 39; # VOWEL_MOD_POST
+VMPre	= 23; # VOWEL_MOD_PRE
+SMAbv	= 41; # SYM_MOD_ABOVE
+SMBlw	= 42; # SYM_MOD_BELOW
+
+
+consonant_modifiers = CMAbv* CMBlw* ((H B | SUB) VS? CMAbv? CMBlw*)*;
+medial_consonants = MPre? MAbv? MBlw? MPst?;
+dependent_vowels = VPre* VAbv* VBlw* VPst*;
+vowel_modifiers = VMPre* VMAbv* VMBlw* VMPst*;
+final_consonants = FAbv* FBlw* FPst* FM?;
+
+virama_terminated_cluster =
+	R? (B | GB | IV) VS?
+	consonant_modifiers
+	H
+;
+consonant_cluster =
+	R? (B | GB) VS?
+	consonant_modifiers
+	medial_consonants
+	dependent_vowels
+	vowel_modifiers
+	final_consonants
+;
+vowel_cluster =
+	R? (IV) VS?
+	consonant_modifiers
+	medial_consonants
+	vowel_modifiers
+	final_consonants
+;
+
+broken_cluster =
+	R?
+	consonant_modifiers
+	medial_consonants
+	dependent_vowels
+	vowel_modifiers
+	final_consonants
+;
+
+number_joiner_terminated_cluster = N VS? (HN N VS?)* H;
+numeral_cluster = N VS? (HN N VS?)*;
+symbol_cluster = S VS? SMAbv* SMBlw*;
+independent_cluster = (IND | O | Rsv | WJ) VS?;
+
+main := |*
+	independent_cluster			=> { found_syllable (independent_cluster); };
+	virama_terminated_cluster		=> { found_syllable (virama_terminated_cluster); };
+	consonant_cluster			=> { found_syllable (consonant_cluster); };
+	vowel_cluster				=> { found_syllable (vowel_cluster); };
+	number_joiner_terminated_cluster	=> { found_syllable (number_joiner_terminated_cluster); };
+	numeral_cluster				=> { found_syllable (numeral_cluster); };
+	symbol_cluster				=> { found_syllable (symbol_cluster); };
+	broken_cluster				=> { found_syllable (broken_cluster); };
+*|;
+
+
+}%%
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  %%{
+    write init;
+    getkey info[p].use_category();
+  }%%
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  %%{
+    write exec;
+  }%%
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-private.hh
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+#define USE_TABLE_ELEMENT_TYPE uint8_t
+
+/* Cateories used in the Universal Shaping Engine spec:
+ * https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum use_category_t {
+  USE_O		= 0,	/* OTHER */
+
+  USE_B		= 1,	/* BASE */
+  USE_IV	= 2,	/* BASE_VOWEL */
+  USE_IND	= 3,	/* BASE_IND */
+  USE_N		= 4,	/* BASE_NUM */
+  USE_GB	= 5,	/* BASE_OTHER */
+  USE_CGJ	= 6,	/* CGJ */
+//  USE_F		= 7,	/* CONS_FINAL */
+  USE_FM	= 8,	/* CONS_FINAL_MOD */
+//  USE_M		= 9,	/* CONS_MED */
+//  USE_CM	= 10,	/* CONS_MOD */
+  USE_SUB	= 11,	/* CONS_SUB */
+  USE_H		= 12,	/* HALANT */
+
+  USE_HN	= 13,	/* HALANT_NUM */
+  USE_ZWNJ	= 14,	/* Zero width non-joiner */
+  USE_ZWJ	= 15,	/* Zero width joiner */
+  USE_WJ	= 16,	/* Word joiner */
+  USE_Rsv	= 17,	/* Reserved characters */
+  USE_R		= 18,	/* REPHA */
+  USE_S		= 19,	/* SYM */
+//  USE_SM	= 20,	/* SYM_MOD */
+  USE_VS	= 21,	/* VARIATION_SELECTOR */
+//  USE_V	= 36,	/* VOWEL */
+//  USE_VM	= 40,	/* VOWEL_MOD */
+
+  USE_FAbv	= 24,	/* CONS_FINAL_ABOVE */
+  USE_FBlw	= 25,	/* CONS_FINAL_BELOW */
+  USE_FPst	= 26,	/* CONS_FINAL_POST */
+  USE_MAbv	= 27,	/* CONS_MED_ABOVE */
+  USE_MBlw	= 28,	/* CONS_MED_BELOW */
+  USE_MPst	= 29,	/* CONS_MED_POST */
+  USE_MPre	= 30,	/* CONS_MED_PRE */
+  USE_CMAbv	= 31,	/* CONS_MOD_ABOVE */
+  USE_CMBlw	= 32,	/* CONS_MOD_BELOW */
+  USE_VAbv	= 33,	/* VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST */
+  USE_VBlw	= 34,	/* VOWEL_BELOW / VOWEL_BELOW_POST */
+  USE_VPst	= 35,	/* VOWEL_POST	UIPC = Right */
+  USE_VPre	= 22,	/* VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST */
+  USE_VMAbv	= 37,	/* VOWEL_MOD_ABOVE */
+  USE_VMBlw	= 38,	/* VOWEL_MOD_BELOW */
+  USE_VMPst	= 39,	/* VOWEL_MOD_POST */
+  USE_VMPre	= 23,	/* VOWEL_MOD_PRE */
+  USE_SMAbv	= 41,	/* SYM_MOD_ABOVE */
+  USE_SMBlw	= 42	/* SYM_MOD_BELOW */
+};
+
+HB_INTERNAL USE_TABLE_ELEMENT_TYPE
+hb_use_get_categories (hb_codepoint_t u);
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
@@ -0,0 +1,696 @@
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
+ *
+ * on files with these headers:
+ *
+ * # IndicSyllabicCategory-8.0.0.txt
+ * # Date: 2015-05-12, 10:00:00 GMT [RP, KW, LI]
+ * # IndicPositionalCategory-8.0.0.txt
+ * # Date: 2015-05-12, 10:00:00 GMT [RP, KW, LI]
+ * # Blocks-8.0.0.txt
+ * # Date: 2014-11-10, 23:04:00 GMT [KW]
+ * UnicodeData.txt does not have a header.
+ */
+
+#include "hb-ot-shape-complex-use-private.hh"
+
+#define B	USE_B	/* BASE */
+#define CGJ	USE_CGJ	/* CGJ */
+#define FM	USE_FM	/* CONS_FINAL_MOD */
+#define GB	USE_GB	/* BASE_OTHER */
+#define H	USE_H	/* HALANT */
+#define HN	USE_HN	/* HALANT_NUM */
+#define IND	USE_IND	/* BASE_IND */
+#define IV	USE_IV	/* BASE_VOWEL */
+#define N	USE_N	/* BASE_NUM */
+#define O	USE_O	/* OTHER */
+#define R	USE_R	/* REPHA */
+#define Rsv	USE_Rsv	/* Reserved */
+#define S	USE_S	/* SYM */
+#define SUB	USE_SUB	/* CONS_SUB */
+#define VS	USE_VS	/* VARIATION_SELECTOR */
+#define WJ	USE_WJ	/* Word_Joiner */
+#define ZWJ	USE_ZWJ	/* ZWJ */
+#define ZWNJ	USE_ZWNJ	/* ZWNJ */
+#define CMBlw	USE_CMBlw
+#define CMAbv	USE_CMAbv
+#define FBlw	USE_FBlw
+#define FPst	USE_FPst
+#define FAbv	USE_FAbv
+#define MPre	USE_MPre
+#define MBlw	USE_MBlw
+#define MPst	USE_MPst
+#define MAbv	USE_MAbv
+#define SMBlw	USE_SMBlw
+#define SMAbv	USE_SMAbv
+#define VPre	USE_VPre
+#define VBlw	USE_VBlw
+#define VPst	USE_VPst
+#define VAbv	USE_VAbv
+#define VMPre	USE_VMPre
+#define VMBlw	USE_VMBlw
+#define VMPst	USE_VMPst
+#define VMAbv	USE_VMAbv
+
+static const USE_TABLE_ELEMENT_TYPE use_table[] = {
+
+
+#define use_offset_0x0028u 0
+
+
+  /* Basic Latin */
+                                                                         O,     O,     O,     O,     O,    GB,     O,     O,
+  /* 0030 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x00a0u 24
+
+
+  /* Latin-1 Supplement */
+
+  /* 00A0 */    GB,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 00B0 */     O,     O,    FM,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 00C0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 00D0 */     O,     O,     O,     O,     O,     O,     O,    GB,
+
+#define use_offset_0x0900u 80
+
+
+  /* Devanagari */
+
+  /* 0900 */ VMAbv, VMAbv, VMAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 0910 */    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0920 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0930 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VPst, CMBlw,     B,  VPst,  VPre,
+  /* 0940 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,  VPst,  VPst,  VPst,  VPst,     H,  VPre,  VPst,
+  /* 0950 */     O, VMAbv, VMBlw,     O,     O,  VAbv,  VBlw,  VBlw,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0960 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0970 */     O,     O,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,
+
+  /* Bengali */
+
+  /* 0980 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,    IV,
+  /* 0990 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 09A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 09B0 */     B,     O,     B,     O,     O,     O,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPre,
+  /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,   IND,     O,
+  /* 09D0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     B,     B,     O,     B,
+  /* 09E0 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 09F0 */     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Gurmukhi */
+
+  /* 0A00 */     O, VMAbv, VMAbv, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,     O,     O,    IV,
+  /* 0A10 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0A30 */     B,     O,     B,     B,     O,     B,     B,     O,     B,     B,     O,     O, CMBlw,     O,  VPst,  VPre,
+  /* 0A40 */  VPst,  VBlw,  VBlw,     O,     O,     O,     O,  VAbv,  VAbv,     O,     O,  VAbv,  VAbv,     H,     O,     O,
+  /* 0A50 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     O,     B,     O,
+  /* 0A60 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0A70 */ VMAbv, CMAbv,    GB,    GB,     O,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Gujarati */
+
+  /* 0A80 */     O, VMAbv, VMAbv, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,
+  /* 0A90 */    IV,    IV,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0AA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0AB0 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPre,
+  /* 0AC0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,     O,  VAbv,  VAbv,  VAbv,     O,  VPst,  VPst,     H,     O,     O,
+  /* 0AD0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 0AE0 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0AF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Oriya */
+
+  /* 0B00 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,    IV,
+  /* 0B10 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0B30 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VAbv,
+  /* 0B40 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
+  /* 0B50 */     O,     O,     O,     O,     O,     O,  VAbv,  VAbv,     O,     O,     O,     O,     B,     B,     O,     B,
+  /* 0B60 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0B70 */     O,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Tamil */
+
+  /* 0B80 */     O,     O, VMAbv,   IND,     O,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,     O,    IV,    IV,
+  /* 0B90 */    IV,     O,    IV,    IV,    IV,     B,     O,     O,     O,     B,     B,     O,     B,     O,     B,     B,
+  /* 0BA0 */     O,     O,     O,     B,     B,     O,     O,     O,     B,     B,     B,     O,     O,     O,     B,     B,
+  /* 0BB0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,  VPst,  VPst,
+  /* 0BC0 */  VAbv,  VPst,  VPst,     O,     O,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     O,     O,
+  /* 0BD0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 0BE0 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0BF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Telugu */
+
+  /* 0C00 */ VMAbv, VMPst, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,    IV,
+  /* 0C10 */    IV,     O,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0C20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0C30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,  VAbv,  VAbv,
+  /* 0C40 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,  VAbv,  VAbv,  VAbv,     O,  VAbv,  VAbv,  VAbv,     H,     O,     O,
+  /* 0C50 */     O,     O,     O,     O,     O,  VAbv,  VBlw,     O,     B,     B,     B,     O,     O,     O,     O,     O,
+  /* 0C60 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0C70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Kannada */
+
+  /* 0C80 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,    IV,
+  /* 0C90 */    IV,     O,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0CA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0CB0 */     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VAbv,
+  /* 0CC0 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,  VAbv,  VAbv,  VAbv,     O,  VAbv,  VAbv,  VAbv,     H,     O,     O,
+  /* 0CD0 */     O,     O,     O,     O,     O,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     B,     O,
+  /* 0CE0 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0CF0 */     O,     R,     R,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Malayalam */
+
+  /* 0D00 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,    IV,
+  /* 0D10 */    IV,     O,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0D20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0D30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     B,  VPst,  VPst,
+  /* 0D40 */  VPst,  VPst,  VPst,  VBlw,  VBlw,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     R,     O,
+  /* 0D50 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,    IV,
+  /* 0D60 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0D70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,   IND,   IND,   IND,   IND,   IND,   IND,
+
+  /* Sinhala */
+
+  /* 0D80 */     O,     O, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 0D90 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,     O,     B,     B,     B,     B,     B,     B,
+  /* 0DA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0DB0 */     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     O,     O,
+  /* 0DC0 */     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     H,     O,     O,     O,     O,  VPst,
+  /* 0DD0 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,     O,  VBlw,     O,  VPst,  VPre,  VPre,  VPre,  VPre,  VPre,  VPre,  VPst,
+  /* 0DE0 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0DF0 */     O,     O,  VPst,  VPst,     O,     O,     O,     O,
+
+#define use_offset_0x1000u 1352
+
+
+  /* Myanmar */
+
+  /* 1000 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1010 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1020 */     B,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,  VPst,  VPst,  VAbv,  VAbv,  VBlw,
+  /* 1030 */  VBlw,  VPre,  VAbv,  VAbv,  VAbv,  VAbv, VMAbv, VMBlw, VMPst,     H,  VAbv,  MPst,  MPre,  MBlw,  MBlw,     B,
+  /* 1040 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,    GB,     O,
+  /* 1050 */     B,     B,    IV,    IV,    IV,    IV,  VPst,  VPst,  VBlw,  VBlw,     B,     B,     B,     B,  MBlw,  MBlw,
+  /* 1060 */  MBlw,     B,  VPst, VMPst, VMPst,     B,     B,  VPst,  VPst, VMPst, VMPst, VMPst, VMPst, VMPst,     B,     B,
+  /* 1070 */     B,  VAbv,  VAbv,  VAbv,  VAbv,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1080 */     B,     B,  MBlw,  VPst,  VPre,  VAbv,  VAbv, VMPst, VMPst, VMPst, VMPst, VMPst, VMPst, VMBlw,     B, VMPst,
+  /* 1090 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMPst, VMPst,  VPst,  VAbv,     O,     O,
+
+#define use_offset_0x1700u 1512
+
+
+  /* Tagalog */
+
+  /* 1700 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,
+  /* 1710 */     B,     B,  VAbv,  VBlw,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Hanunoo */
+
+  /* 1720 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1730 */     B,     B,  VAbv,  VBlw,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Buhid */
+
+  /* 1740 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1750 */     B,     B,  VAbv,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Tagbanwa */
+
+  /* 1760 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,
+  /* 1770 */     B,     O,  VAbv,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Khmer */
+
+  /* 1780 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1790 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 17A0 */     B,     B,     B,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 17B0 */    IV,    IV,    IV,    IV,     O,     O,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VPre,  VPre,
+  /* 17C0 */  VPre,  VPre,  VPre,  VPre,  VPre,  VPre, VMAbv, VMPst,  VPst, VMAbv, VMAbv,    FM,  FAbv, CMAbv,    FM,    FM,
+  /* 17D0 */    FM,  VAbv,     H,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     B,  VAbv,     O,     O,
+  /* 17E0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x1900u 1752
+
+
+  /* Limbu */
+
+  /* 1900 */    GB,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1910 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,
+  /* 1920 */  VAbv,  VAbv,  VBlw,  VPst,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,   SUB,   SUB,   SUB,     O,     O,     O,     O,
+  /* 1930 */  FPst,  FPst, VMBlw,  FPst,  FPst,  FPst,  FPst,  FPst,  FPst,  FBlw,  VAbv,    FM,     O,     O,     O,     O,
+  /* 1940 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+
+  /* Tai Le */
+
+  /* 1950 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1960 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,
+  /* 1970 */     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* New Tai Lue */
+
+  /* 1980 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1990 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 19A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+  /* 19B0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 19C0 */     B,     B,     B,     B,     B,     B,     B,     B, VMPst, VMPst,     O,     O,     O,     O,     O,     O,
+  /* 19D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 19E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 19F0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Buginese */
+
+  /* 1A00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1A10 */     B,     B,     B,     B,     B,     B,     B,  VAbv,  VBlw,  VPre,  VPst,  VAbv,     O,     O,     O,     O,
+
+  /* Tai Tham */
+
+  /* 1A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1A30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,    IV,    IV,    IV,
+  /* 1A50 */    IV,    IV,    IV,     B,     B,  MPre,  MBlw,  FPst,  FAbv,  FAbv,  FAbv,  FBlw,  FBlw,  FBlw,  FBlw,     O,
+  /* 1A60 */     H,  VPst,  VAbv,  VPst,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VAbv,  VBlw,  VPst,  VPre,  VPre,
+  /* 1A70 */  VPre,  VPre,  VPre,  VAbv,  VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,    FM,    FM,    FM,     O,     O,    FM,
+  /* 1A80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 1A90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x1b00u 2168
+
+
+  /* Balinese */
+
+  /* 1B00 */ VMAbv, VMAbv, VMAbv,  FAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 1B10 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1B30 */     B,     B,     B,     B, CMAbv,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPre,  VPre,
+  /* 1B40 */  VPre,  VPre,  VAbv,  VAbv,     H,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+  /* 1B50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 1B60 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
+  /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Sundanese */
+
+  /* 1B80 */ VMAbv,  FAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,
+  /* 1B90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1BA0 */     B,   SUB,   SUB,   SUB,  VAbv,  VBlw,  VPre,  VPst,  VAbv,  VAbv,  VPst,     H,   SUB,   SUB,     B,     B,
+  /* 1BB0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+
+  /* Batak */
+
+  /* 1BC0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1BD0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1BE0 */     B,     B,     B,     B,    IV,    IV, CMAbv,  VPst,  VAbv,  VAbv,  VPst,  VPst,  VPst,  VAbv,  VPst,  VAbv,
+  /* 1BF0 */  FAbv,  FAbv,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Lepcha */
+
+  /* 1C00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1C10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1C20 */     B,     B,     B,     B,   SUB,   SUB,  VPst,  VPre,  VPre,  VPre,  VPst,  VPst,  VBlw,  FAbv,  FAbv,  FAbv,
+  /* 1C30 */  FAbv,  FAbv,  FAbv,  FAbv, VMPre, VMPre,    FM, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 1C40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,     B,     B,
+
+#define use_offset_0x1cd0u 2504
+
+
+  /* Vedic Extensions */
+
+  /* 1CD0 */ VMAbv, VMAbv, VMAbv,     O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw,
+  /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw,     O,     O,     O,     O, VMBlw,     O,     O,
+  /* 1CF0 */     O,     O, VMPst, VMPst, VMAbv,     O,     O,     O, VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x2008u 2552
+
+
+  /* General Punctuation */
+                                                                         O,     O,     O,     O,  ZWNJ,   ZWJ,     O,     O,
+  /* 2010 */    GB,    GB,    GB,    GB,    GB,     O,     O,     O,
+
+#define use_offset_0x2060u 2568
+
+  /* 2060 */    WJ,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Superscripts and Subscripts */
+
+  /* 2070 */     O,     O,     O,     O,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 2080 */     O,     O,    FM,    FM,    FM,     O,     O,     O,
+
+#define use_offset_0xa800u 2608
+
+
+  /* Syloti Nagri */
+
+  /* A800 */    IV,    IV,     O,    IV,    IV,    IV,  VAbv,     B,     B,     B,     B, VMAbv,     B,     B,     B,     B,
+  /* A810 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A820 */     B,     B,     B,  VPst,  VPst,  VBlw,  VAbv,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A830 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Phags-pa */
+
+  /* A840 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A850 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A860 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A870 */     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Saurashtra */
+
+  /* A880 */ VMPst, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* A890 */    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A8A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A8B0 */     B,     B,     B,     B,  FPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,
+  /* A8C0 */  VPst,  VPst,  VPst,  VPst,     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A8D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Devanagari Extended */
+
+  /* A8E0 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,
+  /* A8F0 */ VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Kayah Li */
+
+  /* A900 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A910 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A920 */     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VAbv, VMBlw, VMBlw, VMBlw,     O,     O,
+
+  /* Rejang */
+
+  /* A930 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A940 */     B,     B,     B,     B,     B,     B,     B,  VBlw,  VBlw,  VBlw,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  FAbv,
+  /* A950 */  FAbv,  FAbv,  FPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A960 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A970 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Javanese */
+
+  /* A980 */ VMAbv, VMAbv,  FAbv, VMPst,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,    IV,    IV,    IV,     B,
+  /* A990 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A9A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A9B0 */     B,     B,     B, CMAbv,  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VPre,  VAbv,   SUB,  MPst,  MPst,
+  /* A9C0 */     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A9D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Myanmar Extended-B */
+
+  /* A9E0 */     B,     B,     B,     B,     B,  VAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A9F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,
+
+  /* Cham */
+
+  /* AA00 */    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VAbv,  VPre,
+  /* AA30 */  VPre,  VAbv,  VBlw,  MPst,  MPre,  MBlw,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* AA40 */     B,     B,     B,  FAbv,     B,     B,     B,     B,     B,     B,     B,     B,  FAbv,  FPst,     O,     O,
+  /* AA50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Myanmar Extended-A */
+
+  /* AA60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA70 */     O,     B,     B,     B,     O,     O,     O,     O,     O,     O,     B, VMPst, VMAbv, VMPst,     B,     B,
+
+  /* Tai Viet */
+
+  /* AA80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AAA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AAB0 */  VAbv,     B,  VAbv,  VAbv,  VBlw,     B,     B,  VAbv,  VAbv,     B,     B,     B,     B,     B,  VAbv, VMAbv,
+  /* AAC0 */     B, VMAbv,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* AAD0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Meetei Mayek Extensions */
+
+  /* AAE0 */    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPre,  VBlw,  VAbv,  VPre,  VPst,
+  /* AAF0 */     O,     O,     O,     O,     O, VMPst,     H,     O,
+
+#define use_offset_0xabc0u 3368
+
+
+  /* Meetei Mayek */
+
+  /* ABC0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,    IV,    IV,
+  /* ABD0 */     B,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* ABE0 */     B,     B,     B,  VPst,  VPst,  VAbv,  VPst,  VPst,  VBlw,  VPst,  VPst,     O, VMPst,  VBlw,     O,     O,
+  /* ABF0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0xfe00u 3432
+
+
+  /* Variation Selectors */
+
+  /* FE00 */    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,
+
+#define use_offset_0x10a00u 3448
+
+
+  /* Kharoshthi */
+
+  /* 10A00 */     B,  VBlw,  VBlw,  VBlw,     O,  VAbv,  VBlw,     O,     O,     O,     O,     O,  VBlw,  VBlw, VMBlw, VMAbv,
+  /* 10A10 */     B,     B,     B,     B,     O,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,
+  /* 10A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 10A30 */     B,     B,     B,     B,     O,     O,     O,     O, CMAbv, CMBlw, CMBlw,     O,     O,     O,     O,     H,
+  /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,
+
+#define use_offset_0x11000u 3520
+
+
+  /* Brahmi */
+
+  /* 11000 */ VMPst, VMAbv, VMPst,     R,     R,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 11010 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11020 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11030 */     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,
+  /* 11040 */  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11050 */     O,     O,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,
+  /* 11060 */     N,     N,     N,     N,     N,     N,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11070 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Kaithi */
+
+  /* 11080 */ VMAbv, VMAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,
+  /* 11090 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 110A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 110B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VPst,  VPst,     H, CMBlw,     O,     O,     O,     O,     O,
+
+#define use_offset_0x11100u 3712
+
+
+  /* Chakma */
+
+  /* 11100 */ VMAbv, VMAbv, VMAbv,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11110 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11120 */     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VAbv,  VAbv,
+  /* 11130 */  VAbv,  VBlw,  VBlw,     H,  VAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11140 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Mahajani */
+
+  /* 11150 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11160 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11170 */     B,     B,     B, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Sharada */
+
+  /* 11180 */ VMAbv, VMAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 11190 */    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 111A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 111B0 */     B,     B,     B,  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,
+  /* 111C0 */     H,     B,     R,     R,     O,     O,     O,     O,     O,     O, CMBlw,  VAbv,  VBlw,     O,     O,     O,
+  /* 111D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Sinhala Archaic Numbers */
+
+  /* 111E0 */     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 111F0 */     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Khojki */
+
+  /* 11200 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11210 */     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11220 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,  VPst,  VPst,  VBlw,
+  /* 11230 */  VAbv,  VAbv,  VAbv,  VAbv, VMAbv,     H, CMAbv, CMAbv,
+
+#define use_offset_0x11280u 4024
+
+
+  /* Multani */
+
+  /* 11280 */    IV,    IV,    IV,    IV,     B,     B,     B,     O,     B,     O,     B,     B,     B,     B,     O,     B,
+  /* 11290 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,
+  /* 112A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Khudawadi */
+
+  /* 112B0 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,
+  /* 112C0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 112D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv,
+  /* 112E0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv, CMBlw,  VBlw,     O,     O,     O,     O,     O,
+  /* 112F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Grantha */
+
+  /* 11300 */ VMAbv, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,    IV,
+  /* 11310 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11320 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 11330 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPst,
+  /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
+  /* 11350 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11360 */    IV,    IV,  VPst,  VPst,     O,     O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
+  /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
+
+#define use_offset_0x11480u 4272
+
+
+  /* Tirhuta */
+
+  /* 11480 */     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,
+  /* 11490 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 114A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 114B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VPre,  VAbv,  VPre,  VPre,  VPst,  VPre, VMAbv,
+  /* 114C0 */ VMAbv, VMPst,     H, CMBlw,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 114D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x11580u 4368
+
+
+  /* Siddham */
+
+  /* 11580 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,
+  /* 11590 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 115A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,
+  /* 115B0 */  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,  VPre,  VPre, VMAbv, VMAbv, VMPst,     H,
+  /* 115C0 */ CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 115D0 */     O,     O,     O,     O,     O,     O,     O,     O,    IV,    IV,    IV,    IV,  VBlw,  VBlw,     O,     O,
+  /* 115E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 115F0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Modi */
+
+  /* 11600 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,
+  /* 11610 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11620 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11630 */  VPst,  VPst,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPst,  VPst, VMAbv, VMPst,     H,
+  /* 11640 */  VAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11650 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 11660 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11670 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Takri */
+
+  /* 11680 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,
+  /* 11690 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 116A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv, VMPst,  VAbv,  VPre,  VPst,
+  /* 116B0 */  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,     H, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 116C0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 116D0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 116E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 116F0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Ahom */
+
+  /* 11700 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11710 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,  MBlw,  MPre,  MAbv,
+  /* 11720 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VBlw,  VAbv,  VAbv,  VAbv,     O,     O,     O,     O,
+  /* 11730 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+
+}; /* Table items: 4816; occupancy: 72% */
+
+USE_TABLE_ELEMENT_TYPE
+hb_use_get_categories (hb_codepoint_t u)
+{
+  switch (u >> 12)
+  {
+    case 0x0u:
+      if (hb_in_range (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
+      if (hb_in_range (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
+      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
+      if (unlikely (u == 0x034Fu)) return CGJ;
+      break;
+
+    case 0x1u:
+      if (hb_in_range (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
+      if (hb_in_range (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
+      if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
+      if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
+      if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
+      break;
+
+    case 0x2u:
+      if (hb_in_range (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
+      if (hb_in_range (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
+      if (unlikely (u == 0x25CCu)) return GB;
+      break;
+
+    case 0xAu:
+      if (hb_in_range (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
+      if (hb_in_range (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
+      break;
+
+    case 0xFu:
+      if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
+      break;
+
+    case 0x10u:
+      if (hb_in_range (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
+      break;
+
+    case 0x11u:
+      if (hb_in_range (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
+      if (hb_in_range (u, 0x11100u, 0x11237u)) return use_table[u - 0x11100u + use_offset_0x11100u];
+      if (hb_in_range (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
+      if (hb_in_range (u, 0x11480u, 0x114DFu)) return use_table[u - 0x11480u + use_offset_0x11480u];
+      if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
+      if (unlikely (u == 0x1107Fu)) return HN;
+      break;
+
+    default:
+      break;
+  }
+  return USE_O;
+}
+
+#undef B
+#undef CGJ
+#undef FM
+#undef GB
+#undef H
+#undef HN
+#undef IND
+#undef IV
+#undef N
+#undef O
+#undef R
+#undef Rsv
+#undef S
+#undef SUB
+#undef VS
+#undef WJ
+#undef ZWJ
+#undef ZWNJ
+#undef CMBlw
+#undef CMAbv
+#undef FBlw
+#undef FPst
+#undef FAbv
+#undef MPre
+#undef MBlw
+#undef MPst
+#undef MAbv
+#undef SMBlw
+#undef SMAbv
+#undef VPre
+#undef VBlw
+#undef VPst
+#undef VAbv
+#undef VMPre
+#undef VMBlw
+#undef VMPst
+#undef VMAbv
+
+/* == End of generated table == */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -0,0 +1,593 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-complex-use-private.hh"
+#include "hb-ot-shape-complex-arabic-private.hh"
+
+/* buffer var allocations */
+#define use_category() complex_var_u8_0()
+
+
+/*
+ * Universal Shaping Engine.
+ * https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm
+ */
+
+static const hb_tag_t
+basic_features[] =
+{
+  /*
+   * Basic features.
+   * These features are applied all at once, before reordering.
+   */
+  HB_TAG('r','k','r','f'),
+  HB_TAG('a','b','v','f'),
+  HB_TAG('b','l','w','f'),
+  HB_TAG('h','a','l','f'),
+  HB_TAG('p','s','t','f'),
+  HB_TAG('v','a','t','u'),
+  HB_TAG('c','j','c','t'),
+};
+static const hb_tag_t
+arabic_features[] =
+{
+  HB_TAG('i','s','o','l'),
+  HB_TAG('i','n','i','t'),
+  HB_TAG('m','e','d','i'),
+  HB_TAG('f','i','n','a'),
+  /* The spec doesn't specify these but we apply anyway, since our Arabic shaper
+   * does.  These are only used in Syriac spec. */
+  HB_TAG('m','e','d','2'),
+  HB_TAG('f','i','n','2'),
+  HB_TAG('f','i','n','3'),
+};
+/* Same order as arabic_features.  Don't need Syriac stuff.*/
+enum joining_form_t {
+  ISOL,
+  INIT,
+  MEDI,
+  FINA,
+  _NONE
+};
+static const hb_tag_t
+other_features[] =
+{
+  /*
+   * Other features.
+   * These features are applied all at once, after reordering.
+   */
+  HB_TAG('a','b','v','s'),
+  HB_TAG('b','l','w','s'),
+  HB_TAG('h','a','l','n'),
+  HB_TAG('p','r','e','s'),
+  HB_TAG('p','s','t','s'),
+  /* Positioning features, though we don't care about the types. */
+  HB_TAG('d','i','s','t'),
+  HB_TAG('a','b','v','m'),
+  HB_TAG('b','l','w','m'),
+};
+
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
+static void
+clear_substitution_flags (const hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer);
+static void
+record_rphf (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer);
+static void
+record_pref (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer);
+static void
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer);
+
+static void
+collect_features_use (hb_ot_shape_planner_t *plan)
+{
+  hb_ot_map_builder_t *map = &plan->map;
+
+  /* Do this before any lookups have been applied. */
+  map->add_gsub_pause (setup_syllables);
+
+  /* "Default glyph pre-processing group" */
+  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
+  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
+  map->add_global_bool_feature (HB_TAG('n','u','k','t'));
+  map->add_global_bool_feature (HB_TAG('a','k','h','n'));
+
+  /* "Reordering group" */
+  map->add_gsub_pause (clear_substitution_flags);
+  map->add_feature (HB_TAG('r','p','h','f'), 1, F_MANUAL_ZWJ);
+  map->add_gsub_pause (record_rphf);
+  map->add_gsub_pause (clear_substitution_flags);
+  map->add_feature (HB_TAG('p','r','e','f'), 1, F_GLOBAL | F_MANUAL_ZWJ);
+  map->add_gsub_pause (record_pref);
+
+  /* "Orthographic unit shaping group" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
+    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+
+  map->add_gsub_pause (reorder);
+
+  /* "Topographical features" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
+    map->add_feature (arabic_features[i], 1, F_NONE);
+  map->add_gsub_pause (NULL);
+
+  /* "Standard typographic presentation" and "Positional feature application" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
+    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+}
+
+struct use_shape_plan_t
+{
+  ASSERT_POD ();
+
+  hb_mask_t rphf_mask;
+
+  arabic_shape_plan_t *arabic_plan;
+};
+
+static bool
+has_arabic_joining (hb_script_t script)
+{
+  /* List of scripts that have data in arabic-table. */
+  switch ((int) script)
+  {
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_ARABIC:
+
+    /* Unicode-3.0 additions */
+    case HB_SCRIPT_MONGOLIAN:
+    case HB_SCRIPT_SYRIAC:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_NKO:
+    case HB_SCRIPT_PHAGS_PA:
+
+    /* Unicode-6.0 additions */
+    case HB_SCRIPT_MANDAIC:
+
+    /* Unicode-7.0 additions */
+    case HB_SCRIPT_MANICHAEAN:
+    case HB_SCRIPT_PSALTER_PAHLAVI:
+
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+static void *
+data_create_use (const hb_ot_shape_plan_t *plan)
+{
+  use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t));
+  if (unlikely (!use_plan))
+    return NULL;
+
+  use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f'));
+
+  if (has_arabic_joining (plan->props.script))
+  {
+    use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan);
+    if (unlikely (!use_plan->arabic_plan))
+    {
+      free (use_plan);
+      return NULL;
+    }
+  }
+
+  return use_plan;
+}
+
+static void
+data_destroy_use (void *data)
+{
+  use_shape_plan_t *use_plan = (use_shape_plan_t *) data;
+
+  if (use_plan->arabic_plan)
+    data_destroy_arabic (use_plan->arabic_plan);
+
+  free (data);
+}
+
+enum syllable_type_t {
+  independent_cluster,
+  virama_terminated_cluster,
+  consonant_cluster,
+  vowel_cluster,
+  number_joiner_terminated_cluster,
+  numeral_cluster,
+  symbol_cluster,
+  broken_cluster,
+};
+
+#include "hb-ot-shape-complex-use-machine.hh"
+
+
+static inline void
+set_use_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  info.use_category() = hb_use_get_categories (u);
+}
+
+
+static void
+setup_masks_use (const hb_ot_shape_plan_t *plan,
+		 hb_buffer_t              *buffer,
+		 hb_font_t                *font HB_UNUSED)
+{
+  const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
+
+  /* Do this before allocating use_category(). */
+  if (use_plan->arabic_plan)
+  {
+    setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script);
+  }
+
+  HB_BUFFER_ALLOCATE_VAR (buffer, use_category);
+
+  /* We cannot setup masks here.  We save information about characters
+   * and setup masks later on in a pause-callback. */
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].use_category() = hb_use_get_categories (info[i].codepoint);
+}
+
+static void
+setup_rphf_mask (const hb_ot_shape_plan_t *plan,
+		 hb_buffer_t *buffer)
+{
+  const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
+
+  hb_mask_t mask = use_plan->rphf_mask;
+  if (!mask) return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+  {
+    unsigned int limit = info[start].use_category() == USE_R ? 1 : MIN (3u, end - start);
+    for (unsigned int i = start; i < start + limit; i++)
+      info[i].mask |= mask;
+  }
+}
+
+static void
+setup_topographical_masks (const hb_ot_shape_plan_t *plan,
+			   hb_buffer_t *buffer)
+{
+
+  ASSERT_STATIC (INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4);
+  hb_mask_t masks[4], all_masks = 0;
+  for (unsigned int i = 0; i < 4; i++)
+  {
+    masks[i] = plan->map.get_1_mask (arabic_features[i]);
+    if (masks[i] == plan->map.get_global_mask ())
+      masks[i] = 0;
+    all_masks |= masks[i];
+  }
+  if (!all_masks)
+    return;
+  hb_mask_t other_masks = ~all_masks;
+
+  unsigned int last_start = 0;
+  joining_form_t last_form = _NONE;
+  hb_glyph_info_t *info = buffer->info;
+  foreach_syllable (buffer, start, end)
+  {
+    syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F);
+    switch (syllable_type)
+    {
+      case independent_cluster:
+      case symbol_cluster:
+	/* These don't join.  Nothing to do. */
+	last_form = _NONE;
+	break;
+
+      case virama_terminated_cluster:
+      case consonant_cluster:
+      case vowel_cluster:
+      case number_joiner_terminated_cluster:
+      case numeral_cluster:
+      case broken_cluster:
+
+	bool join = last_form == FINA || last_form == ISOL;
+
+	if (join)
+	{
+	  /* Fixup previous syllable's form. */
+	  last_form = last_form == FINA ? MEDI : INIT;
+	  for (unsigned int i = last_start; i < start; i++)
+	    info[i].mask = (info[i].mask & other_masks) | masks[last_form];
+	}
+
+	/* Form for this syllable. */
+	last_form = join ? FINA : ISOL;
+	for (unsigned int i = start; i < end; i++)
+	  info[i].mask = (info[i].mask & other_masks) | masks[last_form];
+
+	break;
+    }
+
+    last_start = start;
+  }
+}
+
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  find_syllables (buffer);
+  setup_rphf_mask (plan, buffer);
+  setup_topographical_masks (plan, buffer);
+}
+
+static void
+clear_substitution_flags (const hb_ot_shape_plan_t *plan,
+			  hb_font_t *font HB_UNUSED,
+			  hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    _hb_glyph_info_clear_substituted_and_ligated_and_multiplied (&info[i]);
+}
+
+static void
+record_rphf (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer)
+{
+  const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
+
+  hb_mask_t mask = use_plan->rphf_mask;
+  if (!mask) return;
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+  {
+    /* Mark a substituted repha as USE_R. */
+    for (unsigned int i = start; i < end && (info[i].mask & mask); i++)
+      if (_hb_glyph_info_substituted (&info[i]))
+      {
+	info[i].use_category() = USE_R;
+	break;
+      }
+  }
+}
+
+static void
+record_pref (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+  {
+    /* Mark a substituted pref as VPre, as they behave the same way. */
+    for (unsigned int i = start; i < end; i++)
+      if (_hb_glyph_info_substituted (&info[i]))
+      {
+	info[i].use_category() = USE_VPre;
+	break;
+      }
+  }
+}
+
+static void
+reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end)
+{
+  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  /* Only a few syllable types need reordering. */
+  if (unlikely (!(FLAG_SAFE (syllable_type) &
+		  (FLAG (virama_terminated_cluster) |
+		   FLAG (consonant_cluster) |
+		   FLAG (vowel_cluster) |
+		   FLAG (broken_cluster) |
+		   0))))
+    return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+#define HALANT_FLAGS FLAG(USE_H)
+#define BASE_FLAGS (FLAG (USE_B) | FLAG (USE_GB) | FLAG (USE_IV))
+
+  /* Move things forward. */
+  if (info[start].use_category() == USE_R && end - start > 1)
+  {
+    /* Got a repha.  Reorder it to after first base, before first halant. */
+    for (unsigned int i = start + 1; i < end; i++)
+      if (FLAG_UNSAFE (info[i].use_category()) & (HALANT_FLAGS | BASE_FLAGS))
+      {
+	/* If we hit a halant, move before it; otherwise it's a base: move to it's
+	 * place, and shift things in between backward. */
+
+	if (info[i].use_category() == USE_H)
+	  i--;
+
+	hb_glyph_info_t t = info[start];
+	memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0]));
+	info[i] = t;
+	buffer->merge_clusters (start, i + 1);
+
+	break;
+      }
+  }
+
+  /* Move things back. */
+  unsigned int j = end;
+  for (unsigned int i = start; i < end; i++)
+  {
+    uint32_t flag = FLAG_UNSAFE (info[i].use_category());
+    if (flag & (HALANT_FLAGS | BASE_FLAGS))
+    {
+      /* If we hit a halant, move before it; otherwise it's a base: move to it's
+       * place, and shift things in between backward. */
+      if (info[i].use_category() == USE_H)
+	j = i + 1;
+      else
+	j = i;
+    }
+    else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) &&
+	     /* Only move the first component of a MultipleSubst. */
+	     0 == _hb_glyph_info_get_lig_comp (&info[i]) &&
+	     j < i)
+    {
+      hb_glyph_info_t t = info[i];
+      memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0]));
+      info[j] = t;
+      buffer->merge_clusters (j, i + 1);
+    }
+  }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  /* Note: This loop is extra overhead, but should not be measurable. */
+  bool has_broken_syllables = false;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    {
+      has_broken_syllables = true;
+      break;
+    }
+  if (likely (!has_broken_syllables))
+    return;
+
+
+  hb_codepoint_t dottedcircle_glyph;
+  if (!font->get_glyph (0x25CCu, 0, &dottedcircle_glyph))
+    return;
+
+  hb_glyph_info_t dottedcircle = {0};
+  if (!font->get_glyph (0x25CCu, 0, &dottedcircle.codepoint))
+    return;
+  dottedcircle.use_category() = hb_use_get_categories (0x25CC);
+
+  buffer->clear_output ();
+
+  buffer->idx = 0;
+
+  unsigned int last_syllable = 0;
+  while (buffer->idx < buffer->len)
+  {
+    unsigned int syllable = buffer->cur().syllable();
+    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    {
+      last_syllable = syllable;
+
+      hb_glyph_info_t info = dottedcircle;
+      info.cluster = buffer->cur().cluster;
+      info.mask = buffer->cur().mask;
+      info.syllable() = buffer->cur().syllable();
+      /* TODO Set glyph_props? */
+
+      /* Insert dottedcircle after possible Repha. */
+      while (buffer->idx < buffer->len &&
+	     last_syllable == buffer->cur().syllable() &&
+	     buffer->cur().use_category() == USE_R)
+        buffer->next_glyph ();
+
+      buffer->output_info (info);
+    }
+    else
+      buffer->next_glyph ();
+  }
+
+  buffer->swap_buffers ();
+}
+
+static void
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer)
+{
+  insert_dotted_circles (plan, font, buffer);
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+    reorder_syllable (buffer, start, end);
+
+  /* Zero syllables now... */
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
+}
+
+static bool
+compose_use (const hb_ot_shape_normalize_context_t *c,
+	     hb_codepoint_t  a,
+	     hb_codepoint_t  b,
+	     hb_codepoint_t *ab)
+{
+  /* Avoid recomposing split matras. */
+  if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
+    return false;
+
+  return c->unicode->compose (a, b, ab);
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
+{
+  "use",
+  collect_features_use,
+  NULL, /* override_features */
+  data_create_use,
+  data_destroy_use,
+  NULL, /* preprocess_text */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+  NULL, /* decompose */
+  compose_use,
+  setup_masks_use,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  false, /* fallback_position */
+};
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -54,20 +54,16 @@ static hb_tag_t horizontal_features[] = 
   HB_TAG('c','a','l','t'),
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
   HB_TAG('l','i','g','a'),
   HB_TAG('r','c','l','t'),
 };
 
-static hb_tag_t vertical_features[] = {
-  HB_TAG('v','e','r','t'),
-};
-
 
 
 static void
 hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
 			      const hb_segment_properties_t  *props,
 			      const hb_feature_t             *user_features,
 			      unsigned int                    num_user_features)
 {
@@ -100,20 +96,23 @@ hb_ot_shape_collect_features (hb_ot_shap
     map->add_global_bool_feature (common_features[i]);
 
   if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
     for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
       map->add_feature (horizontal_features[i], 1, F_GLOBAL |
 			(horizontal_features[i] == HB_TAG('k','e','r','n') ?
 			 F_HAS_FALLBACK : F_NONE));
   else
-    for (unsigned int i = 0; i < ARRAY_LENGTH (vertical_features); i++)
-      map->add_feature (vertical_features[i], 1, F_GLOBAL |
-			(vertical_features[i] == HB_TAG('v','k','r','n') ?
-			 F_HAS_FALLBACK : F_NONE));
+  {
+    /* We really want to find a 'vert' feature if there's any in the font, no
+     * matter which script/langsys it is listed (or not) under.
+     * See various bugs referenced from:
+     * https://github.com/behdad/harfbuzz/issues/63 */
+    map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH);
+  }
 
   if (planner->shaper->override_features)
     planner->shaper->override_features (planner);
 
   for (unsigned int i = 0; i < num_user_features; i++) {
     const hb_feature_t *feature = &user_features[i];
     map->add_feature (feature->tag, feature->value,
 		      (feature->start == 0 && feature->end == (unsigned int) -1) ?
@@ -259,36 +258,67 @@ hb_insert_dotted_circle (hb_buffer_t *bu
     buffer->next_glyph ();
 
   buffer->swap_buffers ();
 }
 
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
+  if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    return;
+
+  /* Loop duplicated in hb_ensure_native_direction(). */
+  unsigned int base = 0;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
-    if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))
-      buffer->merge_clusters (i - 1, i + 1);
+  {
+    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
+    {
+      buffer->merge_clusters (base, i);
+      base = i;
+    }
+  }
+  buffer->merge_clusters (base, count);
 }
 
 static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
 
   /* TODO vertical:
    * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
    * Ogham fonts are supposed to be implemented BTT or not.  Need to research that
    * first. */
   if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) ||
       (HB_DIRECTION_IS_VERTICAL   (direction) && direction != HB_DIRECTION_TTB))
   {
-    hb_buffer_reverse_clusters (buffer);
+    /* Same loop as hb_form_clusters().
+     * Since form_clusters() merged clusters already, we don't merge. */
+    unsigned int base = 0;
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    for (unsigned int i = 1; i < count; i++)
+    {
+      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
+      {
+	buffer->reverse_range (base, i);
+	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+	  buffer->merge_clusters (base, i);
+	base = i;
+      }
+    }
+    buffer->reverse_range (base, count);
+    if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+      buffer->merge_clusters (base, count);
+
+    buffer->reverse ();
+
     buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
   }
 }
 
 
 /* Substitute */
 
 static inline void
@@ -300,17 +330,17 @@ hb_ot_mirror_chars (hb_ot_shape_context_
   hb_buffer_t *buffer = c->buffer;
   hb_unicode_funcs_t *unicode = buffer->unicode;
   hb_mask_t rtlm_mask = c->plan->rtlm_mask;
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++) {
     hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint);
-    if (likely (codepoint == info[i].codepoint))
+    if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint)))
       info[i].mask |= rtlm_mask;
     else
       info[i].codepoint = codepoint;
   }
 }
 
 static inline void
 hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
@@ -375,16 +405,111 @@ hb_ot_shape_setup_masks (hb_ot_shape_con
     if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
       unsigned int shift;
       hb_mask_t mask = map->get_mask (feature->tag, &shift);
       buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
     }
   }
 }
 
+static void
+hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c)
+{
+  hb_buffer_t *buffer = c->buffer;
+
+  if (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
+    return;
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  unsigned int i = 0;
+  for (i = 0; i < count; i++)
+    if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
+      pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
+}
+
+static void
+hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
+{
+  hb_buffer_t *buffer = c->buffer;
+
+  if (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
+    return;
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  unsigned int i = 0;
+  for (i = 0; i < count; i++)
+  {
+    if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
+      break;
+  }
+
+  /* No default-ignorables found; return. */
+  if (i == count)
+    return;
+
+  hb_codepoint_t space;
+  if (c->font->get_glyph (' ', 0, &space))
+  {
+    /* Replace default-ignorables with a zero-advance space glyph. */
+    for (/*continue*/; i < count; i++)
+    {
+      if (_hb_glyph_info_is_default_ignorable (&info[i]))
+	info[i].codepoint = space;
+    }
+  }
+  else
+  {
+    /* Merge clusters and delete default-ignorables.
+     * NOTE! We can't use out-buffer as we have positioning data. */
+    unsigned int j = i;
+    for (; i < count; i++)
+    {
+      if (_hb_glyph_info_is_default_ignorable (&info[i]))
+      {
+	/* Merge clusters.
+	 * Same logic as buffer->delete_glyph(), but for in-place removal. */
+
+	unsigned int cluster = info[i].cluster;
+	if (i + 1 < count && cluster == info[i + 1].cluster)
+	  continue; /* Cluster survives; do nothing. */
+
+	if (j)
+	{
+	  /* Merge cluster backward. */
+	  if (cluster < info[j - 1].cluster)
+	  {
+	    unsigned int old_cluster = info[j - 1].cluster;
+	    for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
+	      info[k - 1].cluster = cluster;
+	  }
+	  continue;
+	}
+
+	if (i + 1 < count)
+	  buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
+
+	continue;
+      }
+
+      if (j != i)
+      {
+	info[j] = info[i];
+	pos[j] = pos[i];
+      }
+      j++;
+    }
+    buffer->len = j;
+  }
+}
+
+
 static inline void
 hb_ot_map_glyphs_fast (hb_buffer_t  *buffer)
 {
   /* Normalization process sets up glyph_index(), we just copy it. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
     info[i].codepoint = info[i].glyph_index();
@@ -620,16 +745,18 @@ static inline void
 hb_ot_position (hb_ot_shape_context_t *c)
 {
   hb_ot_layout_position_start (c->font, c->buffer);
 
   hb_ot_position_default (c);
 
   hb_bool_t fallback = !hb_ot_position_complex (c);
 
+  hb_ot_zero_width_default_ignorables (c);
+
   hb_ot_layout_position_finish (c->font, c->buffer);
 
   if (fallback && c->plan->shaper->fallback_position)
     _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
 
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
 
@@ -637,63 +764,16 @@ hb_ot_position (hb_ot_shape_context_t *c
 
   if (fallback)
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
 
-/* Post-process */
-
-static void
-hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
-{
-  if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
-    return;
-
-  hb_codepoint_t space;
-  enum {
-    SPACE_DONT_KNOW,
-    SPACE_AVAILABLE,
-    SPACE_UNAVAILABLE
-  } space_status = SPACE_DONT_KNOW;
-
-  unsigned int count = c->buffer->len;
-  hb_glyph_info_t *info = c->buffer->info;
-  hb_glyph_position_t *pos = c->buffer->pos;
-  unsigned int j = 0;
-  for (unsigned int i = 0; i < count; i++)
-  {
-    if (unlikely (!_hb_glyph_info_ligated (&info[i]) &&
-		  _hb_glyph_info_is_default_ignorable (&info[i])))
-    {
-      if (space_status == SPACE_DONT_KNOW)
-	space_status = c->font->get_glyph (' ', 0, &space) ? SPACE_AVAILABLE : SPACE_UNAVAILABLE;
-
-      if (space_status == SPACE_AVAILABLE)
-      {
-	info[i].codepoint = space;
-	pos[i].x_advance = 0;
-	pos[i].y_advance = 0;
-      }
-      else
-	continue; /* Delete it. XXX Merge clusters? */
-    }
-    if (j != i)
-    {
-      info[j] = info[i];
-      pos[j] = pos[i];
-    }
-    j++;
-  }
-  c->buffer->len = j;
-}
-
-
 /* Pull it all together! */
 
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
 
   /* Save the original direction, we use it later. */
@@ -731,16 +811,19 @@ hb_bool_t
 {
   hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features};
   hb_ot_shape_internal (&c);
 
   return true;
 }
 
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
 				  hb_tag_t         table_tag,
 				  hb_set_t        *lookup_indexes /* OUT */)
 {
   /* XXX Does the first part always succeed? */
   HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes);
 }
@@ -761,16 +844,19 @@ add_char (hb_font_t          *font,
   {
     hb_codepoint_t m = unicode->mirroring (u);
     if (m != u && font->get_glyph (m, 0, &glyph))
       glyphs->add (glyph);
   }
 }
 
 
+/**
+ * Since: 0.9.2
+ **/
 void
 hb_ot_shape_glyphs_closure (hb_font_t          *font,
 			    hb_buffer_t        *buffer,
 			    const hb_feature_t *features,
 			    unsigned int        num_features,
 			    hb_set_t           *glyphs)
 {
   hb_ot_shape_plan_t plan;
--- a/gfx/harfbuzz/src/hb-ot-tag.cc
+++ b/gfx/harfbuzz/src/hb-ot-tag.cc
@@ -170,16 +170,21 @@ typedef struct {
  *
  * Updated on 2012-12-07 with more research into remaining codes.
  *
  * Updated on 2013-11-23 based on usage in SIL and Microsoft fonts,
  * the new proposal from Microsoft, and latest ISO 639-3 names.
  *
  * Some items still missing.  Those are commented out at the end.
  * Keep sorted for bsearch.
+ *
+ * Updated as of 2015-05-06: OT1.7 on MS website has some newer
+ * items that we don't have here, eg. Zazaki.  This is the new
+ * items in OpenType 1.7 (red items), most of which we have:
+ * http://www.microsoft.com/typography/otspec170/languagetags.htm
  */
 
 static const LangTag ot_languages[] = {
   {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
   {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
   {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
   {"ach",	HB_TAG('A','C','H',' ')},	/* Acoli */
   {"ada",	HB_TAG('D','N','G',' ')},	/* Dangme */
@@ -212,19 +217,19 @@ static const LangTag ot_languages[] = {
   {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [family] */
   {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolangauge] */
   {"ban",	HB_TAG('B','A','N',' ')},	/* Balinese */
   {"bar",	HB_TAG('B','A','R',' ')},	/* Bavarian */
   {"bbc",	HB_TAG('B','B','C',' ')},	/* Batak Toba */
   {"bci",	HB_TAG('B','A','U',' ')},	/* Baoulé */
   {"bcl",	HB_TAG('B','I','K',' ')},	/* Central Bikol */
   {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
-  {"be",	HB_TAG('B','E','L',' ')},  	/* Belarusian */
+  {"be",	HB_TAG('B','E','L',' ')},	/* Belarusian */
   {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
-  {"ber",	HB_TAG('B','E','R',' ')},  	/* Berber [family] */
+  {"ber",	HB_TAG('B','E','R',' ')},	/* Berber [family] */
   {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
   {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
   {"bfy",	HB_TAG('B','A','G',' ')},	/* Baghelkhandi */
   {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
   {"bgc",	HB_TAG('B','G','C',' ')},	/* Haryanvi */
   {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
   {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) */
@@ -341,19 +346,19 @@ static const LangTag ot_languages[] = {
   {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
   {"guc",	HB_TAG('G','U','C',' ')},	/* Wayuu */
   {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
 /*{"guk",	HB_TAG('G','U','K',' ')},*/	/* Gumuz (in SIL fonts) */
   {"guz",	HB_TAG('G','U','Z',' ')},	/* Ekegusii/Gusii */
   {"gv",	HB_TAG('M','N','X',' ')},	/* Manx */
   {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
   {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
-  {"haw",	HB_TAG('H','A','W',' ')},  	/* Hawaiian */
-  {"hay",	HB_TAG('H','A','Y',' ')},  	/* Haya */
-  {"haz",	HB_TAG('H','A','Z',' ')},  	/* Hazaragi */
+  {"haw",	HB_TAG('H','A','W',' ')},	/* Hawaiian */
+  {"hay",	HB_TAG('H','A','Y',' ')},	/* Haya */
+  {"haz",	HB_TAG('H','A','Z',' ')},	/* Hazaragi */
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
   {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
   {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
   {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
   {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
   {"hno",	HB_TAG('H','N','D',' ')},	/* [Northern] Hindko */
   {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
@@ -537,16 +542,17 @@ static const LangTag ot_languages[] = {
   {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Thai */
   {"noe",	HB_TAG('N','O','E',' ')},	/* Nimadi */
   {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
   {"nov",	HB_TAG('N','O','V',' ')},	/* Novial */
   {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
   {"nr",	HB_TAG('N','D','B',' ')},	/* [South] Ndebele */
   {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
   {"nso",	HB_TAG('S','O','T',' ')},	/* [Northern] Sotho */
+  {"nv",	HB_TAG('N','A','V',' ')},	/* Navajo */
   {"ny",	HB_TAG('C','H','I',' ')},	/* Chewa/Chichwa/Nyanja */
   {"nym",	HB_TAG('N','Y','M',' ')},	/* Nyamwezi */
   {"nyn",	HB_TAG('N','K','L',' ')},	/* Nyankole */
   {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
   {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] */
   {"ojs",	HB_TAG('O','C','R',' ')},	/* Oji-Cree */
   {"om",	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
   {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
@@ -859,16 +865,25 @@ hb_ot_tag_from_language (hb_language_t l
   if (s - lang_str == 3) {
     /* Assume it's ISO-639-3 and upper-case and use it. */
     return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u;
   }
 
   return HB_OT_TAG_DEFAULT_LANGUAGE;
 }
 
+/**
+ * hb_ot_tag_to_language:
+ *
+ * 
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 1.0
+ **/
 hb_language_t
 hb_ot_tag_to_language (hb_tag_t tag)
 {
   unsigned int i;
 
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     return NULL;
 
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -188,17 +188,18 @@ static inline unsigned int ARRAY_LENGTH 
 
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
 #define _ASSERT_STATIC1(_line, _cond)	HB_UNUSED typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
 #define _ASSERT_STATIC0(_line, _cond)	_ASSERT_STATIC1 (_line, (_cond))
 #define ASSERT_STATIC(_cond)		_ASSERT_STATIC0 (__LINE__, (_cond))
 
-#define ASSERT_STATIC_EXPR(_cond)((void) sizeof (char[(_cond) ? 1 : -1]))
+/* Note: C++ allows sizeof() of variable-lengh arrays.  So, if _cond is not
+ * constant, it still compiles (ouch!), but at least we'll get a -Wvla warning. */
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * sizeof (char[(_cond) ? 1 : -1]))
 
 #define _PASTE1(a,b) a##b
 #define PASTE(a,b) _PASTE1(a,b)
 
 /* Lets assert int types.  Saves trouble down the road. */
 
 ASSERT_STATIC (sizeof (int8_t) == 1);
@@ -242,18 +243,18 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 
 # define ASSERT_POD()		_ASSERT_POD0 (__LINE__)
 
 
 
 /* Misc */
 
 /* Void! */
 struct _hb_void_t {};
-typedef const _hb_void_t &hb_void_t;
-#define HB_VOID (* (const _hb_void_t *) NULL)
+typedef const _hb_void_t *hb_void_t;
+#define HB_VOID ((const _hb_void_t *) NULL)
 
 /* Return the number of 1 bits in mask. */
 static inline HB_CONST_FUNC unsigned int
 _hb_popcount32 (uint32_t mask)
 {
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
   return __builtin_popcount (mask);
 #else
@@ -840,19 +841,21 @@ template <typename T> static inline bool
 hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
 {
   return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
 }
 
 
 /* Useful for set-operations on small enums.
  * For example, for testing "x ∈ {x1, x2, x3}" use:
- * (FLAG(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
+ * (FLAG_SAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
  */
-#define FLAG(x) (1<<(x))
+#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((x) < 32) + (1U << (x)))
+#define FLAG_SAFE(x) (1U << (x))
+#define FLAG_UNSAFE(x) ((x) < 32 ? FLAG_SAFE(x) : 0)
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
 template <typename T, typename T2> static inline void
 hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
 {
   if (unlikely (!len))
     return;
--- a/gfx/harfbuzz/src/hb-set-private.hh
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -31,17 +31,25 @@
 #include "hb-object-private.hh"
 
 
 /*
  * The set digests here implement various "filters" that support
  * "approximate member query".  Conceptually these are like Bloom
  * Filter and Quotient Filter, however, much smaller, faster, and
  * designed to fit the requirements of our uses for glyph coverage
- * queries.  As a result, our filters have much higher.
+ * queries.
+ *
+ * Our filters are highly accurate if the lookup covers fairly local
+ * set of glyphs, but fully flooded and ineffective if coverage is
+ * all over the place.
+ *
+ * The frozen-set can be used instead of a digest, to trade more
+ * memory for 100% accuracy, but in practice, that doesn't look like
+ * an attractive trade-off.
  */
 
 template <typename mask_t, unsigned int shift>
 struct hb_set_digest_lowest_bits_t
 {
   ASSERT_POD ();
 
   static const unsigned int mask_bytes = sizeof (mask_t);
--- a/gfx/harfbuzz/src/hb-set.cc
+++ b/gfx/harfbuzz/src/hb-set.cc
@@ -30,17 +30,17 @@
 /* Public API */
 
 
 /**
  * hb_set_create: (Xconstructor)
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_set_t *
 hb_set_create (void)
 {
   hb_set_t *set;
 
   if (!(set = hb_object_create<hb_set_t> ()))
     return hb_set_get_empty ();
@@ -50,17 +50,17 @@ hb_set_create (void)
   return set;
 }
 
 /**
  * hb_set_get_empty:
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_set_t *
 hb_set_get_empty (void)
 {
   static const hb_set_t _hb_set_nil = {
     HB_OBJECT_HEADER_STATIC,
     true, /* in_error */
 
@@ -71,29 +71,29 @@ hb_set_get_empty (void)
 }
 
 /**
  * hb_set_reference: (skip)
  * @set: a set.
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_set_t *
 hb_set_reference (hb_set_t *set)
 {
   return hb_object_reference (set);
 }
 
 /**
  * hb_set_destroy: (skip)
  * @set: a set.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_destroy (hb_set_t *set)
 {
   if (!hb_object_destroy (set)) return;
 
   set->fini ();
 
@@ -105,17 +105,17 @@ hb_set_destroy (hb_set_t *set)
  * @set: a set.
  * @key:
  * @data:
  * @destroy (closure data):
  * @replace:
  *
  * Return value:
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_set_user_data (hb_set_t           *set,
 		      hb_user_data_key_t *key,
 		      void *              data,
 		      hb_destroy_func_t   destroy,
 		      hb_bool_t           replace)
 {
@@ -124,17 +124,17 @@ hb_set_set_user_data (hb_set_t          
 
 /**
  * hb_set_get_user_data: (skip)
  * @set: a set.
  * @key:
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_set_get_user_data (hb_set_t           *set,
 		      hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (set, key);
 }
 
@@ -142,131 +142,131 @@ hb_set_get_user_data (hb_set_t          
 /**
  * hb_set_allocation_successful:
  * @set: a set.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_allocation_successful (const hb_set_t  *set HB_UNUSED)
 {
   return !set->in_error;
 }
 
 /**
  * hb_set_clear:
  * @set: a set.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_clear (hb_set_t *set)
 {
   set->clear ();
 }
 
 /**
  * hb_set_is_empty:
  * @set: a set.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_set_is_empty (const hb_set_t *set)
 {
   return set->is_empty ();
 }
 
 /**
  * hb_set_has:
  * @set: a set.
  * @codepoint: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_has (const hb_set_t *set,
 	    hb_codepoint_t  codepoint)
 {
   return set->has (codepoint);
 }
 
 /**
  * hb_set_add:
  * @set: a set.
  * @codepoint: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_add (hb_set_t       *set,
 	    hb_codepoint_t  codepoint)
 {
   set->add (codepoint);
 }
 
 /**
  * hb_set_add_range:
  * @set: a set.
  * @first: 
  * @last: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_set_add_range (hb_set_t       *set,
 		  hb_codepoint_t  first,
 		  hb_codepoint_t  last)
 {
   set->add_range (first, last);
 }
 
 /**
  * hb_set_del:
  * @set: a set.
  * @codepoint: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_del (hb_set_t       *set,
 	    hb_codepoint_t  codepoint)
 {
   set->del (codepoint);
 }
 
 /**
  * hb_set_del_range:
  * @set: a set.
  * @first: 
  * @last: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_set_del_range (hb_set_t       *set,
 		  hb_codepoint_t  first,
 		  hb_codepoint_t  last)
 {
   set->del_range (first, last);
 }
@@ -275,177 +275,177 @@ hb_set_del_range (hb_set_t       *set,
  * hb_set_is_equal:
  * @set: a set.
  * @other: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_set_is_equal (const hb_set_t *set,
 		 const hb_set_t *other)
 {
   return set->is_equal (other);
 }
 
 /**
  * hb_set_set:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_set (hb_set_t       *set,
 	    const hb_set_t *other)
 {
   set->set (other);
 }
 
 /**
  * hb_set_union:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_union (hb_set_t       *set,
 	      const hb_set_t *other)
 {
   set->union_ (other);
 }
 
 /**
  * hb_set_intersect:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_intersect (hb_set_t       *set,
 		  const hb_set_t *other)
 {
   set->intersect (other);
 }
 
 /**
  * hb_set_subtract:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_subtract (hb_set_t       *set,
 		 const hb_set_t *other)
 {
   set->subtract (other);
 }
 
 /**
  * hb_set_symmetric_difference:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_symmetric_difference (hb_set_t       *set,
 			     const hb_set_t *other)
 {
   set->symmetric_difference (other);
 }
 
 /**
  * hb_set_invert:
  * @set: a set.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.10
  **/
 void
 hb_set_invert (hb_set_t *set)
 {
   set->invert ();
 }
 
 /**
  * hb_set_get_population:
  * @set: a set.
  *
  * Returns the number of numbers in the set.
  *
  * Return value: set population.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 unsigned int
 hb_set_get_population (const hb_set_t *set)
 {
   return set->get_population ();
 }
 
 /**
  * hb_set_get_min:
  * @set: a set.
  *
  * Finds the minimum number in the set.
  *
  * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_codepoint_t
 hb_set_get_min (const hb_set_t *set)
 {
   return set->get_min ();
 }
 
 /**
  * hb_set_get_max:
  * @set: a set.
  *
  * Finds the maximum number in the set.
  *
  * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_codepoint_t
 hb_set_get_max (const hb_set_t *set)
 {
   return set->get_max ();
 }
 
 /**
  * hb_set_next:
  * @set: a set.
  * @codepoint: (inout):
  *
  * 
  *
  * Return value: whether there was a next value.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_next (const hb_set_t *set,
 	     hb_codepoint_t *codepoint)
 {
   return set->next (codepoint);
 }
 
@@ -455,17 +455,17 @@ hb_set_next (const hb_set_t *set,
  * @first: (out): output first codepoint in the range.
  * @last: (inout): input current last and output last codepoint in the range.
  *
  * Gets the next consecutive range of numbers in @set that
  * are greater than current value of @last.
  *
  * Return value: whether there was a next range.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_set_next_range (const hb_set_t *set,
 		   hb_codepoint_t *first,
 		   hb_codepoint_t *last)
 {
   return set->next_range (first, last);
 }
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -101,17 +101,17 @@ hb_shape_plan_plan (hb_shape_plan_t    *
  * @user_features: (array length=num_user_features):
  * @num_user_features: 
  * @shaper_list: (array zero-terminated=1):
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_create (hb_face_t                     *face,
 		      const hb_segment_properties_t *props,
 		      const hb_feature_t            *user_features,
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list)
 {
@@ -153,17 +153,17 @@ hb_shape_plan_create (hb_face_t         
 
 /**
  * hb_shape_plan_get_empty:
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_get_empty (void)
 {
   static const hb_shape_plan_t _hb_shape_plan_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     true, /* default_shaper_list */
@@ -189,31 +189,31 @@ hb_shape_plan_get_empty (void)
 /**
  * hb_shape_plan_reference: (skip)
  * @shape_plan: a shape plan.
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
 {
   return hb_object_reference (shape_plan);
 }
 
 /**
  * hb_shape_plan_destroy: (skip)
  * @shape_plan: a shape plan.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
 {
   if (!hb_object_destroy (shape_plan)) return;
 
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
 #include "hb-shaper-list.hh"
@@ -231,17 +231,17 @@ hb_shape_plan_destroy (hb_shape_plan_t *
  * @data: 
  * @destroy: 
  * @replace: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
 			     hb_user_data_key_t *key,
 			     void *              data,
 			     hb_destroy_func_t   destroy,
 			     hb_bool_t           replace)
 {
@@ -252,17 +252,17 @@ hb_shape_plan_set_user_data (hb_shape_pl
  * hb_shape_plan_get_user_data: (skip)
  * @shape_plan: a shape plan.
  * @key: 
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void *
 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
 			     hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (shape_plan, key);
 }
 
@@ -274,17 +274,17 @@ hb_shape_plan_get_user_data (hb_shape_pl
  * @buffer: a buffer.
  * @features: (array length=num_features):
  * @num_features: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
 		       hb_font_t          *font,
 		       hb_buffer_t        *buffer,
 		       const hb_feature_t *features,
 		       unsigned int        num_features)
 {
@@ -390,17 +390,17 @@ hb_non_global_user_features_present (con
  * @user_features: (array length=num_user_features):
  * @num_user_features: 
  * @shaper_list: (array zero-terminated=1):
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_create_cached (hb_face_t                     *face,
 			     const hb_segment_properties_t *props,
 			     const hb_feature_t            *user_features,
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list)
 {
@@ -481,15 +481,15 @@ retry:
 /**
  * hb_shape_plan_get_shaper:
  * @shape_plan: a shape plan.
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 const char *
 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
 {
   return shape_plan->shaper_name;
 }
--- a/gfx/harfbuzz/src/hb-shape.cc
+++ b/gfx/harfbuzz/src/hb-shape.cc
@@ -28,16 +28,27 @@
 
 #include "hb-private.hh"
 
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-font-private.hh"
 
+/**
+ * SECTION:hb-shape
+ * @title: Shaping
+ * @short_description: Conversion of text strings into positioned glyphs
+ * @include: hb.h
+ *
+ * Shaping is the central operation of HarfBuzz. Shaping operates on buffers,
+ * which are sequences of Unicode characters that use the same font and have
+ * the same text direction, script and language. After shaping the buffer
+ * contains the output glyphs and their positions.
+ **/
 
 static bool
 parse_space (const char **pp, const char *end)
 {
   while (*pp < end && ISSPACE (**pp))
     (*pp)++;
   return true;
 }
@@ -193,25 +204,26 @@ parse_one_feature (const char **pp, cons
 	 parse_feature_indices (pp, end, feature) &&
 	 parse_feature_value_postfix (pp, end, feature) &&
 	 parse_space (pp, end) &&
 	 *pp == end;
 }
 
 /**
  * hb_feature_from_string:
- * @str: (array length=len) (element-type uint8_t):
- * @len: 
- * @feature: (out) (optional):
+ * @str: (array length=len) (element-type uint8_t): a string to parse
+ * @len: length of @str, or -1 if string is nul-terminated
+ * @feature: (out): the #hb_feature_t to initialize with the parsed values
  *
- * 
+ * Parses a string into a #hb_feature_t. If @len is -1 then @str is
+ * %NULL-terminated.
  *
- * Return value: 
+ * Return value: %TRUE if @str is successfully parsed, %FALSE otherwise
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 hb_bool_t
 hb_feature_from_string (const char *str, int len,
 			hb_feature_t *feature)
 {
   hb_feature_t feat;
 
   if (len < 0)
@@ -226,23 +238,25 @@ hb_feature_from_string (const char *str,
 
   if (feature)
     memset (feature, 0, sizeof (*feature));
   return false;
 }
 
 /**
  * hb_feature_to_string:
- * @feature: 
- * @buf: (array length=size):
- * @size: 
+ * @feature: an #hb_feature_t to convert
+ * @buf: (array length=size) (out): output string
+ * @size: the allocated size of @buf
  *
- * 
+ * Converts a #hb_feature_t into a %NULL-terminated string in the format
+ * understood by hb_feature_from_string(). The client in responsible for
+ * allocating big enough size for @buf, 128 bytes is more than enough.
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 void
 hb_feature_to_string (hb_feature_t *feature,
 		      char *buf, unsigned int size)
 {
   if (unlikely (!size)) return;
 
   char s[128];
@@ -285,21 +299,22 @@ void free_static_shaper_list (void)
 {
   free (static_shaper_list);
 }
 #endif
 
 /**
  * hb_shape_list_shapers:
  *
- * 
+ * Retrieves the list of shapers supported by HarfBuzz.
  *
- * Return value: (transfer none):
+ * Return value: (transfer none) (array zero-terminated=1): an array of
+ *    constant strings
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char **
 hb_shape_list_shapers (void)
 {
 retry:
   const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list)