Bug 1393375 - Update HarfBuzz to version 1.5.1. r=jfkthame
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 07 Sep 2017 09:36:27 -0400
changeset 429048 3d548d178c2524ab72aea64a539a658830bf513d
parent 429047 df9d3661abb63c68caeca8de66c6e44eb7456260
child 429049 ec7fedc88cb3121ad2cca99cf9d36aa388be067a
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1393375
milestone57.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 1393375 - Update HarfBuzz to version 1.5.1. r=jfkthame
gfx/harfbuzz/NEWS
gfx/harfbuzz/README-mozilla
gfx/harfbuzz/configure.ac
gfx/harfbuzz/src/harfbuzz-icu.pc
gfx/harfbuzz/src/harfbuzz.pc
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer-serialize.cc
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-buffer.h
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-directwrite.cc
gfx/harfbuzz/src/hb-face-private.hh
gfx/harfbuzz/src/hb-face.cc
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-ot-font.cc
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-map.cc
gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
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-thai.cc
gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
gfx/harfbuzz/src/hb-ot-shape-fallback.cc
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-shape-plan.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-version.h
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,8 +1,65 @@
+Overview of changes leading to 1.5.1
+Tuesday, September 5, 2017
+====================================
+
+- Fix "unsafe-to-break" in fallback shaping and other corner cases.
+  All our tests pass with --verify now, meaning unsafe-to-break API
+  works as expected.
+- Add --unicodes to hb-view / hb-shape.
+- [indic] Treat Consonant_With_Stacker as consonant.  This will need
+  further tweaking.
+- hb_buffer_diff() tweaks.
+
+
+Overview of changes leading to 1.5.0
+Wednesday, August 23, 2017
+====================================
+
+- Misc new API, for appending a buffer to another, and for comparing
+  contents of two buffers for types of differences.
+
+- New "unsafe-to-break" API.  Can be used to speed up reshaping
+  in line-breaking situations.  Essentially, after shaping, it returns
+  positions in the input string (some of the cluster boundaries) that
+  are "safe to break" in that if the text is segmented at that position
+  and two sides reshaped and concatenated, the shaping result is
+  exactly the same as shaping the text in one piece.
+
+  hb-view and hb-shape and hb-shape now take --verify, which verifies
+  the above property.
+
+  Some corner cases of the implementation are still not quite working.
+  Those will be fixed in subsequent releases.
+
+- New API:
+
+hb_buffer_append()
+
+hb_glyph_flags_t
+HB_GLYPH_FLAG_UNSAFE_TO_BREAK
+HB_GLYPH_FLAG_DEFINED
+hb_glyph_info_get_glyph_flags()
+
+HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS
+
+hb_buffer_diff_flags_t
+HB_BUFFER_DIFF_FLAG_EQUAL
+HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH
+HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH
+HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT
+HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH
+HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH
+HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH
+HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH
+hb_buffer_diff
+
+
 Overview of changes leading to 1.4.8
 Tuesday, August 8, 2017
 ====================================
 
 - Major fix to avar table handling.
 - Rename hb-shape --show-message to --trace.
 - Build fixes.
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,14 +1,14 @@
-gfx/harfbuzz status as of 2017-08-08:
+gfx/harfbuzz status as of 2017-09-05:
 
 This directory contains the harfbuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
-Current version: 1.4.8
+Current version: 1.5.1
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the harfbuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating harfbuzz, and check it into the mozilla tree.
 
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.4.8],
+        [1.5.1],
         [https://github.com/behdad/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
--- a/gfx/harfbuzz/src/harfbuzz-icu.pc
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -1,13 +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.4.8
+Version: 1.5.1
 
 Requires: harfbuzz
 Requires.private: icu-uc
 Libs: -L${libdir} -lharfbuzz-icu
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/harfbuzz.pc
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library
-Version: 1.4.8
+Version: 1.5.1
 
 Libs: -L${libdir} -lharfbuzz
 Libs.private:    
 Requires.private: glib-2.0 >= 2.19.1 
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -45,23 +45,26 @@
 #define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
 #endif
 
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
 HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t);
 
 enum hb_buffer_scratch_flags_t {
   HB_BUFFER_SCRATCH_FLAG_DEFAULT			= 0x00000000u,
   HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII			= 0x00000001u,
   HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES		= 0x00000002u,
   HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK		= 0x00000004u,
   HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000008u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK		= 0x00000010u,
+
   /* Reserved for complex shapers' internal use. */
   HB_BUFFER_SCRATCH_FLAG_COMPLEX0			= 0x01000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX1			= 0x02000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX2			= 0x04000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX3			= 0x08000000u,
 };
 HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
 
@@ -227,35 +230,41 @@ struct hb_buffer_t {
     for (unsigned int j = 0; j < len; j++)
       info[j].mask = mask;
   }
   inline void add_masks (hb_mask_t mask)
   {
     for (unsigned int j = 0; j < len; j++)
       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 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)
+  inline void merge_clusters (unsigned int start, 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);
+  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);
 
+  inline void unsafe_to_break (unsigned int start,
+			       unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    unsafe_to_break_impl (start, end);
+  }
+  HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
+
+
   /* 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)
   { return likely (!size || size < allocated); }
@@ -277,19 +286,76 @@ struct hb_buffer_t {
       return true;
     va_list ap;
     va_start (ap, fmt);
     bool ret = message_impl (font, fmt, ap);
     va_end (ap);
     return ret;
   }
   HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
+
+  static inline void
+  set_cluster (hb_glyph_info_t &info, unsigned int cluster, unsigned int mask = 0)
+  {
+    if (info.cluster != cluster)
+    {
+      if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+	info.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      else
+	info.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+    }
+    info.cluster = cluster;
+  }
+
+  int
+  _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *info,
+				     unsigned int start, unsigned int end,
+				     unsigned int cluster) const
+  {
+    for (unsigned int i = start; i < end; i++)
+      cluster = MIN (cluster, info[i].cluster);
+    return cluster;
+  }
+  void
+  _unsafe_to_break_set_mask (hb_glyph_info_t *info,
+			     unsigned int start, unsigned int end,
+			     unsigned int cluster)
+  {
+    for (unsigned int i = start; i < end; i++)
+      if (cluster != info[i].cluster)
+      {
+	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
+	info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      }
+  }
 };
 
 
+/* Loop over clusters. Duplicated in foreach_syllable(). */
+#define foreach_cluster(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_cluster (buffer, start))
+
+static inline unsigned int
+_next_cluster (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  unsigned int cluster = info[start].cluster;
+  while (++start < count && cluster == info[start].cluster)
+    ;
+
+  return start;
+}
+
+
 #define HB_BUFFER_XALLOCATE_VAR(b, func, var) \
   b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
 	   sizeof (b->info[0].var))
 #define HB_BUFFER_ALLOCATE_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, allocate_var,   var ())
 #define HB_BUFFER_DEALLOCATE_VAR(b, var)	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ())
 #define HB_BUFFER_ASSERT_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, assert_var,     var ())
 
 
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc
+++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc
@@ -140,30 +140,36 @@ static unsigned int
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
     }
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
-		     pos[i].x_offset, pos[i].y_offset);
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
-		     pos[i].x_advance, pos[i].y_advance);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
+			     pos[i].x_offset, pos[i].y_offset));
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
+			     pos[i].x_advance, pos[i].y_advance));
+    }
+
+    if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
+    {
+      if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED));
     }
 
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
     {
       hb_glyph_extents_t extents;
       hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
-        extents.x_bearing, extents.y_bearing));
+		extents.x_bearing, extents.y_bearing));
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
-        extents.width, extents.height));
+		extents.width, extents.height));
     }
 
     *p++ = '}';
 
     unsigned int l = p - b;
     if (buf_size > l)
     {
       memcpy (buf, b, l);
@@ -221,16 +227,22 @@ static unsigned int
 	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[i].y_advance)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
     }
 
+    if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
+    {
+      if (info[i].mask &HB_GLYPH_FLAG_DEFINED)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
+    }
+
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
     {
       hb_glyph_extents_t extents;
       hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
     }
 
     unsigned int l = p - b;
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -26,20 +26,16 @@
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-buffer-private.hh"
 #include "hb-utf-private.hh"
 
 
-#ifndef HB_DEBUG_BUFFER
-#define HB_DEBUG_BUFFER (HB_DEBUG+0)
-#endif
-
 /**
  * SECTION: hb-buffer
  * @title: Buffers
  * @short_description: Input and output buffers
  * @include: hb.h
  *
  * Buffers serve dual role in HarfBuzz; they hold the input characters that are
  * passed hb_shape(), and after shaping they hold the output glyphs.
@@ -262,17 +258,17 @@ hb_buffer_t::add (hb_codepoint_t  codepo
   hb_glyph_info_t *glyph;
 
   if (unlikely (!ensure (len + 1))) return;
 
   glyph = &info[len];
 
   memset (glyph, 0, sizeof (*glyph));
   glyph->codepoint = codepoint;
-  glyph->mask = 1;
+  glyph->mask = 0;
   glyph->cluster = cluster;
 
   len++;
 }
 
 void
 hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info)
 {
@@ -545,104 +541,136 @@ hb_buffer_t::reverse_clusters (void)
   reverse_range (start, i);
 }
 
 void
 hb_buffer_t::merge_clusters_impl (unsigned int start,
 				  unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+  {
+    unsafe_to_break (start, end);
     return;
+  }
 
-  uint32_t cluster = info[start].cluster;
+  unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, info[i].cluster);
 
   /* Extend end */
   while (end < len && info[end - 1].cluster == info[end].cluster)
     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 int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
-      out_info[i - 1].cluster = cluster;
+      set_cluster (out_info[i - 1], cluster);
 
   for (unsigned int i = start; i < end; i++)
-    info[i].cluster = cluster;
+    set_cluster (info[i], cluster);
 }
 void
 hb_buffer_t::merge_out_clusters (unsigned int start,
 				 unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     return;
 
   if (unlikely (end - start < 2))
     return;
 
-  uint32_t cluster = out_info[start].cluster;
+  unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, out_info[i].cluster);
 
   /* Extend start */
   while (start && out_info[start - 1].cluster == out_info[start].cluster)
     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 int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
-      info[i].cluster = cluster;
+      set_cluster (info[i], cluster);
 
   for (unsigned int i = start; i < end; i++)
-    out_info[i].cluster = cluster;
+    set_cluster (out_info[i], cluster);
 }
 void
 hb_buffer_t::delete_glyph ()
 {
+  /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */
+
   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 mask = info[idx].mask;
       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;
+	set_cluster (out_info[i - 1], cluster, mask);
     }
     goto done;
   }
 
   if (idx + 1 < len)
   {
     /* Merge cluster forward. */
     merge_clusters (idx, idx + 2);
     goto done;
   }
 
 done:
   skip_glyph ();
 }
 
 void
+hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
+{
+  unsigned int cluster = (unsigned int) -1;
+  cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
+  _unsafe_to_break_set_mask (info, start, end, cluster);
+}
+void
+hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
+{
+  if (!have_output)
+  {
+    unsafe_to_break_impl (start, end);
+    return;
+  }
+
+  assert (start <= out_len);
+  assert (idx <= end);
+
+  unsigned int cluster = (unsigned int) -1;
+  cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster);
+  cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster);
+  _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
+  _unsafe_to_break_set_mask (info, idx, end, cluster);
+}
+
+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 */
   if (props.script == HB_SCRIPT_INVALID) {
     for (unsigned int i = 0; i < len; i++) {
@@ -1375,16 +1403,33 @@ hb_buffer_get_glyph_positions (hb_buffer
 
   if (length)
     *length = buffer->len;
 
   return (hb_glyph_position_t *) buffer->pos;
 }
 
 /**
+ * hb_glyph_info_get_glyph_flags:
+ * @info: a #hb_glyph_info_t.
+ *
+ * Returns glyph flags encoded within a #hb_glyph_info_t.
+ *
+ * Return value:
+ * The #hb_glyph_flags_t encoded within @info.
+ *
+ * Since: 1.5.0
+ **/
+hb_glyph_flags_t
+(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info)
+{
+  return hb_glyph_info_get_glyph_flags (info);
+}
+
+/**
  * hb_buffer_reverse:
  * @buffer: an #hb_buffer_t.
  *
  * Reverses buffer contents.
  *
  * Since: 0.9.2
  **/
 void
@@ -1661,16 +1706,68 @@ hb_buffer_add_codepoints (hb_buffer_t   
 			  int                   text_length,
 			  unsigned int          item_offset,
 			  int                   item_length)
 {
   hb_buffer_add_utf<hb_utf32_t<false> > (buffer, text, text_length, item_offset, item_length);
 }
 
 
+/**
+ * hb_buffer_append:
+ * @buffer: an #hb_buffer_t.
+ * @source: source #hb_buffer_t.
+ * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
+ * @end: end index into source buffer to copy.  Use (unsigned int) -1 to copy to end of buffer.
+ *
+ * Append (part of) contents of another buffer to this buffer.
+ *
+ * Since: 1.5.0
+ **/
+HB_EXTERN void
+hb_buffer_append (hb_buffer_t *buffer,
+		  hb_buffer_t *source,
+		  unsigned int start,
+		  unsigned int end)
+{
+  assert (!buffer->have_output && !source->have_output);
+  assert (buffer->have_positions == source->have_positions ||
+	  !buffer->len || !source->len);
+  assert (buffer->content_type == source->content_type ||
+	  !buffer->len || !source->len);
+
+  if (end > source->len)
+    end = source->len;
+  if (start > end)
+    start = end;
+  if (start == end)
+    return;
+
+  if (!buffer->len)
+    buffer->content_type = source->content_type;
+  if (!buffer->have_positions && source->have_positions)
+    buffer->clear_positions ();
+
+  if (buffer->len + (end - start) < buffer->len) /* Overflows. */
+  {
+    buffer->in_error = true;
+    return;
+  }
+
+  unsigned int orig_len = buffer->len;
+  hb_buffer_set_length (buffer, buffer->len + (end - start));
+  if (buffer->in_error)
+    return;
+
+  memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
+  if (buffer->have_positions)
+    memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
+}
+
+
 static int
 compare_info_codepoint (const hb_glyph_info_t *pa,
 			const hb_glyph_info_t *pb)
 {
   return (int) pb->codepoint - (int) pa->codepoint;
 }
 
 static inline void
@@ -1731,17 +1828,18 @@ normalize_glyphs_cluster (hb_buffer_t *b
  * <note>This has nothing to do with Unicode normalization.</note>
  *
  * 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);
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
   hb_glyph_info_t *info = buffer->info;
 
   unsigned int start = 0;
@@ -1770,16 +1868,108 @@ hb_buffer_t::sort (unsigned int start, u
     {
       hb_glyph_info_t t = info[i];
       memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t));
       info[j] = t;
     }
   }
 }
 
+
+/*
+ * Comparing buffers.
+ */
+
+/**
+ * hb_buffer_diff:
+ *
+ * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+ * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
+ * callers if just comparing two buffers is needed.
+ *
+ * Since: 1.5.0
+ **/
+hb_buffer_diff_flags_t
+hb_buffer_diff (hb_buffer_t *buffer,
+		hb_buffer_t *reference,
+		hb_codepoint_t dottedcircle_glyph,
+		unsigned int position_fuzz)
+{
+  if (buffer->content_type != reference->content_type && buffer->len && reference->len)
+    return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH;
+
+  hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL;
+  bool contains = dottedcircle_glyph != (hb_codepoint_t) -1;
+
+  unsigned int count = reference->len;
+
+  if (buffer->len != count)
+  {
+    /*
+     * we can't compare glyph-by-glyph, but we do want to know if there
+     * are .notdef or dottedcircle glyphs present in the reference buffer
+     */
+    const hb_glyph_info_t *info = reference->info;
+    unsigned int i;
+    for (i = 0; i < count; i++)
+    {
+      if (contains && info[i].codepoint == dottedcircle_glyph)
+        result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+      if (contains && info[i].codepoint == 0)
+        result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
+    }
+    result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
+    return hb_buffer_diff_flags_t (result);
+  }
+
+  if (!count)
+    return hb_buffer_diff_flags_t (result);
+
+  const hb_glyph_info_t *buf_info = buffer->info;
+  const hb_glyph_info_t *ref_info = reference->info;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (buf_info->codepoint != ref_info->codepoint)
+      result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH;
+    if (buf_info->cluster != ref_info->cluster)
+      result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
+    if ((buf_info->mask & HB_GLYPH_FLAG_DEFINED) != (ref_info->mask & HB_GLYPH_FLAG_DEFINED))
+      result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
+    if (contains && ref_info->codepoint == dottedcircle_glyph)
+      result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+    if (contains && ref_info->codepoint == 0)
+      result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
+    buf_info++;
+    ref_info++;
+  }
+
+  if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)
+  {
+    assert (buffer->have_positions);
+    const hb_glyph_position_t *buf_pos = buffer->pos;
+    const hb_glyph_position_t *ref_pos = reference->pos;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz ||
+          (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz ||
+          (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz ||
+          (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz)
+      {
+        result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH;
+        break;
+      }
+      buf_pos++;
+      ref_pos++;
+    }
+  }
+
+  return result;
+}
+
+
 /*
  * Debugging.
  */
 
 /**
  * hb_buffer_set_message_func:
  * @buffer: an #hb_buffer_t.
  * @func: (closure user_data) (destroy destroy) (scope notified):
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -58,24 +58,37 @@ HB_BEGIN_DECLS
  *           allow selecting more fine-grained cluster handling.
  *
  * The #hb_glyph_info_t is the structure that holds information about the
  * glyphs and their relation to input text.
  *
  */
 typedef struct hb_glyph_info_t {
   hb_codepoint_t codepoint;
-  hb_mask_t      mask;
+  hb_mask_t      mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */
   uint32_t       cluster;
 
   /*< private >*/
   hb_var_int_t   var1;
   hb_var_int_t   var2;
 } hb_glyph_info_t;
 
+typedef enum { /*< flags >*/
+  HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
+
+  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
+} hb_glyph_flags_t;
+
+HB_EXTERN hb_glyph_flags_t
+hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info);
+
+#define hb_glyph_info_get_glyph_flags(info) \
+	((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED))
+
+
 /**
  * hb_glyph_position_t:
  * @x_advance: how much the line advances after drawing this glyph when setting
  *             text in horizontal direction.
  * @y_advance: how much the line advances after drawing this glyph when setting
  *             text in vertical direction.
  * @x_offset: how much the glyph moves on the X-axis before drawing it, this
  *            should not affect how much the line advances.
@@ -158,16 +171,17 @@ hb_buffer_set_user_data (hb_buffer_t    
 			 void *              data,
 			 hb_destroy_func_t   destroy,
 			 hb_bool_t           replace);
 
 HB_EXTERN void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key);
 
+
 /**
  * hb_buffer_content_type_t:
  * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer.
  * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping).
  * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping).
  */
 typedef enum {
   HB_BUFFER_CONTENT_TYPE_INVALID = 0,
@@ -354,16 +368,21 @@ hb_buffer_add_latin1 (hb_buffer_t   *buf
 
 HB_EXTERN void
 hb_buffer_add_codepoints (hb_buffer_t          *buffer,
 			  const hb_codepoint_t *text,
 			  int                   text_length,
 			  unsigned int          item_offset,
 			  int                   item_length);
 
+HB_EXTERN void
+hb_buffer_append (hb_buffer_t *buffer,
+		  hb_buffer_t *source,
+		  unsigned int start,
+		  unsigned int end);
 
 HB_EXTERN hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length);
 
 HB_EXTERN unsigned int
 hb_buffer_get_length (hb_buffer_t *buffer);
 
@@ -398,17 +417,18 @@ hb_buffer_normalize_glyphs (hb_buffer_t 
  *
  * Since: 0.9.20
  */
 typedef enum { /*< flags >*/
   HB_BUFFER_SERIALIZE_FLAG_DEFAULT		= 0x00000000u,
   HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS		= 0x00000001u,
   HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS		= 0x00000002u,
   HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004u,
-  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u
+  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u,
+  HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS		= 0x00000010u
 } hb_buffer_serialize_flags_t;
 
 /**
  * hb_buffer_serialize_format_t:
  * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format.
  * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format.
  * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format.
  *
@@ -448,16 +468,55 @@ hb_buffer_deserialize_glyphs (hb_buffer_
 			      const char *buf,
 			      int buf_len,
 			      const char **end_ptr,
 			      hb_font_t *font,
 			      hb_buffer_serialize_format_t format);
 
 
 /*
+ * Compare buffers
+ */
+
+typedef enum { /*< flags >*/
+  HB_BUFFER_DIFF_FLAG_EQUAL			= 0x0000,
+
+  /* Buffers with different content_type cannot be meaningfully compared
+   * in any further detail. */
+  HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH	= 0x0001,
+
+  /* For buffers with differing length, the per-glyph comparison is not
+   * attempted, though we do still scan reference for dottedcircle / .notdef
+   * glyphs. */
+  HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH		= 0x0002,
+
+  /* We want to know if dottedcircle / .notdef glyphs are present in the
+   * reference, as we may not care so much about other differences in this
+   * case. */
+  HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT		= 0x0004,
+  HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT	= 0x0008,
+
+  /* If the buffers have the same length, we compare them glyph-by-glyph
+   * and report which aspect(s) of the glyph info/position are different. */
+  HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH	= 0x0010,
+  HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH		= 0x0020,
+  HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH	= 0x0040,
+  HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH		= 0x0080
+
+} hb_buffer_diff_flags_t;
+
+/* Compare the contents of two buffers, report types of differences. */
+hb_buffer_diff_flags_t
+hb_buffer_diff (hb_buffer_t *buffer,
+		hb_buffer_t *reference,
+		hb_codepoint_t dottedcircle_glyph,
+		unsigned int position_fuzz);
+
+
+/*
  * Debugging.
  */
 
 typedef hb_bool_t	(*hb_buffer_message_func_t)	(hb_buffer_t *buffer,
 							 hb_font_t   *font,
 							 const char  *message,
 							 void        *user_data);
 
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -1175,24 +1175,30 @@ resize_and_retry:
     hb_glyph_info_t *info = buffer->info;
     hb_glyph_position_t *pos = buffer->pos;
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
       for (unsigned int i = 0; i < count; i++)
       {
 	pos->x_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
+
+	info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+
 	info++, pos++;
       }
     else
       for (unsigned int i = 0; i < count; i++)
       {
 	pos->y_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
+
+	info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+
 	info++, pos++;
       }
 
     /* Fix up clusters so that we never return out-of-order indices;
      * if core text has reordered glyphs, we'll merge them to the
      * beginning of the reordered cluster.  CoreText is nice enough
      * to tell us whenever it has produced nonmonotonic results...
      * Note that we assume the input clusters were nonmonotonic to
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -875,16 +875,18 @@ retry_getglyphs:
     hb_glyph_info_t *info = &buffer->info[i];
     hb_glyph_position_t *pos = &buffer->pos[i];
 
     /* TODO vertical */
     pos->x_advance = x_mult * (int32_t) info->mask;
     pos->x_offset =
       x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
     pos->y_offset = y_mult * info->var2.i32;
+
+    info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 
   if (isRightToLeft)
     hb_buffer_reverse (buffer);
 
   free (clusterMap);
   free (glyphIndices);
   free (textProperties);
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -50,20 +50,20 @@ struct hb_face_t {
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
   unsigned int index;			/* Face index in a collection, zero-based. */
   mutable unsigned int upem;		/* Units-per-EM. */
   mutable unsigned int num_glyphs;	/* Number of glyphs. */
 
   enum dirty_t {
-    NOTHING	= 0x0000,
-    INDEX	= 0x0001,
-    UPEM	= 0x0002,
-    NUM_GLYPHS	= 0x0004,
+    DIRTY_NOTHING	= 0x0000,
+    DIRTY_INDEX		= 0x0001,
+    DIRTY_UPEM		= 0x0002,
+    DIRTY_NUM_GLYPHS	= 0x0004,
   } dirty;
 
   struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
   /* Various non-shaping data. */
   /* ... */
 
   /* Cache */
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -46,17 +46,17 @@ const hb_face_t _hb_face_nil = {
   NULL, /* reference_table_func */
   NULL, /* user_data */
   NULL, /* destroy */
 
   0,    /* index */
   1000, /* upem */
   0,    /* num_glyphs */
 
-  hb_face_t::NOTHING, /* dirty */
+  hb_face_t::DIRTY_NOTHING, /* dirty */
 
   {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
   },
 
   NULL, /* shape_plans */
@@ -365,17 +365,17 @@ hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
   if (face->immutable)
     return;
 
   if (face->index == index)
     return;
 
-  face->dirty |= face->INDEX;
+  face->dirty |= face->DIRTY_INDEX;
 
   face->index = index;
 }
 
 /**
  * hb_face_get_index:
  * @face: a face.
  *
@@ -405,17 +405,17 @@ hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem)
 {
   if (face->immutable)
     return;
 
   if (face->upem == upem)
     return;
 
-  face->dirty |= face->UPEM;
+  face->dirty |= face->DIRTY_UPEM;
 
   face->upem = upem;
 }
 
 /**
  * hb_face_get_upem:
  * @face: a face.
  *
@@ -454,17 +454,17 @@ hb_face_set_glyph_count (hb_face_t    *f
 			 unsigned int  glyph_count)
 {
   if (face->immutable)
     return;
 
   if (face->num_glyphs == glyph_count)
     return;
 
-  face->dirty |= face->NUM_GLYPHS;
+  face->dirty |= face->DIRTY_NUM_GLYPHS;
 
   face->num_glyphs = glyph_count;
 }
 
 /**
  * hb_face_get_glyph_count:
  * @face: a face.
  *
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -112,23 +112,23 @@ struct hb_font_t {
   unsigned int num_coords;
   int *coords;
 
   hb_font_funcs_t   *klass;
   void              *user_data;
   hb_destroy_func_t  destroy;
 
   enum dirty_t {
-    NOTHING	= 0x0000,
-    FACE	= 0x0001,
-    PARENT	= 0x0002,
-    FUNCS	= 0x0004,
-    SCALE	= 0x0008,
-    PPEM	= 0x0010,
-    VARIATIONS	= 0x0020,
+    DIRTY_NOTHING	= 0x0000,
+    DIRTY_FACE		= 0x0001,
+    DIRTY_PARENT	= 0x0002,
+    DIRTY_FUNCS		= 0x0004,
+    DIRTY_SCALE		= 0x0008,
+    DIRTY_PPEM		= 0x0010,
+    DIRTY_VARIATIONS	= 0x0020,
   } dirty;
 
   struct hb_shaper_data_t shaper_data;
 
 
   /* Convert from font-space to user-space */
   inline int dir_scale (hb_direction_t direction)
   { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; }
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -1191,17 +1191,17 @@ hb_font_get_empty (void)
 
     0, /* num_coords */
     NULL, /* coords */
 
     const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
     NULL, /* user_data */
     NULL, /* destroy */
 
-    hb_font_t::NOTHING, /* dirty */
+    hb_font_t::DIRTY_NOTHING, /* dirty */
 
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
     }
   };
 
@@ -1348,17 +1348,17 @@ hb_font_set_parent (hb_font_t *font,
     return;
 
   if (!parent)
     parent = hb_font_get_empty ();
 
   if (parent == font->parent)
     return;
 
-  font->dirty |= font->PARENT;
+  font->dirty |= font->DIRTY_PARENT;
 
   hb_font_t *old = font->parent;
 
   font->parent = hb_font_reference (parent);
 
   hb_font_destroy (old);
 }
 
@@ -1395,17 +1395,17 @@ hb_font_set_face (hb_font_t *font,
     return;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
 
   if (font->face == face)
     return;
 
-  font->dirty |= font->FACE;
+  font->dirty |= font->DIRTY_FACE;
 
   hb_face_t *old = font->face;
 
   font->face = hb_face_reference (face);
 
   hb_face_destroy (old);
 }
 
@@ -1450,17 +1450,17 @@ hb_font_set_funcs (hb_font_t         *fo
   }
 
   if (font->destroy)
     font->destroy (font->user_data);
 
   if (!klass)
     klass = hb_font_funcs_get_empty ();
 
-  font->dirty |= font->FUNCS;
+  font->dirty |= font->DIRTY_FUNCS;
 
   hb_font_funcs_reference (klass);
   hb_font_funcs_destroy (font->klass);
   font->klass = klass;
   font->user_data = font_data;
   font->destroy = destroy;
 }
 
@@ -1510,17 +1510,17 @@ hb_font_set_scale (hb_font_t *font,
 		   int y_scale)
 {
   if (font->immutable)
     return;
 
   if (font->x_scale == x_scale && font->y_scale == y_scale)
     return;
 
-  font->dirty |= font->SCALE;
+  font->dirty |= font->DIRTY_SCALE;
 
   font->x_scale = x_scale;
   font->y_scale = y_scale;
 }
 
 /**
  * hb_font_get_scale:
  * @font: a font.
@@ -1556,17 +1556,17 @@ hb_font_set_ppem (hb_font_t *font,
 		  unsigned int y_ppem)
 {
   if (font->immutable)
     return;
 
   if (font->x_ppem == x_ppem && font->y_ppem == y_ppem)
     return;
 
-  font->dirty |= font->PPEM;
+  font->dirty |= font->DIRTY_PPEM;
 
   font->x_ppem = x_ppem;
   font->y_ppem = y_ppem;
 }
 
 /**
  * hb_font_get_ppem:
  * @font: a font.
@@ -1598,17 +1598,17 @@ static void
   if (font->num_coords == coords_length &&
       (coords_length == 0 ||
        0 == memcmp (font->coords, coords, coords_length * sizeof (coords[0]))))
   {
     free (coords);
     return;
   }
 
-  font->dirty |= font->VARIATIONS;
+  font->dirty |= font->DIRTY_VARIATIONS;
 
   free (font->coords);
 
   font->coords = coords;
   font->num_coords = coords_length;
 }
 
 /**
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -488,17 +488,17 @@ reference_table  (hb_face_t *face HB_UNU
 
   /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
   if (error)
     return NULL;
 
   buffer = (FT_Byte *) malloc (length);
-  if (buffer == NULL)
+  if (!buffer)
     return NULL;
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
   if (error)
     return NULL;
 
   return hb_blob_create ((const char *) buffer, length,
 			 HB_MEMORY_MODE_WRITABLE,
@@ -516,17 +516,17 @@ reference_table  (hb_face_t *face HB_UNU
  * Since: 0.9.2
  **/
 hb_face_t *
 hb_ft_face_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy)
 {
   hb_face_t *face;
 
-  if (ft_face->stream->read == NULL) {
+  if (!ft_face->stream->read) {
     hb_blob_t *blob;
 
     blob = hb_blob_create ((const char *) ft_face->stream->base,
 			   (unsigned int) ft_face->stream->size,
 			   HB_MEMORY_MODE_READONLY,
 			   ft_face, destroy);
     face = hb_face_create (blob, ft_face->face_index);
     hb_blob_destroy (blob);
@@ -627,19 +627,19 @@ hb_ft_font_create (FT_Face           ft_
     {
       if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
       {
 	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
 	  coords[i] = ft_coords[i] >>= 2;
 
 	hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
       }
-      free (coords);
-      free (ft_coords);
     }
+    free (coords);
+    free (ft_coords);
     free (mm_var);
   }
 #endif
 
   return font;
 }
 
 /**
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -350,27 +350,28 @@ hb_bool_t
 
   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;
+      info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
       info->var1.i32 = clusters[i].advance;     // all glyphs in the cluster get the same advance
     }
   }
   buffer->len = glyph_count;
 
   unsigned int upem = hb_face_get_upem (face);
   float xscale = (float) font->x_scale / upem;
   float yscale = (float) font->y_scale / upem;
   yscale *= yscale / xscale;
   /* Positioning. */
-  int currclus = -1;
+  unsigned int currclus = (unsigned int) -1;
   const hb_glyph_info_t *info = buffer->info;
   hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
   if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
     curradvx = 0;
     for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
     {
       pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx;
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -219,17 +219,17 @@ struct hb_ot_face_glyf_accelerator_t
 struct hb_ot_face_cbdt_accelerator_t
 {
   hb_blob_t *cblc_blob;
   hb_blob_t *cbdt_blob;
   const OT::CBLC *cblc;
   const OT::CBDT *cbdt;
 
   unsigned int cbdt_len;
-  float upem;
+  unsigned int upem;
 
   inline void init (hb_face_t *face)
   {
     upem = face->get_upem();
 
     cblc_blob = OT::Sanitizer<OT::CBLC>::sanitize (face->reference_table (HB_OT_TAG_CBLC));
     cbdt_blob = OT::Sanitizer<OT::CBDT>::sanitize (face->reference_table (HB_OT_TAG_CBDT));
     cbdt_len = hb_blob_get_length (cbdt_blob);
@@ -249,21 +249,21 @@ struct hb_ot_face_cbdt_accelerator_t
     hb_blob_destroy (this->cblc_blob);
     hb_blob_destroy (this->cbdt_blob);
   }
 
   inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
   {
     unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
 
-    if (cblc == NULL)
+    if (!cblc)
       return false;  // Not a color bitmap font.
 
     const OT::IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
-    if (subtable_record == NULL)
+    if (!subtable_record || !x_ppem || !y_ppem)
       return false;
 
     if (subtable_record->get_extents (extents))
       return true;
 
     unsigned int image_offset = 0, image_length = 0, image_format = 0;
     if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
       return false;
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -936,17 +936,17 @@ struct Coverage
     switch (u.format) {
     case 1: u.format1.add_coverage (glyphs); break;
     case 2: u.format2.add_coverage (glyphs); break;
     default:                                 break;
     }
   }
 
   struct Iter {
-    Iter (void) : format (0) {};
+    Iter (void) : format (0), u () {};
     inline void init (const Coverage &c_) {
       format = c_.u.format;
       switch (format) {
       case 1: u.format1.init (c_.u.format1); return;
       case 2: u.format2.init (c_.u.format2); return;
       default:                               return;
       }
     }
@@ -977,18 +977,18 @@ struct Coverage
       case 2: return u.format2.get_coverage ();
       default:return -1;
       }
     }
 
     private:
     unsigned int format;
     union {
+    CoverageFormat2::Iter	format2; /* Put this one first since it's larger; helps shut up compiler. */
     CoverageFormat1::Iter	format1;
-    CoverageFormat2::Iter	format2;
     } u;
   };
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   CoverageFormat1	format1;
   CoverageFormat2	format2;
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -427,16 +427,17 @@ struct MarkArray : ArrayOf<MarkRecord>	/
     bool found;
     const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
     /* If this subtable doesn't have an anchor for this base and this class,
      * return false such that the subsequent subtables have a chance at it. */
     if (unlikely (!found)) return_trace (false);
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
+    buffer->unsafe_to_break (glyph_pos, buffer->idx);
     mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
     hb_glyph_position_t &o = buffer->cur_pos();
     o.x_offset = base_x - mark_x;
     o.y_offset = base_y - mark_y;
     o.attach_type() = ATTACH_TYPE_MARK;
     o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
@@ -638,16 +639,17 @@ struct PairSet
       const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
       hb_codepoint_t mid_x = record->secondGlyph;
       if (x < mid_x)
         max = mid - 1;
       else if (x > mid_x)
         min = mid + 1;
       else
       {
+        buffer->unsafe_to_break (buffer->idx, pos + 1);
 	valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
 	valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
 	if (len2)
 	  pos++;
 	buffer->idx = pos;
 	return_trace (true);
       }
     }
@@ -785,16 +787,17 @@ struct PairPosFormat2
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int record_len = len1 + len2;
 
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
+    buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c, this, v, buffer->cur_pos());
     valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
 
     buffer->idx = skippy_iter.idx;
     if (len2)
       buffer->idx++;
 
@@ -924,16 +927,17 @@ struct CursivePosFormat1
     if (!skippy_iter.next ()) return_trace (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
     if (!next_record.entryAnchor) return_trace (false);
 
     unsigned int i = buffer->idx;
     unsigned int j = skippy_iter.idx;
 
+    buffer->unsafe_to_break (i, j);
     hb_position_t entry_x, entry_y, exit_x, exit_y;
     (this+this_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
     (this+next_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
 
     hb_glyph_position_t *pos = buffer->pos;
 
     hb_position_t d;
     /* Main-direction adjustment */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -1009,24 +1009,27 @@ struct ReverseChainSingleSubstFormat1
       return_trace (false); /* No chaining to this type */
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
 
+  unsigned int start_index = 0, end_index = 0;
     if (match_backtrack (c,
 			 backtrack.len, (USHORT *) backtrack.array,
-			 match_coverage, this) &&
+			 match_coverage, this,
+			 &start_index) &&
         match_lookahead (c,
 			 lookahead.len, (USHORT *) lookahead.array,
 			 match_coverage, this,
-			 1))
+			 1, &end_index))
     {
+      c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
       c->replace_glyph_inplace (substitute[index]);
       /* Note: We DON'T decrease buffer->idx.  The main loop does it
        * for us.  This is useful for preventing surprises if someone
        * calls us through a Context lookup. */
       return_trace (true);
     }
 
     return_trace (false);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -886,48 +886,54 @@ static inline bool ligate_input (hb_appl
   }
   return_trace (true);
 }
 
 static inline bool match_backtrack (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT backtrack[],
 				    match_func_t match_func,
-				    const void *match_data)
+				    const void *match_data,
+				    unsigned int *match_start)
 {
   TRACE_APPLY (NULL);
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->backtrack_len (), count);
   skippy_iter.set_match_func (match_func, match_data, backtrack);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.prev ())
       return_trace (false);
 
+  *match_start = skippy_iter.idx;
+
   return_trace (true);
 }
 
 static inline bool match_lookahead (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT lookahead[],
 				    match_func_t match_func,
 				    const void *match_data,
-				    unsigned int offset)
+				    unsigned int offset,
+				    unsigned int *end_index)
 {
   TRACE_APPLY (NULL);
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->idx + offset - 1, count);
   skippy_iter.set_match_func (match_func, match_data, lookahead);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.next ())
       return_trace (false);
 
+  *end_index = skippy_iter.idx + 1;
+
   return_trace (true);
 }
 
 
 
 struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1140,20 +1146,21 @@ static inline bool context_apply_lookup 
 					 ContextApplyLookupContext &lookup_context)
 {
   unsigned int match_length = 0;
   unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   return match_input (c,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data,
 		      &match_length, match_positions)
-      && apply_lookup (c,
+      && (c->buffer->unsafe_to_break (c->buffer->idx, c->buffer->idx + match_length),
+	  apply_lookup (c,
 		       inputCount, match_positions,
 		       lookupCount, lookupRecord,
-		       match_length);
+		       match_length));
 }
 
 struct Rule
 {
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
@@ -1661,33 +1668,35 @@ static inline bool chain_context_apply_l
 					       unsigned int inputCount, /* Including the first glyph (not matched) */
 					       const USHORT input[], /* Array of input values--start with second glyph */
 					       unsigned int lookaheadCount,
 					       const USHORT lookahead[],
 					       unsigned int lookupCount,
 					       const LookupRecord lookupRecord[],
 					       ChainContextApplyLookupContext &lookup_context)
 {
-  unsigned int match_length = 0;
+  unsigned int start_index = 0, match_length = 0, end_index = 0;
   unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   return match_input (c,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data[1],
 		      &match_length, match_positions)
       && match_backtrack (c,
 			  backtrackCount, backtrack,
-			  lookup_context.funcs.match, lookup_context.match_data[0])
+			  lookup_context.funcs.match, lookup_context.match_data[0],
+			  &start_index)
       && match_lookahead (c,
 			  lookaheadCount, lookahead,
 			  lookup_context.funcs.match, lookup_context.match_data[2],
-			  match_length)
-      && apply_lookup (c,
+			  match_length, &end_index)
+      && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
+          apply_lookup (c,
 		       inputCount, match_positions,
 		       lookupCount, lookupRecord,
-		       match_length);
+		       match_length));
 }
 
 struct ChainRule
 {
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -192,18 +192,17 @@ HB_INTERNAL void
 #define unicode_props()		var2.u16[0]
 
 /* 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 */
-
+/* Loop over syllables. Based on foreach_cluster(). */
 #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
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -133,17 +133,21 @@ void hb_ot_map_builder_t::add_pause (uns
   current_stage[table_index]++;
 }
 
 void
 hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
 			      const int    *coords,
 			      unsigned int  num_coords)
 {
-  m.global_mask = 1;
+  ASSERT_STATIC (!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1)));
+  unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
+  unsigned int global_bit_shift = _hb_popcount32 (HB_GLYPH_FLAG_DEFINED);
+
+  m.global_mask = global_bit_mask;
 
   unsigned int required_feature_index[2];
   hb_tag_t required_feature_tag[2];
   /* We default to applying required feature in stage 0.  If the required
    * feature has a tag that is known to the shaper, we apply required feature
    * in the stage for that tag.
    */
   unsigned int required_feature_stage[2] = {0, 0};
@@ -185,17 +189,18 @@ hb_ot_map_builder_t::compile (hb_ot_map_
 	feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]);
 	feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]);
       }
     feature_infos.shrink (j + 1);
   }
 
 
   /* Allocate bits now */
-  unsigned int next_bit = 1;
+  unsigned int next_bit = global_bit_shift + 1;
+
   for (unsigned int i = 0; i < feature_infos.len; i++)
   {
     const feature_info_t *info = &feature_infos[i];
 
     unsigned int bits_needed;
 
     if ((info->flags & F_GLOBAL) && info->max_value == 1)
       /* Uses the global bit */
@@ -244,18 +249,18 @@ hb_ot_map_builder_t::compile (hb_ot_map_
     map->index[0] = feature_index[0];
     map->index[1] = feature_index[1];
     map->stage[0] = info->stage[0];
     map->stage[1] = info->stage[1];
     map->auto_zwnj = !(info->flags & F_MANUAL_ZWNJ);
     map->auto_zwj = !(info->flags & F_MANUAL_ZWJ);
     if ((info->flags & F_GLOBAL) && info->max_value == 1) {
       /* Uses the global bit */
-      map->shift = 0;
-      map->mask = 1;
+      map->shift = global_bit_shift;
+      map->mask = global_bit_mask;
     } else {
       map->shift = next_bit;
       map->mask = (1u << (next_bit + bits_needed)) - (1u << next_bit);
       next_bit += bits_needed;
       m.global_mask |= (info->default_value << map->shift) & map->mask;
     }
     map->_1_mask = (1u << map->shift) & map->mask;
     map->needs_fallback = !found;
@@ -282,17 +287,17 @@ hb_ot_map_builder_t::compile (hb_ot_map_
     unsigned int last_num_lookups = 0;
     for (unsigned stage = 0; stage < current_stage[table_index]; stage++)
     {
       if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX &&
 	  required_feature_stage[table_index] == stage)
 	add_lookups (m, face, table_index,
 		     required_feature_index[table_index],
 		     variations_index,
-		     1 /* mask */);
+		     global_bit_mask);
 
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
 	  add_lookups (m, face, table_index,
 		       m.features[i].index[table_index],
 		       variations_index,
 		       m.features[i].mask,
 		       m.features[i].auto_zwnj,
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -316,17 +316,20 @@ arabic_joining (hb_buffer_t *buffer)
     if (unlikely (this_type == JOINING_TYPE_T)) {
       info[i].arabic_shaping_action() = NONE;
       continue;
     }
 
     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
 
     if (entry->prev_action != NONE && prev != (unsigned int) -1)
+    {
       info[prev].arabic_shaping_action() = entry->prev_action;
+      buffer->unsafe_to_break (prev, i + 1);
+    }
 
     info[i].arabic_shaping_action() = entry->curr_action;
 
     prev = i;
     state = entry->next_state;
   }
 
   for (unsigned int i = 0; i < buffer->context_len[1]; i++)
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -197,16 +197,17 @@ preprocess_text_hangul (const hb_ot_shap
       /*
        * 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->unsafe_to_break_from_outbuffer (start, buffer->idx);
 	buffer->next_glyph ();
 	if (!is_zero_width_char (font, u))
 	{
 	  buffer->merge_out_clusters (start, end + 1);
 	  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;
@@ -253,16 +254,17 @@ preprocess_text_hangul (const hb_ot_shap
 	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. */
 	}
+	buffer->unsafe_to_break (buffer->idx, buffer->idx + (t ? 3 : 2));
 
 	/* 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))
 	  {
@@ -317,16 +319,18 @@ preprocess_text_hangul (const hb_ot_shap
 	if (font->has_glyph (new_s))
 	{
 	  buffer->replace_glyphs (2, 1, &new_s);
 	  if (unlikely (buffer->in_error))
 	    return;
 	  end = start + 1;
 	  continue;
 	}
+	else
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
       }
 
       /* 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 &&
@@ -363,16 +367,18 @@ preprocess_text_hangul (const hb_ot_shap
 	  info[i++].hangul_shaping_feature() = LJMO;
 	  info[i++].hangul_shaping_feature() = VJMO;
 	  if (i < end)
 	    info[i++].hangul_shaping_feature() = TJMO;
 	  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
 	    buffer->merge_out_clusters (start, end);
 	  continue;
 	}
+	else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint)))
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
       }
 
       if (has_glyph)
       {
         /* We didn't decompose the S, so just advance past it. */
 	end = start + 1;
 	buffer->next_glyph ();
 	continue;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -116,17 +116,17 @@ enum indic_syllabic_category_t {
   INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER		= OT_C,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER		= OT_M, /* U+17CD only. */
   INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL		= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER		= OT_PLACEHOLDER,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA	= OT_Repha,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED		= OT_X, /* Don't care. */
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED		= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	= OT_N,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_Repha, /* TODO */
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_C,
   INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		= OT_SM,
   INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER		= OT_Coeng,
   INDIC_SYLLABIC_CATEGORY_JOINER			= OT_ZWJ,
   INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER		= OT_X,
   INDIC_SYLLABIC_CATEGORY_NON_JOINER			= OT_ZWNJ,
   INDIC_SYLLABIC_CATEGORY_NUKTA				= OT_N,
   INDIC_SYLLABIC_CATEGORY_NUMBER			= OT_PLACEHOLDER,
   INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER			= OT_PLACEHOLDER, /* Don't care. */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -619,16 +619,18 @@ setup_masks_indic (const hb_ot_shape_pla
 }
 
 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);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
 }
 
 static int
 compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   int a = pa->indic_position();
   int b = pb->indic_position();
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -292,16 +292,18 @@ setup_masks_myanmar (const hb_ot_shape_p
 }
 
 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);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
 }
 
 static int
 compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   int a = pa->myanmar_position();
   int b = pb->myanmar_position();
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -239,16 +239,17 @@ do_thai_pua_shaping (const hb_ot_shape_p
     const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt];
     const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt];
     above_state = above_edge.next_state;
     below_state = below_edge.next_state;
 
     /* At least one of the above/below actions is NOP. */
     thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action;
 
+    buffer->unsafe_to_break (base, i);
     if (action == RD)
       info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font);
     else
       info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font);
   }
 }
 
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -349,16 +349,18 @@ setup_topographical_masks (const hb_ot_s
 }
 
 static void
 setup_syllables (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
   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)
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -302,16 +302,19 @@ position_mark (const hb_ot_shape_plan_t 
 static inline void
 position_around_base (const hb_ot_shape_plan_t *plan,
 		      hb_font_t *font,
 		      hb_buffer_t  *buffer,
 		      unsigned int base,
 		      unsigned int end)
 {
   hb_direction_t horiz_dir = HB_DIRECTION_INVALID;
+
+  buffer->unsafe_to_break (base, end);
+
   hb_glyph_extents_t base_extents;
   if (!font->get_glyph_extents (buffer->info[base].codepoint,
 				&base_extents))
   {
     /* If extents don't work, zero marks and go home. */
     zero_mark_advances (buffer, base + 1, end);
     return;
   }
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -270,34 +270,39 @@ 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->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
-      buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
     return;
 
   /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
   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])) &&
 		!_hb_glyph_info_is_joiner (&info[i])))
     {
-      buffer->merge_clusters (base, i);
+      if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	buffer->merge_clusters (base, i);
+      else
+	buffer->unsafe_to_break (base, i);
       base = i;
     }
   }
-  buffer->merge_clusters (base, count);
+  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    buffer->merge_clusters (base, count);
+  else
+    buffer->unsafe_to_break (base, count);
 }
 
 static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
 
   /* TODO vertical:
@@ -389,16 +394,18 @@ hb_ot_shape_setup_masks_fraction (hb_ot_
 	     _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++;
 
+      buffer->unsafe_to_break (start, end);
+
       for (unsigned int j = start; j < i; j++)
         info[j].mask |= pre_mask;
       info[i].mask |= c->plan->frac_mask;
       for (unsigned int j = i + 1; j < end; j++)
         info[j].mask |= post_mask;
 
       i = end - 1;
     }
@@ -504,19 +511,20 @@ hb_ot_hide_default_ignorables (hb_ot_sha
 	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 mask = info[i].mask;
 	    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;
+	      buffer->set_cluster (info[k - 1], cluster, mask);
 	  }
 	  continue;
 	}
 
 	if (i + 1 < count)
 	  buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
 
 	continue;
@@ -572,18 +580,16 @@ 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;
 
-  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);
 
@@ -777,16 +783,41 @@ hb_ot_position (hb_ot_shape_context_t *c
   /* Visual fallback goes here. */
 
   if (c->fallback_positioning)
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
+static inline void
+hb_propagate_flags (hb_buffer_t *buffer)
+{
+  /* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
+   * Simplifies using them. */
+
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
+    return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_cluster (buffer, start, end)
+  {
+    unsigned int mask = 0;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+      {
+	 mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+	 break;
+      }
+    if (mask)
+      for (unsigned int i = start; i < end; i++)
+	info[i].mask |= mask;
+  }
+}
 
 /* Pull it all together! */
 
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
   c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
@@ -803,33 +834,37 @@ hb_ot_shape_internal (hb_ot_shape_contex
 
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
   _hb_buffer_allocate_unicode_vars (c->buffer);
 
   c->buffer->clear_output ();
 
+  hb_ot_shape_initialize_masks (c);
   hb_set_unicode_props (c->buffer);
   hb_insert_dotted_circle (c->buffer, c->font);
+
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
 
   if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
 
   hb_ot_substitute (c);
   hb_ot_position (c);
 
   hb_ot_hide_default_ignorables (c);
 
   if (c->plan->shaper->postprocess_glyphs)
     c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
 
+  hb_propagate_flags (c->buffer);
+
   _hb_buffer_deallocate_unicode_vars (c->buffer);
 
   c->buffer->props.direction = c->target_direction;
 
   c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
   c->buffer->deallocate_var_all ();
 }
 
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -155,17 +155,17 @@ hb_shape_plan_create2 (hb_face_t        
     free (coords);
     free (features);
     return hb_shape_plan_get_empty ();
   }
 
   assert (props->direction != HB_DIRECTION_INVALID);
 
   hb_face_make_immutable (face);
-  shape_plan->default_shaper_list = shaper_list == NULL;
+  shape_plan->default_shaper_list = !shaper_list;
   shape_plan->face_unsafe = face;
   shape_plan->props = *props;
   shape_plan->num_user_features = num_user_features;
   shape_plan->user_features = features;
   if (num_user_features)
     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
   shape_plan->num_coords = num_coords;
   shape_plan->coords = coords;
@@ -418,17 +418,17 @@ hb_shape_plan_coords_match (const hb_sha
 
 static hb_bool_t
 hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
 		       const hb_shape_plan_proposal_t *proposal)
 {
   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
 	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
 	 hb_shape_plan_coords_match (shape_plan, proposal) &&
-	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
+	 ((shape_plan->default_shaper_list && !proposal->shaper_list) ||
 	  (shape_plan->shaper_func == proposal->shaper_func));
 }
 
 static inline hb_bool_t
 hb_non_global_user_features_present (const hb_feature_t *user_features,
 				     unsigned int        num_user_features)
 {
   while (num_user_features) {
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -100,16 +100,20 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
     decomposed[ret] = 0;
     return ret;
   }
 
 
   inline unsigned int
   modified_combining_class (hb_codepoint_t unicode)
   {
+    /* XXX This hack belongs to the Arabic shaper:
+     * Put HAMZA ABOVE in the same class as SHADDA. */
+    if (unlikely (unicode == 0x0654u)) unicode = 0x0651u;
+
     /* XXX This hack belongs to the Myanmar shaper. */
     if (unlikely (unicode == 0x1037u)) unicode = 0x103Au;
 
     /* XXX This hack belongs to the SEA shaper (for Tai Tham):
      * Reorder SAKOT to ensure it comes after any tone marks. */
     if (unlikely (unicode == 0x1A60u)) return 254;
 
     /* XXX This hack belongs to the Tibetan shaper:
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -1019,16 +1019,18 @@ retry:
   {
     hb_glyph_info_t *info = &buffer->info[i];
     hb_glyph_position_t *pos = &buffer->pos[i];
 
     /* TODO vertical */
     pos->x_advance = x_mult * (int32_t) info->mask;
     pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32);
     pos->y_offset = y_mult * info->var2.i32;
+
+    info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 
   if (backward)
     hb_buffer_reverse (buffer);
 
   /* Wow, done! */
   return true;
 }
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -32,20 +32,20 @@
 #define HB_VERSION_H
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
-#define HB_VERSION_MINOR 4
-#define HB_VERSION_MICRO 8
+#define HB_VERSION_MINOR 5
+#define HB_VERSION_MICRO 1
 
-#define HB_VERSION_STRING "1.4.8"
+#define HB_VERSION_STRING "1.5.1"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,