bug 964240 - update harfbuzz to pick up Hangul shaper improvements and other fixes. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Thu, 30 Jan 2014 09:48:20 +0000
changeset 165966 4bf651486f133ba4ff1067de994d8882139c1387
parent 165965 c98dcb17b56b631dba8b0e6b326ad3455aa479ad
child 165967 e4a97d6b2d408b8063096bc32474e4f8b24c5667
push id26108
push userryanvm@gmail.com
push dateThu, 30 Jan 2014 21:12:36 +0000
treeherdermozilla-central@df55b667600a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs964240
milestone29.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
bug 964240 - update harfbuzz to pick up Hangul shaper improvements and other fixes. r=jdaggett
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/hb-blob.cc
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer-serialize.cc
gfx/harfbuzz/src/hb-common.cc
gfx/harfbuzz/src/hb-common.h
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-face-private.hh
gfx/harfbuzz/src/hb-face.cc
gfx/harfbuzz/src/hb-fallback-shape.cc
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-layout.h
gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-default.cc
gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
gfx/harfbuzz/src/hb-ot-shape-complex-hebrew.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc
gfx/harfbuzz/src/hb-ot-shape-fallback.cc
gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
gfx/harfbuzz/src/hb-ot-shape-normalize.cc
gfx/harfbuzz/src/hb-ot-shape-private.hh
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-ot-shape.h
gfx/harfbuzz/src/hb-ot-tag.cc
gfx/harfbuzz/src/hb-ot.h
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-set-private.hh
gfx/harfbuzz/src/hb-shape-plan-private.hh
gfx/harfbuzz/src/hb-shaper-list.hh
gfx/harfbuzz/src/hb-tt-font.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-version.h
gfx/harfbuzz/src/moz.build
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -22,17 +22,16 @@ HBSOURCES =  \
 	hb-blob.cc \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
 	hb-buffer-private.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
 	hb-cache-private.hh \
 	hb-common.cc \
-	hb-fallback-shape.cc \
 	hb-face-private.hh \
 	hb-face.cc \
 	hb-font-private.hh \
 	hb-font.cc \
 	hb-mutex-private.hh \
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
@@ -87,39 +86,47 @@ HBSOURCES += \
 	hb-ot-layout-private.hh \
 	hb-ot-map.cc \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-default.cc \
+	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-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 += \
 	hb-ot.h \
 	hb-ot-layout.h \
+	hb-ot-shape.h \
 	hb-ot-tag.h \
 	$(NULL)
 endif
 
+if HAVE_FALLBACK
+HBSOURCES += hb-fallback-shape.cc
+endif
+
 if HAVE_PTHREAD
 HBCFLAGS += $(PTHREAD_CFLAGS)
 HBLIBS   += $(PTHREAD_LIBS)
 endif
 
 if HAVE_GLIB
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -24,17 +24,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
 /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
 #define _POSIX_C_SOURCE 199309L
 
 #include "hb-private.hh"
 
-#include "hb-blob.h"
 #include "hb-object-private.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <sys/mman.h>
 #endif /* HAVE_SYS_MMAN_H */
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -26,17 +26,16 @@
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_BUFFER_PRIVATE_HH
 #define HB_BUFFER_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-buffer.h"
 #include "hb-object-private.hh"
 #include "hb-unicode-private.hh"
 
 
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
 
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc
+++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc
@@ -199,17 +199,17 @@ static unsigned int
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       if (pos[i].x_offset || pos[i].y_offset)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset));
 
       *p++ = '+';
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
-      if (pos->y_advance)
+      if (pos[i].y_advance)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
     }
 
     unsigned int l = p - b;
     if (buf_size > l)
     {
       memcpy (buf, b, l);
       buf += l;
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -23,18 +23,16 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
-#include "hb-version.h"
-
 #include "hb-mutex-private.hh"
 #include "hb-object-private.hh"
 
 #include <locale.h>
 
 
 /* hb_options_t */
 
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -89,16 +89,17 @@ typedef union _hb_var_int_t {
 /* hb_tag_t */
 
 typedef uint32_t hb_tag_t;
 
 #define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
 #define HB_UNTAG(tag)   ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
+#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
 
 /* len=-1 means str is NUL-terminated. */
 hb_tag_t
 hb_tag_from_string (const char *str, int len);
 
 /* buf should have 4 bytes. */
 void
 hb_tag_to_string (hb_tag_t tag, char *buf);
@@ -265,17 +266,22 @@ typedef enum
   /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE		= HB_TAG ('M','e','r','c'),
   /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS	= HB_TAG ('M','e','r','o'),
   /*6.1*/ HB_SCRIPT_MIAO			= HB_TAG ('P','l','r','d'),
   /*6.1*/ HB_SCRIPT_SHARADA			= HB_TAG ('S','h','r','d'),
   /*6.1*/ HB_SCRIPT_SORA_SOMPENG		= HB_TAG ('S','o','r','a'),
   /*6.1*/ HB_SCRIPT_TAKRI			= HB_TAG ('T','a','k','r'),
 
   /* No script set. */
-  /*---*/ HB_SCRIPT_INVALID			= HB_TAG_NONE
+  /*---*/ HB_SCRIPT_INVALID			= HB_TAG_NONE,
+
+  /* Dummy value to ensure any hb_tag_t value can be passed/stored as hb_script_t
+   * without risking undefined behavior. */
+  /*---*/ _HB_SCRIPT_MAX_VALUE			= HB_TAG_MAX
+
 } hb_script_t;
 
 /* These are moved out of hb_script_t because glib-mkenums chokes otherwise. */
 #if 0
   /*7.0*/ HB_SCRIPT_BASSA_VAH			= HB_TAG ('B','a','s','s'),
   /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN		= HB_TAG ('A','g','h','b'),
   /*7.0*/ HB_SCRIPT_DUPLOYAN			= HB_TAG ('D','u','p','l'),
   /*7.0*/ HB_SCRIPT_ELBASAN			= HB_TAG ('E','l','b','a'),
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -586,17 +586,16 @@ hb_bool_t
 #undef utf16_index
 
   CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (NULL,
                                                                pchars, chars_len,
                                                                kCFAllocatorNull);
 
   CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len);
   CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
-  CFRelease (string_ref);
   CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 				  kCTFontAttributeName, font_data->ct_font);
 
   if (num_features)
   {
     ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len);
 
     /* Need log_clusters to assign features. */
@@ -666,33 +665,43 @@ hb_bool_t
     {
         CFRelease (run_cg_font);
 
 	CFRange range = CTRunGetStringRange (run);
 	buffer->ensure (buffer->len + range.length);
 	if (buffer->in_error)
 	  FAIL ("Buffer resize failed");
 	hb_glyph_info_t *info = buffer->info + buffer->len;
-	buffer->len += range.length;
+
+	CGGlyph notdef = 0;
+	double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, &notdef, NULL, 1);
 
-        for (CFIndex j = 0; j < range.length; j++)
+        for (CFIndex j = range.location; j < range.location + range.length; j++)
 	{
-            CGGlyph notdef = 0;
-            double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, &notdef, NULL, 1);
+	    UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
+	    if (hb_in_range<UniChar> (ch, 0xDC00, 0xDFFF) && range.location < j)
+	    {
+	      ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
+	      if (hb_in_range<UniChar> (ch, 0xD800, 0xDBFF))
+	        /* This is the second of a surrogate pair.  Don't need .notdef
+		 * for this one. */
+	        continue;
+	    }
 
             info->codepoint = notdef;
 	    /* TODO We have to fixup clusters later.  See vis_clusters in
 	     * hb-uniscribe.cc for example. */
-            info->cluster = range.location + j;
+            info->cluster = j;
 
             info->mask = advance;
             info->var1.u32 = 0;
             info->var2.u32 = 0;
 
 	    info++;
+	    buffer->len++;
         }
         continue;
     }
     CFRelease (run_cg_font);
 
     unsigned int num_glyphs = CTRunGetGlyphCount (run);
     if (num_glyphs == 0)
       continue;
@@ -791,12 +800,13 @@ hb_bool_t
           else
             break;
         }
       }
       prev_cluster = curr_cluster;
     }
   }
 
+  CFRelease (string_ref);
   CFRelease (line);
 
   return true;
 }
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FACE_PRIVATE_HH
 #define HB_FACE_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-font.h"
 #include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 
 
 /*
  * hb_face_t
  */
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-ot-layout-private.hh"
 
 #include "hb-font-private.hh"
-#include "hb-blob.h"
 #include "hb-open-file-private.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
 #include "hb-cache-private.hh"
 
 #include <string.h>
 
--- a/gfx/harfbuzz/src/hb-fallback-shape.cc
+++ b/gfx/harfbuzz/src/hb-fallback-shape.cc
@@ -100,26 +100,27 @@ hb_bool_t
    * - Apply fallback kern.
    * - Handle Variation Selectors?
    * - Apply normalization?
    *
    * This will make the fallback shaper into a dumb "TrueType"
    * shaper which many people unfortunately still request.
    */
 
+  bool has_space;
   hb_codepoint_t space;
-  font->get_glyph (' ', 0, &space);
+  has_space = font->get_glyph (' ', 0, &space);
 
   buffer->clear_positions ();
 
   unsigned int count = buffer->len;
 
   for (unsigned int i = 0; i < count; i++)
   {
-    if (buffer->unicode->is_default_ignorable (buffer->info[i].codepoint)) {
+    if (has_space && buffer->unicode->is_default_ignorable (buffer->info[i].codepoint)) {
       buffer->info[i].codepoint = space;
       buffer->pos[i].x_advance = 0;
       buffer->pos[i].y_advance = 0;
       continue;
     }
     font->get_glyph (buffer->info[i].codepoint, 0, &buffer->info[i].codepoint);
     font->get_glyph_advance_for_direction (buffer->info[i].codepoint,
 					   buffer->props.direction,
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FONT_PRIVATE_HH
 #define HB_FONT_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-font.h"
 #include "hb-object-private.hh"
 #include "hb-face-private.hh"
 #include "hb-shaper-private.hh"
 
 
 
 /*
  * hb_font_funcs_t
@@ -140,16 +139,22 @@ struct hb_font_t {
   inline void parent_scale_position (hb_position_t *x, hb_position_t *y) {
     *x = parent_scale_x_position (*x);
     *y = parent_scale_y_position (*y);
   }
 
 
   /* Public getters */
 
+  inline hb_bool_t has_glyph (hb_codepoint_t unicode)
+  {
+    hb_codepoint_t glyph;
+    return get_glyph (unicode, 0, &glyph);
+  }
+
   inline hb_bool_t get_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 			      hb_codepoint_t *glyph)
   {
     *glyph = 0;
     return klass->get.glyph (this, user_data,
 			     unicode, variation_selector, glyph,
 			     klass->user_data.glyph);
   }
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-ot-layout-private.hh"
 
 #include "hb-font-private.hh"
-#include "hb-blob.h"
 #include "hb-open-file-private.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
 #include "hb-cache-private.hh"
 
 #include <string.h>
 
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -29,18 +29,16 @@
 #define HB_SHAPER graphite2
 #define hb_graphite2_shaper_font_data_t gr_font
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-graphite2.h"
 
 #include <graphite2/Segment.h>
 
-#include "hb-ot-tag.h"
-
 
 HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face)
 HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font)
 
 
 /*
  * shaper face data
  */
@@ -104,17 +102,17 @@ hb_graphite2_shaper_face_data_t *
   {
     hb_blob_destroy (silf_blob);
     return NULL;
   }
   hb_blob_destroy (silf_blob);
 
   hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t));
   if (unlikely (!data))
-    hb_blob_destroy (silf_blob);
+    return NULL;
 
   data->face = face;
   data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
 
   if (unlikely (!data->grface)) {
     free (data);
     return NULL;
   }
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -26,18 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-blob.h"
-
 
 namespace OT {
 
 
 
 /*
  * Casts
  */
@@ -589,17 +587,17 @@ typedef USHORT UFWORD;
 /* Date represented in number of seconds since 12:00 midnight, January 1,
  * 1904. The value is represented as a signed 64-bit integer. */
 struct LONGDATETIME
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (likely (c->check_struct (this)));
   }
-  private:
+  protected:
   LONG major;
   ULONG minor;
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 /* Array of four uint8s (length = 32 bits) used to identify a script, language
  * system, feature, or baseline */
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -104,43 +104,49 @@ struct ValueFormat : USHORT
     unsigned int format = *this;
     hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (direction);
 
     if (!format) return;
 
     if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++));
     if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++));
     if (format & xAdvance) {
-      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values++)); else values++;
+      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values));
+      values++;
     }
     /* y_advance values grow downward but font-space grows upward, hence negation */
     if (format & yAdvance) {
-      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values++)); else values++;
+      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values));
+      values++;
     }
 
     if (!has_device ()) return;
 
     x_ppem = font->x_ppem;
     y_ppem = font->y_ppem;
 
     if (!x_ppem && !y_ppem) return;
 
     /* pixel -> fractional pixel */
     if (format & xPlaDevice) {
-      if (x_ppem) glyph_pos.x_offset  += (base + get_device (values++)).get_x_delta (font); else values++;
+      if (x_ppem) glyph_pos.x_offset  += (base + get_device (values)).get_x_delta (font);
+      values++;
     }
     if (format & yPlaDevice) {
-      if (y_ppem) glyph_pos.y_offset  += (base + get_device (values++)).get_y_delta (font); else values++;
+      if (y_ppem) glyph_pos.y_offset  += (base + get_device (values)).get_y_delta (font);
+      values++;
     }
     if (format & xAdvDevice) {
-      if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (font); else values++;
+      if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font);
+      values++;
     }
     if (format & yAdvDevice) {
       /* y_advance values grow downward but font-space grows upward, hence negation */
-      if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values++)).get_y_delta (font); else values++;
+      if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font);
+      values++;
     }
   }
 
   private:
   inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
     unsigned int format = *this;
 
     if (format & xPlacement) values++;
@@ -235,22 +241,22 @@ struct AnchorFormat1
 struct AnchorFormat2
 {
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
 			  hb_position_t *x, hb_position_t *y) const
   {
       unsigned int x_ppem = font->x_ppem;
       unsigned int y_ppem = font->y_ppem;
       hb_position_t cx, cy;
-      hb_bool_t ret = false;
+      hb_bool_t ret;
 
-      if (x_ppem || y_ppem)
-	ret = font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
-      *x = x_ppem && ret ? cx : font->em_scale_x (xCoordinate);
-      *y = y_ppem && ret ? cy : font->em_scale_y (yCoordinate);
+      ret = (x_ppem || y_ppem) &&
+             font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
+      *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
+      *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
   protected:
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -26,18 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_PRIVATE_HH
 #define HB_OT_LAYOUT_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-ot-layout.h"
-
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-set-private.hh"
 
 
 /*
  * GDEF
  */
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -189,21 +189,16 @@ 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 */);
 
 void
-hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
-				  hb_tag_t         table_tag,
-				  hb_set_t        *lookup_indexes /* OUT */);
-
-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 */);
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -194,17 +194,16 @@ collect_features_arabic (hb_ot_shape_pla
   map->add_gsub_pause (NULL);
 
   map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK);
   map->add_gsub_pause (arabic_fallback_shape);
 
   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
   map->add_gsub_pause (NULL);
 
-  map->add_global_bool_feature (HB_TAG('c','s','w','h'));
   map->add_global_bool_feature (HB_TAG('m','s','e','t'));
 }
 
 #include "hb-ot-shape-complex-arabic-fallback.hh"
 
 struct arabic_shape_plan_t
 {
   ASSERT_POD ();
@@ -361,15 +360,15 @@ retry:
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
 {
   "arabic",
   collect_features_arabic,
   NULL, /* override_features */
   data_create_arabic,
   data_destroy_arabic,
   NULL, /* preprocess_text_arabic */
-  NULL, /* normalization_preference */
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
   NULL, /* decompose */
   NULL, /* compose */
   setup_masks_arabic,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc
@@ -22,199 +22,23 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-shape-complex-private.hh"
 
 
-/* TODO Add kana, and other small shapers here */
-
-
-/* The default shaper *only* adds additional per-script features.*/
-
-static const hb_tag_t hangul_features[] =
-{
-  HB_TAG('l','j','m','o'),
-  HB_TAG('v','j','m','o'),
-  HB_TAG('t','j','m','o'),
-  HB_TAG_NONE
-};
-
-static const hb_tag_t tibetan_features[] =
-{
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('a','b','v','m'),
-  HB_TAG('b','l','w','m'),
-  HB_TAG_NONE
-};
-
-static void
-collect_features_default (hb_ot_shape_planner_t *plan)
-{
-  const hb_tag_t *script_features = NULL;
-
-  switch ((hb_tag_t) plan->props.script)
-  {
-    /* Unicode-1.1 additions */
-    case HB_SCRIPT_HANGUL:
-      script_features = hangul_features;
-      break;
-
-    /* Unicode-2.0 additions */
-    case HB_SCRIPT_TIBETAN:
-      script_features = tibetan_features;
-      break;
-  }
-
-  for (; script_features && *script_features; script_features++)
-    plan->map.add_global_bool_feature (*script_features);
-}
-
-static hb_ot_shape_normalization_mode_t
-normalization_preference_default (const hb_segment_properties_t *props)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
-}
-
-static bool
-compose_default (const hb_ot_shape_normalize_context_t *c,
-		 hb_codepoint_t  a,
-		 hb_codepoint_t  b,
-		 hb_codepoint_t *ab)
-{
-  /* Hebrew presentation-form shaping.
-   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866
-   * Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
-   * Note that some letters do not have a dagesh presForm encoded.
-   */
-  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
-    0xFB30, /* ALEF */
-    0xFB31, /* BET */
-    0xFB32, /* GIMEL */
-    0xFB33, /* DALET */
-    0xFB34, /* HE */
-    0xFB35, /* VAV */
-    0xFB36, /* ZAYIN */
-    0x0000, /* HET */
-    0xFB38, /* TET */
-    0xFB39, /* YOD */
-    0xFB3A, /* FINAL KAF */
-    0xFB3B, /* KAF */
-    0xFB3C, /* LAMED */
-    0x0000, /* FINAL MEM */
-    0xFB3E, /* MEM */
-    0x0000, /* FINAL NUN */
-    0xFB40, /* NUN */
-    0xFB41, /* SAMEKH */
-    0x0000, /* AYIN */
-    0xFB43, /* FINAL PE */
-    0xFB44, /* PE */
-    0x0000, /* FINAL TSADI */
-    0xFB46, /* TSADI */
-    0xFB47, /* QOF */
-    0xFB48, /* RESH */
-    0xFB49, /* SHIN */
-    0xFB4A /* TAV */
-  };
-
-  bool found = c->unicode->compose (a, b, ab);
-
-  if (!found && (b & ~0x7F) == 0x0580) {
-      /* Special-case Hebrew presentation forms that are excluded from
-       * standard normalization, but wanted for old fonts. */
-      switch (b) {
-      case 0x05B4: /* HIRIQ */
-	  if (a == 0x05D9) { /* YOD */
-	      *ab = 0xFB1D;
-	      found = true;
-	  }
-	  break;
-      case 0x05B7: /* patah */
-	  if (a == 0x05F2) { /* YIDDISH YOD YOD */
-	      *ab = 0xFB1F;
-	      found = true;
-	  } else if (a == 0x05D0) { /* ALEF */
-	      *ab = 0xFB2E;
-	      found = true;
-	  }
-	  break;
-      case 0x05B8: /* QAMATS */
-	  if (a == 0x05D0) { /* ALEF */
-	      *ab = 0xFB2F;
-	      found = true;
-	  }
-	  break;
-      case 0x05B9: /* HOLAM */
-	  if (a == 0x05D5) { /* VAV */
-	      *ab = 0xFB4B;
-	      found = true;
-	  }
-	  break;
-      case 0x05BC: /* DAGESH */
-	  if (a >= 0x05D0 && a <= 0x05EA) {
-	      *ab = sDageshForms[a - 0x05D0];
-	      found = (*ab != 0);
-	  } else if (a == 0xFB2A) { /* SHIN WITH SHIN DOT */
-	      *ab = 0xFB2C;
-	      found = true;
-	  } else if (a == 0xFB2B) { /* SHIN WITH SIN DOT */
-	      *ab = 0xFB2D;
-	      found = true;
-	  }
-	  break;
-      case 0x05BF: /* RAFE */
-	  switch (a) {
-	  case 0x05D1: /* BET */
-	      *ab = 0xFB4C;
-	      found = true;
-	      break;
-	  case 0x05DB: /* KAF */
-	      *ab = 0xFB4D;
-	      found = true;
-	      break;
-	  case 0x05E4: /* PE */
-	      *ab = 0xFB4E;
-	      found = true;
-	      break;
-	  }
-	  break;
-      case 0x05C1: /* SHIN DOT */
-	  if (a == 0x05E9) { /* SHIN */
-	      *ab = 0xFB2A;
-	      found = true;
-	  } else if (a == 0xFB49) { /* SHIN WITH DAGESH */
-	      *ab = 0xFB2C;
-	      found = true;
-	  }
-	  break;
-      case 0x05C2: /* SIN DOT */
-	  if (a == 0x05E9) { /* SHIN */
-	      *ab = 0xFB2B;
-	      found = true;
-	  } else if (a == 0xFB49) { /* SHIN WITH DAGESH */
-	      *ab = 0xFB2D;
-	      found = true;
-	  }
-	  break;
-      }
-  }
-
-  return found;
-}
-
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
 {
   "default",
-  collect_features_default,
+  NULL, /* collect_features */
   NULL, /* override_features */
   NULL, /* data_create */
   NULL, /* data_destroy */
   NULL, /* preprocess_text */
-  normalization_preference_default,
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
   NULL, /* decompose */
-  compose_default,
+  NULL, /* compose */
   NULL, /* setup_masks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
   true, /* fallback_position */
 };
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -0,0 +1,417 @@
+/*
+ * Copyright © 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-private.hh"
+
+
+/* Hangul shaper */
+
+
+/* Same order as the feature array below */
+enum {
+  NONE,
+
+  LJMO,
+  VJMO,
+  TJMO,
+
+  FIRST_HANGUL_FEATURE = LJMO,
+  HANGUL_FEATURE_COUNT = TJMO + 1
+};
+
+static const hb_tag_t hangul_features[HANGUL_FEATURE_COUNT] =
+{
+  HB_TAG_NONE,
+  HB_TAG('l','j','m','o'),
+  HB_TAG('v','j','m','o'),
+  HB_TAG('t','j','m','o')
+};
+
+static void
+collect_features_hangul (hb_ot_shape_planner_t *plan)
+{
+  hb_ot_map_builder_t *map = &plan->map;
+
+  for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++)
+    map->add_feature (hangul_features[i], 1, F_NONE);
+}
+
+struct hangul_shape_plan_t
+{
+  ASSERT_POD ();
+
+  hb_mask_t mask_array[HANGUL_FEATURE_COUNT];
+};
+
+static void *
+data_create_hangul (const hb_ot_shape_plan_t *plan)
+{
+  hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) calloc (1, sizeof (hangul_shape_plan_t));
+  if (unlikely (!hangul_plan))
+    return NULL;
+
+  for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++)
+    hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]);
+
+  return hangul_plan;
+}
+
+static void
+data_destroy_hangul (void *data)
+{
+  free (data);
+}
+
+/* Constants for algorithmic hangul syllable [de]composition. */
+#define LBase 0x1100
+#define VBase 0x1161
+#define TBase 0x11A7
+#define LCount 19
+#define VCount 21
+#define TCount 28
+#define SBase 0xAC00
+#define NCount (VCount * TCount)
+#define SCount (LCount * NCount)
+
+#define isCombiningL(u) (hb_in_range<hb_codepoint_t> ((u), LBase, LBase+LCount-1))
+#define isCombiningV(u) (hb_in_range<hb_codepoint_t> ((u), VBase, VBase+VCount-1))
+#define isCombiningT(u) (hb_in_range<hb_codepoint_t> ((u), TBase+1, TBase+TCount-1))
+#define isCombinedS(u) (hb_in_range<hb_codepoint_t> ((u), SBase, SBase+SCount-1))
+
+#define isL(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1100, 0x115F, 0xA960, 0xA97C))
+#define isV(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1160, 0x11A7, 0xD7B0, 0xD7C6))
+#define isT(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x11A8, 0x11FF, 0xD7CB, 0xD7FB))
+
+#define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302e, 0x302f))
+
+/* buffer var allocations */
+#define hangul_shaping_feature() complex_var_u8_0() /* hangul jamo shaping feature */
+
+static bool
+is_zero_width_char (hb_font_t *font,
+		    hb_codepoint_t unicode)
+{
+  hb_codepoint_t glyph;
+  return hb_font_get_glyph (font, unicode, 0, &glyph) && hb_font_get_glyph_h_advance (font, glyph) == 0;
+}
+
+static void
+preprocess_text_hangul (const hb_ot_shape_plan_t *plan,
+			hb_buffer_t              *buffer,
+			hb_font_t                *font)
+{
+  HB_BUFFER_ALLOCATE_VAR (buffer, hangul_shaping_feature);
+
+  /* Hangul syllables come in two shapes: LV, and LVT.  Of those:
+   *
+   *   - LV can be precomposed, or decomposed.  Lets call those
+   *     <LV> and <L,V>,
+   *   - LVT can be fully precomposed, partically precomposed, or
+   *     fully decomposed.  Ie. <LVT>, <LV,T>, or <L,V,T>.
+   *
+   * The composition / decomposition is mechanical.  However, not
+   * all <L,V> sequences compose, and not all <LV,T> sequences
+   * compose.
+   *
+   * Here are the specifics:
+   *
+   *   - <L>: U+1100..115F, U+A960..A97F
+   *   - <V>: U+1160..11A7, U+D7B0..D7C7
+   *   - <T>: U+11A8..11FF, U+D7CB..D7FB
+   *
+   *   - Only the <L,V> sequences for the 11xx ranges combine.
+   *   - Only <LV,T> sequences for T in U+11A8..11C3 combine.
+   *
+   * Here is what we want to accomplish in this shaper:
+   *
+   *   - If the whole syllable can be precomposed, do that,
+   *   - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features.
+   *   - If a valid syllable is followed by a Hangul tone mark, reorder the tone
+   *     mark to precede the whole syllable - unless it is a zero-width glyph, in
+   *     which case we leave it untouched, assuming it's designed to overstrike.
+   *
+   * That is, of the different possible syllables:
+   *
+   *   <L>
+   *   <L,V>
+   *   <L,V,T>
+   *   <LV>
+   *   <LVT>
+   *   <LV, T>
+   *
+   * - <L> needs no work.
+   *
+   * - <LV> and <LVT> can stay the way they are if the font supports them, otherwise we
+   *   should fully decompose them if font supports.
+   *
+   * - <L,V> and <L,V,T> we should compose if the whole thing can be composed.
+   *
+   * - <LV,T> we should compose if the whole thing can be composed, otherwise we should
+   *   decompose.
+   */
+
+  buffer->clear_output ();
+  unsigned int start = 0, end = 0; /* Extent of most recently seen syllable;
+				    * valid only if start < end
+				    */
+  unsigned int count = buffer->len;
+
+  for (buffer->idx = 0; buffer->idx < count;)
+  {
+    hb_codepoint_t u = buffer->cur().codepoint;
+
+    if (isHangulTone (u))
+    {
+      /*
+       * We could cache the width of the tone marks and the existence of dotted-circle,
+       * but the use of the Hangul tone mark characters seems to be rare enough that
+       * I didn't bother for now.
+       */
+      if (start < end && end == buffer->out_len)
+      {
+	/* 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;
+	}
+	/* 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 (0x25cc))
+	{
+	  hb_codepoint_t chars[2];
+	  if (!is_zero_width_char (font, u)) {
+	    chars[0] = u;
+	    chars[1] = 0x25cc;
+	  } else {
+	    chars[0] = 0x25cc;
+	    chars[1] = u;
+	  }
+	  buffer->replace_glyphs (1, 2, chars);
+	}
+	else
+	{
+	  /* No dotted circle available in the font; just leave tone mark untouched. */
+	  buffer->next_glyph ();
+	}
+      }
+      start = end = buffer->out_len;
+      continue;
+    }
+
+    start = buffer->out_len; /* Remember current position as a potential syllable start;
+			      * will only be used if we set end to a later position.
+			      */
+
+    if (isL (u) && buffer->idx + 1 < count)
+    {
+      hb_codepoint_t l = u;
+      hb_codepoint_t v = buffer->cur(+1).codepoint;
+      if (isV (v))
+      {
+	/* Have <L,V> or <L,V,T>. */
+	hb_codepoint_t t = 0;
+	unsigned int tindex = 0;
+	if (buffer->idx + 2 < count)
+	{
+	  t = buffer->cur(+2).codepoint;
+	  if (isT (t))
+	    tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */
+	  else
+	    t = 0; /* The next character was not a trailing jamo. */
+	}
+
+	/* We've got a syllable <L,V,T?>; see if it can potentially be composed. */
+	if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t)))
+	{
+	  /* Try to compose; if this succeeds, end is set to start+1. */
+	  hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex;
+	  if (font->has_glyph (s))
+	  {
+	    buffer->replace_glyphs (t ? 3 : 2, 1, &s);
+	    if (unlikely (buffer->in_error))
+	      return;
+	    end = start + 1;
+	    continue;
+	  }
+	}
+
+	/* We didn't compose, either because it's an Old Hangul syllable without a
+	 * precomposed character in Unicode, or because the font didn't support the
+	 * necessary precomposed glyph.
+	 * Set jamo features on the individual glyphs, and advance past them.
+	 */
+	buffer->cur().hangul_shaping_feature() = LJMO;
+	buffer->next_glyph ();
+	buffer->cur().hangul_shaping_feature() = VJMO;
+	buffer->next_glyph ();
+	if (t)
+	{
+	  buffer->cur().hangul_shaping_feature() = TJMO;
+	  buffer->next_glyph ();
+	  end = start + 3;
+	}
+	else
+	  end = start + 2;
+	buffer->merge_out_clusters (start, end);
+	continue;
+      }
+    }
+
+    else if (isCombinedS (u))
+    {
+      /* Have <LV>, <LVT>, or <LV,T> */
+      hb_codepoint_t s = u;
+      bool has_glyph = font->has_glyph (s);
+      unsigned int lindex = (s - SBase) / NCount;
+      unsigned int nindex = (s - SBase) % NCount;
+      unsigned int vindex = nindex / TCount;
+      unsigned int tindex = nindex % TCount;
+
+      if (!tindex &&
+	  buffer->idx + 1 < count &&
+	  isCombiningT (buffer->cur(+1).codepoint))
+      {
+	/* <LV,T>, try to combine. */
+	unsigned int new_tindex = buffer->cur(+1).codepoint - TBase;
+	hb_codepoint_t new_s = s + new_tindex;
+	if (font->has_glyph (new_s))
+	{
+	  buffer->replace_glyphs (2, 1, &new_s);
+	  if (unlikely (buffer->in_error))
+	    return;
+	  end = start + 1;
+	  continue;
+	}
+      }
+
+      /* Otherwise, decompose if font doesn't support <LV> or <LVT>,
+       * or if having non-combining <LV,T>.  Note that we already handled
+       * combining <LV,T> above. */
+      if (!has_glyph ||
+	  (!tindex &&
+	   buffer->idx + 1 < count &&
+	   isT (buffer->cur(+1).codepoint)))
+      {
+	hb_codepoint_t decomposed[3] = {LBase + lindex,
+					VBase + vindex,
+					TBase + tindex};
+	if (font->has_glyph (decomposed[0]) &&
+	    font->has_glyph (decomposed[1]) &&
+	    (!tindex || font->has_glyph (decomposed[2])))
+	{
+	  unsigned int s_len = tindex ? 3 : 2;
+	  buffer->replace_glyphs (1, s_len, decomposed);
+	  if (unlikely (buffer->in_error))
+	    return;
+
+	  /* We decomposed S: apply jamo features to the individual glyphs
+	   * that are now in buffer->out_info.
+	   */
+	  hb_glyph_info_t *info = buffer->out_info;
+
+	  /* If we decomposed an LV because of a non-combining T following,
+	   * we want to include this T in the syllable.
+	   */
+	  if (has_glyph && !tindex)
+	  {
+            buffer->next_glyph ();
+            s_len++;
+          }
+          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);
+	  continue;
+	}
+      }
+
+      if (has_glyph)
+      {
+        /* We didn't decompose the S, so just advance past it. */
+	end = start + 1;
+	buffer->next_glyph ();
+	continue;
+      }
+    }
+
+    /* Didn't find a recognizable syllable, so we leave end <= start;
+     * this will prevent tone-mark reordering happening.
+     */
+    buffer->next_glyph ();
+  }
+  buffer->swap_buffers ();
+}
+
+static void
+setup_masks_hangul (const hb_ot_shape_plan_t *plan,
+		    hb_buffer_t              *buffer,
+		    hb_font_t                *font HB_UNUSED)
+{
+  const hangul_shape_plan_t *hangul_plan = (const hangul_shape_plan_t *) plan->data;
+
+  if (likely (hangul_plan))
+  {
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    for (unsigned int i = 0; i < count; i++, info++)
+      info->mask |= hangul_plan->mask_array[info->hangul_shaping_feature()];
+  }
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, hangul_shaping_feature);
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul =
+{
+  "hangul",
+  collect_features_hangul,
+  NULL, /* override_features */
+  data_create_hangul, /* data_create */
+  data_destroy_hangul, /* data_destroy */
+  preprocess_text_hangul,
+  HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
+  NULL, /* decompose */
+  NULL, /* compose */
+  setup_masks_hangul, /* setup_masks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  false, /* fallback_position */
+};
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hebrew.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2010,2012  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-private.hh"
+
+
+static bool
+compose_hebrew (const hb_ot_shape_normalize_context_t *c,
+		hb_codepoint_t  a,
+		hb_codepoint_t  b,
+		hb_codepoint_t *ab)
+{
+  /* Hebrew presentation-form shaping.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866
+   * Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
+   * Note that some letters do not have a dagesh presForm encoded.
+   */
+  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
+    0xFB30, /* ALEF */
+    0xFB31, /* BET */
+    0xFB32, /* GIMEL */
+    0xFB33, /* DALET */
+    0xFB34, /* HE */
+    0xFB35, /* VAV */
+    0xFB36, /* ZAYIN */
+    0x0000, /* HET */
+    0xFB38, /* TET */
+    0xFB39, /* YOD */
+    0xFB3A, /* FINAL KAF */
+    0xFB3B, /* KAF */
+    0xFB3C, /* LAMED */
+    0x0000, /* FINAL MEM */
+    0xFB3E, /* MEM */
+    0x0000, /* FINAL NUN */
+    0xFB40, /* NUN */
+    0xFB41, /* SAMEKH */
+    0x0000, /* AYIN */
+    0xFB43, /* FINAL PE */
+    0xFB44, /* PE */
+    0x0000, /* FINAL TSADI */
+    0xFB46, /* TSADI */
+    0xFB47, /* QOF */
+    0xFB48, /* RESH */
+    0xFB49, /* SHIN */
+    0xFB4A /* TAV */
+  };
+
+  bool found = c->unicode->compose (a, b, ab);
+
+  if (!found)
+  {
+      /* Special-case Hebrew presentation forms that are excluded from
+       * standard normalization, but wanted for old fonts. */
+      switch (b) {
+      case 0x05B4: /* HIRIQ */
+	  if (a == 0x05D9) { /* YOD */
+	      *ab = 0xFB1D;
+	      found = true;
+	  }
+	  break;
+      case 0x05B7: /* patah */
+	  if (a == 0x05F2) { /* YIDDISH YOD YOD */
+	      *ab = 0xFB1F;
+	      found = true;
+	  } else if (a == 0x05D0) { /* ALEF */
+	      *ab = 0xFB2E;
+	      found = true;
+	  }
+	  break;
+      case 0x05B8: /* QAMATS */
+	  if (a == 0x05D0) { /* ALEF */
+	      *ab = 0xFB2F;
+	      found = true;
+	  }
+	  break;
+      case 0x05B9: /* HOLAM */
+	  if (a == 0x05D5) { /* VAV */
+	      *ab = 0xFB4B;
+	      found = true;
+	  }
+	  break;
+      case 0x05BC: /* DAGESH */
+	  if (a >= 0x05D0 && a <= 0x05EA) {
+	      *ab = sDageshForms[a - 0x05D0];
+	      found = (*ab != 0);
+	  } else if (a == 0xFB2A) { /* SHIN WITH SHIN DOT */
+	      *ab = 0xFB2C;
+	      found = true;
+	  } else if (a == 0xFB2B) { /* SHIN WITH SIN DOT */
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      case 0x05BF: /* RAFE */
+	  switch (a) {
+	  case 0x05D1: /* BET */
+	      *ab = 0xFB4C;
+	      found = true;
+	      break;
+	  case 0x05DB: /* KAF */
+	      *ab = 0xFB4D;
+	      found = true;
+	      break;
+	  case 0x05E4: /* PE */
+	      *ab = 0xFB4E;
+	      found = true;
+	      break;
+	  }
+	  break;
+      case 0x05C1: /* SHIN DOT */
+	  if (a == 0x05E9) { /* SHIN */
+	      *ab = 0xFB2A;
+	      found = true;
+	  } else if (a == 0xFB49) { /* SHIN WITH DAGESH */
+	      *ab = 0xFB2C;
+	      found = true;
+	  }
+	  break;
+      case 0x05C2: /* SIN DOT */
+	  if (a == 0x05E9) { /* SHIN */
+	      *ab = 0xFB2B;
+	      found = true;
+	  } else if (a == 0xFB49) { /* SHIN WITH DAGESH */
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      }
+  }
+
+  return found;
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
+{
+  "hebrew",
+  NULL, /* collect_features */
+  NULL, /* override_features */
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  NULL, /* preprocess_text */
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+  NULL, /* decompose */
+  compose_hebrew,
+  NULL, /* setup_masks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
+  true, /* fallback_position */
+};
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -1685,22 +1685,16 @@ clear_syllables (const hb_ot_shape_plan_
 {
   hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     info[i].syllable() = 0;
 }
 
 
-static hb_ot_shape_normalization_mode_t
-normalization_preference_indic (const hb_segment_properties_t *props HB_UNUSED)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
-}
-
 static bool
 decompose_indic (const hb_ot_shape_normalize_context_t *c,
 		 hb_codepoint_t  ab,
 		 hb_codepoint_t *a,
 		 hb_codepoint_t *b)
 {
   switch (ab)
   {
@@ -1801,15 +1795,15 @@ compose_indic (const hb_ot_shape_normali
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
 {
   "indic",
   collect_features_indic,
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
   NULL, /* preprocess_text */
-  normalization_preference_indic,
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   decompose_indic,
   compose_indic,
   setup_masks_indic,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -536,30 +536,23 @@ final_reordering (const hb_ot_shape_plan
   for (unsigned int i = 0; i < count; i++)
     info[i].syllable() = 0;
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
 }
 
 
-static hb_ot_shape_normalization_mode_t
-normalization_preference_myanmar (const hb_segment_properties_t *props HB_UNUSED)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
-}
-
-
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
 {
   "myanmar",
   collect_features_myanmar,
   override_features_myanmar,
   NULL, /* data_create */
   NULL, /* data_destroy */
   NULL, /* preprocess_text */
-  normalization_preference_myanmar,
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   NULL, /* decompose */
   NULL, /* compose */
   setup_masks_myanmar,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -39,28 +39,33 @@
 #define complex_var_u8_1()	var2.u8[3]
 
 
 enum hb_ot_shape_zero_width_marks_type_t {
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
 //  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
+
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE
 };
 
 
 /* Master OT shaper list */
 #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 (indic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
   HB_COMPLEX_SHAPER_IMPLEMENT (sea) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \
   /* ^--- Add new shapers here */
 
 
 struct hb_ot_complex_shaper_t
 {
   char name[8];
 
   /* collect_features()
@@ -100,22 +105,17 @@ struct hb_ot_complex_shaper_t
    * Shapers can use to modify text before shaping starts.
    * May be NULL.
    */
   void (*preprocess_text) (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t              *buffer,
 			   hb_font_t                *font);
 
 
-  /* normalization_preference()
-   * Called during shape().
-   * May be NULL.
-   */
-  hb_ot_shape_normalization_mode_t
-  (*normalization_preference) (const hb_segment_properties_t *props);
+  hb_ot_shape_normalization_mode_t normalization_preference;
 
   /* decompose()
    * Called during shape()'s normalization.
    * May be NULL.
    */
   bool (*decompose) (const hb_ot_shape_normalize_context_t *c,
 		     hb_codepoint_t  ab,
 		     hb_codepoint_t *a,
@@ -184,29 +184,32 @@ hb_ot_shape_complex_categorize (const hb
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_THAI:
     case HB_SCRIPT_LAO:
 
       return &_hb_ot_complex_shaper_thai;
 
 
-#if 0
-    /* Note:
-     * Currently we don't have a separate Hangul shaper.  The default shaper handles
-     * Hangul by enabling jamo features.  We may want to implement a separate shaper
-     * in the future.  See this thread for details of what such a shaper would do:
-     *
-     *   http://lists.freedesktop.org/archives/harfbuzz/2013-April/003070.html
-     */
     /* Unicode-1.1 additions */
     case HB_SCRIPT_HANGUL:
 
       return &_hb_ot_complex_shaper_hangul;
-#endif
+
+
+    /* Unicode-2.0 additions */
+    case HB_SCRIPT_TIBETAN:
+
+      return &_hb_ot_complex_shaper_tibetan;
+
+
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_HEBREW:
+
+      return &_hb_ot_complex_shaper_hebrew;
 
 
     /* ^--- Add new shapers here */
 
 
 #if 0
     /* Note:
      *
@@ -236,19 +239,16 @@ hb_ot_shape_complex_categorize (const hb
 
     /* Simple */
 
     /* Unicode-1.1 additions */
     /* These have their own shaper now. */
     case HB_SCRIPT_LAO:
     case HB_SCRIPT_THAI:
 
-    /* Unicode-2.0 additions */
-    case HB_SCRIPT_TIBETAN:
-
     /* 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:
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
@@ -355,30 +355,23 @@ final_reordering (const hb_ot_shape_plan
   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);
 }
 
 
-static hb_ot_shape_normalization_mode_t
-normalization_preference_sea (const hb_segment_properties_t *props HB_UNUSED)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
-}
-
-
 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 */
-  normalization_preference_sea,
+  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 */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -364,15 +364,15 @@ preprocess_text_thai (const hb_ot_shape_
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
 {
   "thai",
   NULL, /* collect_features */
   NULL, /* override_features */
   NULL, /* data_create */
   NULL, /* data_destroy */
   preprocess_text_thai,
-  NULL, /* normalization_preference */
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
   false,/* fallback_position */
 };
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2010,2012  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-private.hh"
+
+
+static const hb_tag_t tibetan_features[] =
+{
+  HB_TAG('a','b','v','s'),
+  HB_TAG('b','l','w','s'),
+  HB_TAG('a','b','v','m'),
+  HB_TAG('b','l','w','m'),
+  HB_TAG_NONE
+};
+
+static void
+collect_features_tibetan (hb_ot_shape_planner_t *plan)
+{
+  for (const hb_tag_t *script_features = tibetan_features; script_features && *script_features; script_features++)
+    plan->map.add_global_bool_feature (*script_features);
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_tibetan =
+{
+  "default",
+  collect_features_tibetan,
+  NULL, /* override_features */
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  NULL, /* preprocess_text */
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+  NULL, /* decompose */
+  NULL, /* compose */
+  NULL, /* setup_masks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
+  true, /* fallback_position */
+};
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -425,24 +425,22 @@ void
 
 
 /* Performs old-style TrueType kerning. */
 void
 _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
 			    hb_font_t *font,
 			    hb_buffer_t  *buffer)
 {
-  hb_mask_t kern_mask = plan->map.get_1_mask (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ?
-					      HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'));
-  if (!kern_mask) return;
+  if (!plan->has_kern) return;
 
   unsigned int count = buffer->len;
 
   OT::hb_apply_context_t c (1, font, buffer);
-  c.set_lookup_mask (kern_mask);
+  c.set_lookup_mask (plan->kern_mask);
   c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
 
   hb_glyph_info_t *info = buffer->info;
   hb_glyph_position_t *pos = buffer->pos;
 
   for (unsigned int idx = 0; idx < count;)
   {
     OT::hb_apply_context_t::skipping_forward_iterator_t skippy_iter (&c, idx, 1);
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
@@ -24,25 +24,24 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
 #define HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-font.h"
-#include "hb-buffer.h"
 
 /* buffer var allocations, used during the normalization process */
 #define glyph_index()	var1.u32
 
 struct hb_ot_shape_plan_t;
 
 enum hb_ot_shape_normalization_mode_t {
+  HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
   HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* always fully decomposes and then recompose back */
 
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
 };
 
 HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper,
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -208,18 +208,19 @@ decompose_current_character (const hb_ot
     next_char (buffer, glyph);
   else if (decompose_compatibility (c, buffer->cur().codepoint))
     skip_char (buffer);
   else
     next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
 }
 
 static inline void
-handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
+handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
 {
+  /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
   hb_buffer_t * const buffer = c->buffer;
   hb_font_t * const font = c->font;
   for (; buffer->idx < end - 1;) {
     if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
       /* The next two lines are some ugly lines... But work. */
       if (font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index()))
       {
 	buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
@@ -245,37 +246,36 @@ handle_variation_selector_cluster (const
   }
   if (likely (buffer->idx < end)) {
     set_glyph (buffer->cur(), font);
     buffer->next_glyph ();
   }
 }
 
 static inline void
-decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
+decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
 {
   hb_buffer_t * const buffer = c->buffer;
-  /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
   for (unsigned int i = buffer->idx; i < end; i++)
     if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
-      handle_variation_selector_cluster (c, end);
+      handle_variation_selector_cluster (c, end, short_circuit);
       return;
     }
 
   while (buffer->idx < end)
-    decompose_current_character (c, false);
+    decompose_current_character (c, short_circuit);
 }
 
 static inline void
-decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit, unsigned int end)
+decompose_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool might_short_circuit, bool always_short_circuit)
 {
   if (likely (c->buffer->idx + 1 == end))
-    decompose_current_character (c, short_circuit);
+    decompose_current_character (c, might_short_circuit);
   else
-    decompose_multi_char_cluster (c, end);
+    decompose_multi_char_cluster (c, end, always_short_circuit);
 }
 
 
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   unsigned int a = _hb_glyph_info_get_modified_combining_class (pa);
   unsigned int b = _hb_glyph_info_get_modified_combining_class (pb);
@@ -284,30 +284,30 @@ compare_combining_class (const hb_glyph_
 }
 
 
 void
 _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
 			hb_buffer_t *buffer,
 			hb_font_t *font)
 {
-  hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference ?
-					  plan->shaper->normalization_preference (&buffer->props) :
-					  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT;
+  hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference;
   const hb_ot_shape_normalize_context_t c = {
     plan,
     buffer,
     font,
     buffer->unicode,
     plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode,
     plan->shaper->compose   ? plan->shaper->compose   : compose_unicode
   };
 
-  bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
-		       mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
+  bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE;
+  bool might_short_circuit = always_short_circuit ||
+			     (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
+			      mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT);
   unsigned int count;
 
   /* We do a fairly straightforward yet custom normalization process in three
    * separate rounds: decompose, reorder, recompose (if desired).  Currently
    * this makes two buffer swaps.  We can make it faster by moving the last
    * two rounds into the inner loop for the first round, but it's more readable
    * this way. */
 
@@ -318,17 +318,17 @@ void
   count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
       if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
-    decompose_cluster (&c, short_circuit, end);
+    decompose_cluster (&c, end, might_short_circuit, always_short_circuit);
   }
   buffer->swap_buffers ();
 
 
   /* Second round, reorder (inplace) */
 
   count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
@@ -350,17 +350,18 @@ void
     }
 
     hb_bubble_sort (buffer->info + i, end - i, compare_combining_class);
 
     i = end;
   }
 
 
-  if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
+  if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE ||
+      mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
     return;
 
   /* Third round, recompose */
 
   /* As noted in the comment earlier, we don't try to combine
    * ccc=0 chars with their previous Starter. */
 
   buffer->clear_output ();
@@ -388,18 +389,19 @@ void
 	font->get_glyph (composed, 0, &glyph))
     {
       /* Composes. */
       buffer->next_glyph (); /* Copy to out-buffer. */
       if (unlikely (buffer->in_error))
         return;
       buffer->merge_out_clusters (starter, buffer->out_len);
       buffer->out_len--; /* Remove the second composable. */
-      buffer->out_info[starter].codepoint = composed; /* Modify starter and carry on. */
-      set_glyph (buffer->out_info[starter], font);
+      /* Modify starter and carry on. */
+      buffer->out_info[starter].codepoint = composed;
+      buffer->out_info[starter].glyph_index() = glyph;
       _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
 
       continue;
     }
 
     /* Blocked, or doesn't compose. */
     buffer->next_glyph ();
 
--- a/gfx/harfbuzz/src/hb-ot-shape-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh
@@ -35,16 +35,20 @@
 
 
 struct hb_ot_shape_plan_t
 {
   hb_segment_properties_t props;
   const struct hb_ot_complex_shaper_t *shaper;
   hb_ot_map_t map;
   const void *data;
+  hb_mask_t rtlm_mask, frac_mask, numr_mask, dnom_mask;
+  hb_mask_t kern_mask;
+  unsigned int has_frac : 1;
+  unsigned int has_kern : 1;
 
   inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
   {
     unsigned int table_index;
     switch (table_tag) {
       case HB_OT_TAG_GSUB: table_index = 0; break;
       case HB_OT_TAG_GPOS: table_index = 1; break;
       default: return;
@@ -72,16 +76,27 @@ struct hb_ot_shape_planner_t
 			 map (face, &props) {}
   ~hb_ot_shape_planner_t (void) { map.finish (); }
 
   inline void compile (hb_ot_shape_plan_t &plan)
   {
     plan.props = props;
     plan.shaper = shaper;
     map.compile (plan.map);
+
+    plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
+    plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
+    plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r'));
+    plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m'));
+
+    plan.kern_mask = plan.map.get_mask (HB_DIRECTION_IS_HORIZONTAL (plan.props.direction) ?
+					HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'));
+
+    plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask);
+    plan.has_kern = !!plan.kern_mask;
   }
 
   private:
   NO_COPY (hb_ot_shape_planner_t);
 };
 
 
 #endif /* HB_OT_SHAPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -83,16 +83,20 @@ hb_ot_shape_collect_features (hb_ot_shap
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
     case HB_DIRECTION_INVALID:
     default:
       break;
   }
 
+  map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE);
+  map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE);
+  map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE);
+
   if (planner->shaper->collect_features)
     planner->shaper->collect_features (planner);
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
     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++)
@@ -229,18 +233,17 @@ hb_set_unicode_props (hb_buffer_t *buffe
 static void
 hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
 {
   if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
       _hb_glyph_info_get_general_category (&buffer->info[0]) !=
       HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
     return;
 
-  hb_codepoint_t dottedcircle_glyph;
-  if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
+  if (!font->has_glyph (0x25CC))
     return;
 
   hb_glyph_info_t dottedcircle;
   dottedcircle.codepoint = 0x25CC;
   _hb_glyph_info_set_unicode_props (&dottedcircle, buffer->unicode);
 
   buffer->clear_output ();
 
@@ -287,37 +290,82 @@ hb_ensure_native_direction (hb_buffer_t 
 static inline void
 hb_ot_mirror_chars (hb_ot_shape_context_t *c)
 {
   if (HB_DIRECTION_IS_FORWARD (c->target_direction))
     return;
 
   hb_buffer_t *buffer = c->buffer;
   hb_unicode_funcs_t *unicode = buffer->unicode;
-  hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m'));
+  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))
       info[i].mask |= rtlm_mask;
     else
       info[i].codepoint = codepoint;
   }
 }
 
 static inline void
-hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
+hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
+{
+  if (!c->plan->has_frac)
+    return;
+
+  hb_buffer_t *buffer = c->buffer;
+
+  /* TODO look in pre/post context text also. */
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (info[i].codepoint == 0x2044) /* FRACTION SLASH */
+    {
+      unsigned int start = i, end = i + 1;
+      while (start &&
+	     _hb_glyph_info_get_general_category (&info[start - 1]) ==
+	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
+        start--;
+      while (end < count &&
+	     _hb_glyph_info_get_general_category (&info[end]) ==
+	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
+        end++;
+
+      for (unsigned int j = start; j < i; j++)
+        info[j].mask |= c->plan->numr_mask | c->plan->frac_mask;
+      info[i].mask |= c->plan->frac_mask;
+      for (unsigned int j = i + 1; j < end; j++)
+        info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask;
+
+      i = end - 1;
+    }
+  }
+}
+
+static inline void
+hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c)
 {
   hb_ot_map_t *map = &c->plan->map;
   hb_buffer_t *buffer = c->buffer;
 
   hb_mask_t global_mask = map->get_global_mask ();
   buffer->reset_masks (global_mask);
+}
+
+static inline void
+hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
+{
+  hb_ot_map_t *map = &c->plan->map;
+  hb_buffer_t *buffer = c->buffer;
+
+  hb_ot_shape_setup_masks_fraction (c);
 
   if (c->plan->shaper->setup_masks)
     c->plan->shaper->setup_masks (c->plan, buffer, c->font);
 
   for (unsigned int i = 0; i < c->num_user_features; i++)
   {
     const hb_feature_t *feature = &c->user_features[i];
     if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
@@ -353,16 +401,18 @@ hb_synthesize_glyph_classes (hb_ot_shape
 static inline void
 hb_ot_substitute_default (hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
   if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, buffer, c->font);
 
+  hb_ot_shape_initialize_masks (c);
+
   hb_ot_mirror_chars (c);
 
   HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
 
   _hb_ot_shape_normalize (c->plan, buffer, c->font);
 
   hb_ot_shape_setup_masks (c);
 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2013  Red Hat, 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_H
+#define HB_OT_SHAPE_H
+#define HB_OT_SHAPE_H_IN
+
+#include "hb.h"
+
+#include "hb-ot-layout.h"
+#include "hb-ot-tag.h"
+
+HB_BEGIN_DECLS
+
+/* TODO port to shape-plan / set. */
+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);
+
+void
+hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
+                hb_tag_t         table_tag,
+                hb_set_t        *lookup_indexes /* OUT */);
+
+HB_END_DECLS
+
+#undef HB_OT_SHAPE_H_IN
+#endif /* HB_OT_SHAPE_H */
--- a/gfx/harfbuzz/src/hb-ot-tag.cc
+++ b/gfx/harfbuzz/src/hb-ot-tag.cc
@@ -22,17 +22,16 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
 #include "hb-private.hh"
-#include "hb-ot.h"
 
 #include <string.h>
 
 
 
 /* hb_script_t */
 
 static hb_tag_t
--- a/gfx/harfbuzz/src/hb-ot.h
+++ b/gfx/harfbuzz/src/hb-ot.h
@@ -27,23 +27,16 @@
 #ifndef HB_OT_H
 #define HB_OT_H
 #define HB_OT_H_IN
 
 #include "hb.h"
 
 #include "hb-ot-layout.h"
 #include "hb-ot-tag.h"
+#include "hb-ot-shape.h"
 
 HB_BEGIN_DECLS
 
-/* TODO remove */
-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_END_DECLS
 
 #undef HB_OT_H_IN
 #endif /* HB_OT_H */
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -803,16 +803,22 @@ hb_in_range (T u, T lo, T hi)
        ((lo^hi) & hi) == (lo^hi) &&
        ((lo^hi) & ((lo^hi) + 1)) == 0 )
     return (u & ~(lo^hi)) == lo;
   else
     return lo <= u && u <= hi;
 }
 
 template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+}
+
+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:
--- a/gfx/harfbuzz/src/hb-set-private.hh
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SET_PRIVATE_HH
 #define HB_SET_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-set.h"
 #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
--- a/gfx/harfbuzz/src/hb-shape-plan-private.hh
+++ b/gfx/harfbuzz/src/hb-shape-plan-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SHAPE_PLAN_PRIVATE_HH
 #define HB_SHAPE_PLAN_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-shape-plan.h"
 #include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 
 
 struct hb_shape_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
--- a/gfx/harfbuzz/src/hb-shaper-list.hh
+++ b/gfx/harfbuzz/src/hb-shaper-list.hh
@@ -47,9 +47,11 @@ HB_SHAPER_IMPLEMENT (icu_le)
 #endif
 #ifdef HAVE_UNISCRIBE
 HB_SHAPER_IMPLEMENT (uniscribe)
 #endif
 #ifdef HAVE_CORETEXT
 HB_SHAPER_IMPLEMENT (coretext)
 #endif
 
+#ifdef HAVE_FALLBACK
 HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */
+#endif
--- a/gfx/harfbuzz/src/hb-tt-font.cc
+++ b/gfx/harfbuzz/src/hb-tt-font.cc
@@ -26,18 +26,16 @@
 
 #include "hb-font-private.hh" /* Shall be first since may include windows.h */
 
 #include "hb-open-type-private.hh"
 
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-hmtx-table.hh"
 
-#include "hb-blob.h"
-
 #include <string.h>
 
 
 
 #if 0
 struct hb_tt_font_t
 {
   const struct hhea *hhea;
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -27,18 +27,16 @@
  * Codethink Author(s): Ryan Lortie
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_UNICODE_PRIVATE_HH
 #define HB_UNICODE_PRIVATE_HH
 
 #include "hb-private.hh"
-
-#include "hb-unicode.h"
 #include "hb-object-private.hh"
 
 
 extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256];
 
 /*
  * hb_unicode_funcs_t
  */
@@ -103,17 +101,21 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
     return ret;
   }
 
 
   unsigned int
   modified_combining_class (hb_codepoint_t unicode)
   {
     /* XXX This hack belongs to the Myanmar shaper. */
-    if (unicode == 0x1037) unicode = 0x103A;
+    if (unlikely (unicode == 0x1037)) unicode = 0x103A;
+
+    /* XXX This hack belongs to the SEA shaper (for Tai Tham):
+     * Reorder SAKOT to ensure it comes after any tone marks. */
+    if (unlikely (unicode == 0x1A60)) return 254;
 
     return _hb_modified_combining_class[combining_class (unicode)];
   }
 
   inline hb_bool_t
   is_variation_selector (hb_codepoint_t unicode)
   {
     return unlikely (hb_in_ranges<hb_codepoint_t> (unicode,
@@ -127,20 +129,20 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
    * Note that as of Oct 2012 (Unicode 6.2), U+180E MONGOLIAN VOWEL SEPARATOR
    * is NOT Default_Ignorable, but it really behaves in a way that it should
    * be.  That has been reported to the Unicode Technical Committee for
    * consideration.  As such, we include it here, since Uniscribe removes it.
    * It *is* in Unicode 6.3 however.  U+061C ARABIC LETTER MARK from Unicode
    * 6.3 is also added manually.  The new Unicode 6.3 bidi formatting
    * characters are encoded in a block that was Default_Ignorable already.
    *
-   * Note: While U+115F and U+1160 are Default_Ignorable, we do NOT want to
-   * hide them, as the way Uniscribe has implemented them is with regular
-   * spacing glyphs, and that's the way fonts are made to work.  As such,
-   * we make exceptions for those two.
+   * Note: While U+115F, U+1160, U+3164 and U+FFA0 are Default_Ignorable,
+   * we do NOT want to hide them, as the way Uniscribe has implemented them
+   * is with regular spacing glyphs, and that's the way fonts are made to work.
+   * As such, we make exceptions for those four.
    *
    * Gathered from:
    * http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:DI:]&abb=on&ucd=on&esc=on
    *
    * Last updated to the page with the following versions:
    * Version 3.6; ICU version: 50.0.1.0; Unicode version: 6.1.0.0
    *
    * 4,167 Code Points
@@ -152,20 +154,20 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
    * #115F ;HANGUL CHOSEONG FILLER
    * #1160 ;HANGUL JUNGSEONG FILLER
    * 17B4 ;KHMER VOWEL INHERENT AQ
    * 17B5 ;KHMER VOWEL INHERENT AA
    * 180B..180D ;MONGOLIAN FREE VARIATION SELECTOR THREE
    * 200B..200F ;RIGHT-TO-LEFT MARK
    * 202A..202E ;RIGHT-TO-LEFT OVERRIDE
    * 2060..206F ;NOMINAL DIGIT SHAPES
-   * 3164 ;HANGUL FILLER
+   * #3164 ;HANGUL FILLER
    * FE00..FE0F ;VARIATION SELECTOR-16
    * FEFF ;ZERO WIDTH NO-BREAK SPACE
-   * FFA0 ;HALFWIDTH HANGUL FILLER
+   * #FFA0 ;HALFWIDTH HANGUL FILLER
    * FFF0..FFF8 ;<unassigned-FFF8>
    * 1D173..1D17A ;MUSICAL SYMBOL END PHRASE
    * E0000..E0FFF ;<unassigned-E0FFF>
    */
   inline hb_bool_t
   is_default_ignorable (hb_codepoint_t ch)
   {
     hb_codepoint_t plane = ch >> 16;
@@ -177,19 +179,18 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
 	case 0x00: return unlikely (ch == 0x00AD);
 	case 0x03: return unlikely (ch == 0x034F);
 	case 0x06: return unlikely (ch == 0x061C);
 	case 0x17: return hb_in_range<hb_codepoint_t> (ch, 0x17B4, 0x17B5);
 	case 0x18: return hb_in_range<hb_codepoint_t> (ch, 0x180B, 0x180E);
 	case 0x20: return hb_in_ranges<hb_codepoint_t> (ch, 0x200B, 0x200F,
 							    0x202A, 0x202E,
 							    0x2060, 0x206F);
-	case 0x31: return unlikely (ch == 0x3164);
 	case 0xFE: return hb_in_range<hb_codepoint_t> (ch, 0xFE00, 0xFE0F) || ch == 0xFEFF;
-	case 0xFF: return hb_in_range<hb_codepoint_t> (ch, 0xFFF0, 0xFFF8) || ch == 0xFFA0;
+	case 0xFF: return hb_in_range<hb_codepoint_t> (ch, 0xFFF0, 0xFFF8);
 	default: return false;
       }
     }
     else
     {
       /* Other planes */
       switch (plane) {
 	case 0x01: return hb_in_range<hb_codepoint_t> (ch, 0x0001D173, 0x0001D17A);
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -704,17 +704,17 @@ hb_bool_t
 
     if (!range_records.len) /* No active feature found. */
       goto fail_features;
 
     /* Fixup the pointers. */
     for (unsigned int i = 0; i < range_records.len; i++)
     {
       range_record_t *range = &range_records[i];
-      range->props.potfRecords = feature_records.array + reinterpret_cast<unsigned int> (range->props.potfRecords);
+      range->props.potfRecords = feature_records.array + reinterpret_cast<uintptr_t> (range->props.potfRecords);
     }
   }
   else
   {
   fail_features:
     num_features = 0;
   }
 
@@ -771,23 +771,24 @@ retry:
       hb_codepoint_t c = buffer->info[i].codepoint;
       unsigned int cluster = buffer->info[i].cluster;
       log_clusters[chars_len++] = cluster;
       if (c >= 0x10000 && c < 0x110000)
 	log_clusters[chars_len++] = cluster; /* Surrogates. */
     }
   }
 
-  /* All the following types are sized in multiples of sizeof(int). */
-  unsigned int glyphs_size = scratch_size / ((sizeof (WORD) +
-					      sizeof (SCRIPT_GLYPHPROP) +
-					      sizeof (int) +
-					      sizeof (GOFFSET) +
-					      sizeof (uint32_t))
-					     / sizeof (int));
+  /* The -2 in the following is to compensate for possible
+   * alignment needed after the WORD array.  sizeof(WORD) == 2. */
+  unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
+			   / (sizeof (WORD) +
+			      sizeof (SCRIPT_GLYPHPROP) +
+			      sizeof (int) +
+			      sizeof (GOFFSET) +
+			      sizeof (uint32_t));
 
   ALLOCATE_ARRAY (WORD, glyphs, glyphs_size);
   ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size);
   ALLOCATE_ARRAY (int, advances, glyphs_size);
   ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size);
   ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
 
   /* Note:
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -33,19 +33,19 @@
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 0
 #define HB_VERSION_MINOR 9
-#define HB_VERSION_MICRO 24
+#define HB_VERSION_MICRO 25
 
-#define HB_VERSION_STRING "0.9.24"
+#define HB_VERSION_STRING "0.9.25"
 
 #define HB_VERSION_CHECK(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) >= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 void
 hb_version (unsigned int *major,
--- a/gfx/harfbuzz/src/moz.build
+++ b/gfx/harfbuzz/src/moz.build
@@ -7,47 +7,51 @@
 EXPORTS.harfbuzz += [
     'hb-blob.h',
     'hb-buffer.h',
     'hb-common.h',
     'hb-deprecated.h',
     'hb-face.h',
     'hb-font.h',
     'hb-ot-layout.h',
+    'hb-ot-shape.h',
     'hb-ot-tag.h',
     'hb-ot.h',
     'hb-set.h',
     'hb-shape-plan.h',
     'hb-shape.h',
     'hb-unicode.h',
     'hb-version.h',
     'hb.h',
 ]
 
 SOURCES += [
     'hb-blob.cc', # error: use of undeclared identifier 'snprintf' (FreeBSD)
     'hb-common.cc', # error: use of undeclared identifier 'strdup'
+    'hb-ot-shape-complex-hangul.cc', # error: redefinition of enumerator 'NONE'
     'hb-ot-shape-complex-indic.cc', # error: redefinition of enumerator 'INIT'
     'hb-ot-shape-complex-sea.cc', # error: redefinition of 'basic_features'
     'hb-ot-shape.cc', # error: functions that differ only in their return type cannot be overloaded
     'hb-shape-plan.cc', # error: redefinition of 'hb_ot_shaper_face_data_ensure'
 ]
 
 UNIFIED_SOURCES += [
     'hb-buffer.cc',
     'hb-face.cc',
     'hb-fallback-shape.cc',
     'hb-font.cc',
     'hb-ot-layout.cc',
     'hb-ot-map.cc',
     'hb-ot-shape-complex-arabic.cc',
     'hb-ot-shape-complex-default.cc',
+    'hb-ot-shape-complex-hebrew.cc',
     'hb-ot-shape-complex-indic-table.cc',
     'hb-ot-shape-complex-myanmar.cc',
     'hb-ot-shape-complex-thai.cc',
+    'hb-ot-shape-complex-tibetan.cc',
     'hb-ot-shape-fallback.cc',
     'hb-ot-shape-normalize.cc',
     'hb-ot-tag.cc',
     'hb-set.cc',
     'hb-shape.cc',
     'hb-shaper.cc',
     'hb-unicode.cc',
     'hb-warning.cc',