Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 03 Aug 2015 15:02:19 -0400
changeset 287559 abc56d57f6e1aebade48949fb557d26eae555df8
parent 287509 56f1475f352e05f401d65cc626e14b01f9cb4af4 (current diff)
parent 287558 43b640c7daaf5dad4719a820eceacbb88f1ba71c (diff)
child 287560 14e1c43f09e83724ac5f1dbd11c051aedda0d328
child 287583 d191709cd9331027bd000b77c7db93f85a12392c
child 287635 7b898ef15bf491c18f14d7fe7b07cfa8ddbfb45b
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
dom/push/test/test_push_manager_worker.html
gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
js/src/prmjtime.cpp
js/src/prmjtime.h
mobile/android/tests/browser/robocop/testAboutLogins.java
mobile/android/tests/browser/robocop/testAboutLogins.js
mobile/android/tests/browser/robocop/testAppConstants.java
mobile/android/tests/browser/robocop/testAppConstants.js
security/manager/ssl/tests/unit/test_keysize/cert9.db
security/manager/ssl/tests/unit/test_keysize/key4.db
security/manager/ssl/tests/unit/test_keysize/pkcs11.txt
testing/docker/ubuntu-build/REPOSITORY
testing/docker/ubuntu32-build/REPOSITORY
testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled.html
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1011,17 +1011,16 @@ pref("network.proxy.pac_generator", true
 // comma.  Specify '*' in the list to apply to all apps.
 pref("network.proxy.browsing.app_origins", "app://system.gaiamobile.org");
 
 // Enable Web Speech synthesis API
 pref("media.webspeech.synth.enabled", true);
 
 // Enable Web Speech recognition API
 pref("media.webspeech.recognition.enable", true);
-pref("media.webspeech.service.default", "pocketsphinx");
 
 // Downloads API
 pref("dom.mozDownloads.enabled", true);
 pref("dom.downloads.max_retention_days", 7);
 
 // External Helper Application Handling
 //
 // All external helper application handling can require the docshell to be
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -7208,17 +7208,17 @@ HTMLInputElement::SetFilePickerFiltersFr
       filterMask = nsIFilePicker::filterAudio;
       filterBundle->GetStringFromName(MOZ_UTF16("audioFilter"),
                                       getter_Copies(extensionListStr));
     } else if (token.EqualsLiteral("video/*")) {
       filterMask = nsIFilePicker::filterVideo;
       filterBundle->GetStringFromName(MOZ_UTF16("videoFilter"),
                                       getter_Copies(extensionListStr));
     } else if (token.First() == '.') {
-      if (token.FindChar(';') >= 0  || token.FindChar('*') >= 0) {
+      if (token.Contains(';') || token.Contains('*')) {
         // Ignore this filter as it contains reserved characters
         continue;
       }
       extensionListStr = NS_LITERAL_STRING("*") + token;
       filterName = extensionListStr;
       atLeastOneFileExtensionFilter = true;
     } else {
       //... if no image/audio/video filter is found, check mime types filters
--- a/dom/media/webspeech/recognition/PocketSphinxSpeechRecognitionService.cpp
+++ b/dom/media/webspeech/recognition/PocketSphinxSpeechRecognitionService.cpp
@@ -49,23 +49,26 @@ public:
                                    // thread!
 
     // Declare javascript result events
     nsRefPtr<SpeechEvent> event = new SpeechEvent(
       mRecognition, SpeechRecognition::EVENT_RECOGNITIONSERVICE_FINAL_RESULT);
     SpeechRecognitionResultList* resultList =
       new SpeechRecognitionResultList(mRecognition);
     SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
-    SpeechRecognitionAlternative* alternative =
-      new SpeechRecognitionAlternative(mRecognition);
+    ErrorResult rv;
+    if (0 < mRecognition->GetMaxAlternatives(rv)) { // GetMaxAlternatives can't fail
+      SpeechRecognitionAlternative* alternative =
+        new SpeechRecognitionAlternative(mRecognition);
 
-    alternative->mTranscript = mResult;
-    alternative->mConfidence = 100;
+      alternative->mTranscript = mResult;
+      alternative->mConfidence = 100;
 
-    result->mItems.AppendElement(alternative);
+      result->mItems.AppendElement(alternative);
+    }
     resultList->mItems.AppendElement(result);
 
     event->mRecognitionResultList = resultList;
     NS_DispatchToMainThread(event);
 
     // If we don't destroy the thread when we're done with it, it will hang
     // around forever... bad!
     // But thread->Shutdown must be called from the main thread, not from the
@@ -325,21 +328,24 @@ PocketSphinxSpeechRecognitionService::Ob
 }
 
 SpeechRecognitionResultList*
 PocketSphinxSpeechRecognitionService::BuildMockResultList()
 {
   SpeechRecognitionResultList* resultList =
     new SpeechRecognitionResultList(mRecognition);
   SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
-  SpeechRecognitionAlternative* alternative =
-    new SpeechRecognitionAlternative(mRecognition);
+  ErrorResult rv;
+  if (0 < mRecognition->GetMaxAlternatives(rv)) { // GetMaxAlternatives can't fail
+    SpeechRecognitionAlternative* alternative =
+      new SpeechRecognitionAlternative(mRecognition);
 
-  alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
-  alternative->mConfidence = 0.0f;
+    alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
+    alternative->mConfidence = 0.0f;
 
-  result->mItems.AppendElement(alternative);
+    result->mItems.AppendElement(alternative);
+  }
   resultList->mItems.AppendElement(result);
 
   return resultList;
 }
 
 } // namespace mozilla
--- a/dom/media/webspeech/recognition/SpeechGrammarList.cpp
+++ b/dom/media/webspeech/recognition/SpeechGrammarList.cpp
@@ -18,40 +18,32 @@ namespace dom {
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SpeechGrammarList, mParent, mItems)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechGrammarList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechGrammarList)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechGrammarList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-SpeechGrammarList::SpeechGrammarList(nsISupports* aParent, nsISpeechRecognitionService* aRecognitionService)
+SpeechGrammarList::SpeechGrammarList(nsISupports* aParent)
   : mParent(aParent)
 {
-  this->mRecognitionService = aRecognitionService;
 }
 
 SpeechGrammarList::~SpeechGrammarList()
 {
 }
 
 already_AddRefed<SpeechGrammarList>
 SpeechGrammarList::Constructor(const GlobalObject& aGlobal,
                                ErrorResult& aRv)
 {
-  nsCOMPtr<nsISpeechRecognitionService> recognitionService;
-  recognitionService = GetSpeechRecognitionService();
-  if (!recognitionService) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return nullptr;
-  } else {
-    nsRefPtr<SpeechGrammarList> speechGrammarList =
-      new SpeechGrammarList(aGlobal.GetAsSupports(), recognitionService);
-    return speechGrammarList.forget();
-  }
+  nsRefPtr<SpeechGrammarList> speechGrammarList =
+    new SpeechGrammarList(aGlobal.GetAsSupports());
+  return speechGrammarList.forget();
 }
 
 JSObject*
 SpeechGrammarList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return SpeechGrammarListBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -86,17 +78,16 @@ SpeechGrammarList::AddFromURI(const nsAS
 void
 SpeechGrammarList::AddFromString(const nsAString& aString,
                                  const Optional<float>& aWeight,
                                  ErrorResult& aRv)
 {
   SpeechGrammar* speechGrammar = new SpeechGrammar(mParent);
   speechGrammar->SetSrc(aString, aRv);
   mItems.AppendElement(speechGrammar);
-  mRecognitionService->ValidateAndSetGrammarList(speechGrammar, nullptr);
   return;
 }
 
 already_AddRefed<SpeechGrammar>
 SpeechGrammarList::IndexedGetter(uint32_t aIndex, bool& aPresent,
                                  ErrorResult& aRv)
 {
   if (aIndex >= Length()) {
--- a/dom/media/webspeech/recognition/SpeechGrammarList.h
+++ b/dom/media/webspeech/recognition/SpeechGrammarList.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_SpeechGrammarList_h
 #define mozilla_dom_SpeechGrammarList_h
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "nsISpeechRecognitionService.h"
 
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
@@ -24,17 +23,17 @@ namespace dom {
 class GlobalObject;
 class SpeechGrammar;
 template<typename> class Optional;
 
 class SpeechGrammarList final : public nsISupports,
                                 public nsWrapperCache
 {
 public:
-  explicit SpeechGrammarList(nsISupports* aParent, nsISpeechRecognitionService* aRecognitionService);
+  explicit SpeechGrammarList(nsISupports* aParent);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SpeechGrammarList)
 
   static already_AddRefed<SpeechGrammarList> Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
   nsISupports* GetParentObject() const;
 
@@ -45,18 +44,16 @@ public:
   already_AddRefed<SpeechGrammar> Item(uint32_t aIndex, ErrorResult& aRv);
 
   void AddFromURI(const nsAString& aSrc, const Optional<float>& aWeight, ErrorResult& aRv);
 
   void AddFromString(const nsAString& aString, const Optional<float>& aWeight, ErrorResult& aRv);
 
   already_AddRefed<SpeechGrammar> IndexedGetter(uint32_t aIndex, bool& aPresent, ErrorResult& aRv);
 
-  nsCOMPtr<nsISpeechRecognitionService> mRecognitionService;
-
 private:
   ~SpeechGrammarList();
 
   nsCOMPtr<nsISupports> mParent;
 
   nsTArray<nsRefPtr<SpeechGrammar>> mItems;
 };
 
--- a/dom/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp
@@ -5,42 +5,46 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SpeechRecognition.h"
 
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Element.h"
 #include "mozilla/dom/SpeechRecognitionBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
 
 #include "AudioSegment.h"
 #include "endpointer.h"
 
 #include "mozilla/dom/SpeechRecognitionEvent.h"
+#include "nsIDocument.h"
 #include "nsIObserverService.h"
+#include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsQueryObject.h"
 
 #include <algorithm>
 
 // Undo the windows.h damage
 #if defined(XP_WIN) && defined(GetMessage)
 #undef GetMessage
 #endif
 
 namespace mozilla {
 namespace dom {
 
 #define PREFERENCE_DEFAULT_RECOGNITION_SERVICE "media.webspeech.service.default"
-#define DEFAULT_RECOGNITION_SERVICE "pocketsphinx"
+#define DEFAULT_RECOGNITION_SERVICE_PREFIX "pocketsphinx-"
+#define DEFAULT_RECOGNITION_SERVICE "pocketsphinx-en-US"
 
 #define PREFERENCE_ENDPOINTER_SILENCE_LENGTH "media.webspeech.silence_length"
 #define PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH "media.webspeech.long_silence_length"
 #define PREFERENCE_ENDPOINTER_LONG_SPEECH_LENGTH "media.webspeech.long_speech_length"
 
 static const uint32_t kSAMPLE_RATE = 16000;
 static const uint32_t kSPEECH_DETECTION_TIMEOUT_MS = 10000;
 
@@ -57,59 +61,68 @@ GetSpeechRecognitionLog()
     sLog = PR_NewLogModule("SpeechRecognition");
   }
 
   return sLog;
 }
 #define SR_LOG(...) MOZ_LOG(GetSpeechRecognitionLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 already_AddRefed<nsISpeechRecognitionService>
-GetSpeechRecognitionService()
+GetSpeechRecognitionService(const nsAString& aLang)
 {
   nsAutoCString speechRecognitionServiceCID;
 
   nsAdoptingCString prefValue =
   Preferences::GetCString(PREFERENCE_DEFAULT_RECOGNITION_SERVICE);
   nsAutoCString speechRecognitionService;
 
-  if (!prefValue.get() || prefValue.IsEmpty()) {
+  if (!aLang.IsEmpty()) {
+    speechRecognitionService =
+      NS_LITERAL_CSTRING(DEFAULT_RECOGNITION_SERVICE_PREFIX) +
+      NS_ConvertUTF16toUTF8(aLang);
+  } else if (!prefValue.IsEmpty()) {
+    speechRecognitionService = prefValue;
+  } else {
     speechRecognitionService = DEFAULT_RECOGNITION_SERVICE;
-  } else {
-    speechRecognitionService = prefValue;
   }
 
-  if (!SpeechRecognition::mTestConfig.mFakeRecognitionService){
+  if (SpeechRecognition::mTestConfig.mFakeRecognitionService) {
+    speechRecognitionServiceCID =
+      NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake";
+  } else {
     speechRecognitionServiceCID =
       NS_LITERAL_CSTRING(NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX) +
       speechRecognitionService;
-  } else {
-    speechRecognitionServiceCID =
-      NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake";
   }
 
   nsresult rv;
   nsCOMPtr<nsISpeechRecognitionService> recognitionService;
   recognitionService = do_GetService(speechRecognitionServiceCID.get(), &rv);
   return recognitionService.forget();
 }
 
-NS_INTERFACE_MAP_BEGIN(SpeechRecognition)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(SpeechRecognition, DOMEventTargetHelper, mDOMStream, mSpeechGrammarList)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SpeechRecognition)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 
 struct SpeechRecognition::TestConfig SpeechRecognition::mTestConfig;
 
 SpeechRecognition::SpeechRecognition(nsPIDOMWindow* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mEndpointer(kSAMPLE_RATE)
   , mAudioSamplesPerChunk(mEndpointer.FrameSize())
   , mSpeechDetectionTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
+  , mSpeechGrammarList(new SpeechGrammarList(GetParentObject()))
+  , mInterimResults(false)
+  , mMaxAlternatives(1)
 {
   SR_LOG("created SpeechRecognition");
 
   mTestConfig.Init();
   if (mTestConfig.mEnableTests) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     obs->AddObserver(this, SPEECH_RECOGNITION_TEST_EVENT_REQUEST_TOPIC, false);
     obs->AddObserver(this, SPEECH_RECOGNITION_TEST_END_TOPIC, false);
@@ -614,41 +627,38 @@ SpeechRecognition::ProcessTestEventReque
 
     // let the fake recognition service handle the request
   }
 
   return;
 }
 
 already_AddRefed<SpeechGrammarList>
-SpeechRecognition::GetGrammars(ErrorResult& aRv) const
+SpeechRecognition::Grammars() const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
+  nsRefPtr<SpeechGrammarList> speechGrammarList = mSpeechGrammarList;
+  return speechGrammarList.forget();
 }
 
 void
-SpeechRecognition::SetGrammars(SpeechGrammarList& aArg, ErrorResult& aRv)
+SpeechRecognition::SetGrammars(SpeechGrammarList& aArg)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return;
+  mSpeechGrammarList = &aArg;
 }
 
 void
-SpeechRecognition::GetLang(nsString& aRetVal, ErrorResult& aRv) const
+SpeechRecognition::GetLang(nsString& aRetVal) const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return;
+  aRetVal = mLang;
 }
 
 void
-SpeechRecognition::SetLang(const nsAString& aArg, ErrorResult& aRv)
+SpeechRecognition::SetLang(const nsAString& aArg)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return;
+  mLang = aArg;
 }
 
 bool
 SpeechRecognition::GetContinuous(ErrorResult& aRv) const
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return false;
 }
@@ -656,40 +666,38 @@ SpeechRecognition::GetContinuous(ErrorRe
 void
 SpeechRecognition::SetContinuous(bool aArg, ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return;
 }
 
 bool
-SpeechRecognition::GetInterimResults(ErrorResult& aRv) const
+SpeechRecognition::InterimResults() const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return false;
+  return mInterimResults;
 }
 
 void
-SpeechRecognition::SetInterimResults(bool aArg, ErrorResult& aRv)
+SpeechRecognition::SetInterimResults(bool aArg)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  mInterimResults = aArg;
   return;
 }
 
 uint32_t
 SpeechRecognition::GetMaxAlternatives(ErrorResult& aRv) const
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return 0;
+  return mMaxAlternatives;
 }
 
 void
 SpeechRecognition::SetMaxAlternatives(uint32_t aArg, ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  mMaxAlternatives = aArg;
   return;
 }
 
 void
 SpeechRecognition::GetServiceURI(nsString& aRetVal, ErrorResult& aRv) const
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return;
@@ -705,18 +713,21 @@ SpeechRecognition::SetServiceURI(const n
 void
 SpeechRecognition::Start(const Optional<NonNull<DOMMediaStream>>& aStream, ErrorResult& aRv)
 {
   if (mCurrentState != STATE_IDLE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  mRecognitionService = GetSpeechRecognitionService();
-  if (NS_WARN_IF(!mRecognitionService)) {
+  if (!SetRecognitionService(aRv)) {
+    return;
+  }
+
+  if (!ValidateAndSetGrammarList(aRv)) {
     return;
   }
 
   nsresult rv;
   rv = mRecognitionService->Initialize(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
@@ -734,16 +745,87 @@ SpeechRecognition::Start(const Optional<
                           new GetUserMediaSuccessCallback(this),
                           new GetUserMediaErrorCallback(this));
   }
 
   nsRefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_START);
   NS_DispatchToMainThread(event);
 }
 
+bool
+SpeechRecognition::SetRecognitionService(ErrorResult& aRv)
+{
+  // See: https://dvcs.w3.org/hg/speech-api/raw-file/tip/webspeechapi.html#dfn-lang
+  if (!mLang.IsEmpty()) {
+    mRecognitionService = GetSpeechRecognitionService(mLang);
+
+    if (!mRecognitionService) {
+      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return false;
+    }
+
+    return true;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if(!window) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+  nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
+  if(!document) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+  nsCOMPtr<Element> element = document->GetRootElement();
+  if(!element) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  nsAutoString lang;
+  element->GetLang(lang);
+  mRecognitionService = GetSpeechRecognitionService(lang);
+
+  if (!mRecognitionService) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+SpeechRecognition::ValidateAndSetGrammarList(ErrorResult& aRv)
+{
+  if (!mSpeechGrammarList) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  uint32_t grammarListLength = mSpeechGrammarList->Length();
+  if (0 == grammarListLength) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  for (uint32_t count = 0; count < grammarListLength; ++count) {
+    nsRefPtr<SpeechGrammar> speechGrammar = mSpeechGrammarList->Item(count, aRv);
+    if (aRv.Failed()) {
+      return false;
+    }
+    if (NS_FAILED(mRecognitionService->ValidateAndSetGrammarList(speechGrammar.get(), nullptr))) {
+      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void
 SpeechRecognition::Stop()
 {
   nsRefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_STOP);
   NS_DispatchToMainThread(event);
 }
 
 void
--- a/dom/media/webspeech/recognition/SpeechRecognition.h
+++ b/dom/media/webspeech/recognition/SpeechRecognition.h
@@ -44,54 +44,53 @@ namespace dom {
 #define SPEECH_RECOGNITION_TEST_END_TOPIC "SpeechRecognitionTest:End"
 
 class GlobalObject;
 class SpeechEvent;
 
 PRLogModuleInfo* GetSpeechRecognitionLog();
 #define SR_LOG(...) MOZ_LOG(GetSpeechRecognitionLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
-already_AddRefed<nsISpeechRecognitionService> GetSpeechRecognitionService();
-
 class SpeechRecognition final : public DOMEventTargetHelper,
                                 public nsIObserver,
                                 public SupportsWeakPtr<SpeechRecognition>
 {
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SpeechRecognition)
   explicit SpeechRecognition(nsPIDOMWindow* aOwnerWindow);
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SpeechRecognition, DOMEventTargetHelper)
 
   NS_DECL_NSIOBSERVER
 
   nsISupports* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static bool IsAuthorized(JSContext* aCx, JSObject* aGlobal);
 
   static already_AddRefed<SpeechRecognition>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
-  already_AddRefed<SpeechGrammarList> GetGrammars(ErrorResult& aRv) const;
+  already_AddRefed<SpeechGrammarList> Grammars() const;
 
-  void SetGrammars(mozilla::dom::SpeechGrammarList& aArg, ErrorResult& aRv);
+  void SetGrammars(mozilla::dom::SpeechGrammarList& aArg);
 
-  void GetLang(nsString& aRetVal, ErrorResult& aRv) const;
+  void GetLang(nsString& aRetVal) const;
 
-  void SetLang(const nsAString& aArg, ErrorResult& aRv);
+  void SetLang(const nsAString& aArg);
 
   bool GetContinuous(ErrorResult& aRv) const;
 
   void SetContinuous(bool aArg, ErrorResult& aRv);
 
-  bool GetInterimResults(ErrorResult& aRv) const;
+  bool InterimResults() const;
 
-  void SetInterimResults(bool aArg, ErrorResult& aRv);
+  void SetInterimResults(bool aArg);
 
   uint32_t GetMaxAlternatives(ErrorResult& aRv) const;
 
   void SetMaxAlternatives(uint32_t aArg, ErrorResult& aRv);
 
   void GetServiceURI(nsString& aRetVal, ErrorResult& aRv) const;
 
   void SetServiceURI(const nsAString& aArg, ErrorResult& aRv);
@@ -171,16 +170,19 @@ private:
     STATE_RECOGNIZING,
     STATE_WAITING_FOR_RESULT,
     STATE_COUNT
   };
 
   void SetState(FSMState state);
   bool StateBetween(FSMState begin, FSMState end);
 
+  bool SetRecognitionService(ErrorResult& aRv);
+  bool ValidateAndSetGrammarList(ErrorResult& aRv);
+
   class GetUserMediaSuccessCallback : public nsIDOMGetUserMediaSuccessCallback
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIDOMGETUSERMEDIASUCCESSCALLBACK
 
     explicit GetUserMediaSuccessCallback(SpeechRecognition* aRecognition)
       : mRecognition(aRecognition)
@@ -244,16 +246,42 @@ private:
   // buffer holds one chunk of mAudioSamplesPerChunk
   // samples before feeding it to mEndpointer
   nsRefPtr<SharedBuffer> mAudioSamplesBuffer;
   uint32_t mBufferedSamples;
 
   nsCOMPtr<nsITimer> mSpeechDetectionTimer;
   bool mAborted;
 
+  nsString mLang;
+
+  nsRefPtr<SpeechGrammarList> mSpeechGrammarList;
+
+  // WebSpeechAPI (http://bit.ly/1gIl7DC) states:
+  //
+  // 1. Default value MUST be false
+  // 2. If true, interim results SHOULD be returned
+  // 3. If false, interim results MUST NOT be returned
+  //
+  // Pocketsphinx does not return interm results; so, defaulting
+  // mInterimResults to false, then ignoring its subsequent value
+  // is a conforming implementation.
+  bool mInterimResults;
+
+  // WebSpeechAPI (http://bit.ly/1JAiqeo) states:
+  //
+  // 1. Default value is 1
+  // 2. Subsequent value is the "maximum number of SpeechRecognitionAlternatives per result"
+  //
+  // Pocketsphinx can only return at maximum a single SpeechRecognitionAlternative
+  // per SpeechRecognitionResult. So defaulting mMaxAlternatives to 1, for all non
+  // zero values ignoring mMaxAlternatives while for a 0 value returning no
+  // SpeechRecognitionAlternative per result is a conforming implementation.
+  uint32_t mMaxAlternatives;
+
   void ProcessTestEventRequest(nsISupports* aSubject, const nsAString& aEventName);
 
   const char* GetName(FSMState aId);
   const char* GetName(SpeechEvent* aId);
 };
 
 class SpeechEvent : public nsRunnable
 {
--- a/dom/media/webspeech/recognition/test/FakeSpeechRecognitionService.cpp
+++ b/dom/media/webspeech/recognition/test/FakeSpeechRecognitionService.cpp
@@ -97,20 +97,23 @@ FakeSpeechRecognitionService::Observe(ns
   return NS_OK;
 }
 
 SpeechRecognitionResultList*
 FakeSpeechRecognitionService::BuildMockResultList()
 {
   SpeechRecognitionResultList* resultList = new SpeechRecognitionResultList(mRecognition);
   SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
-  SpeechRecognitionAlternative* alternative = new SpeechRecognitionAlternative(mRecognition);
+  ErrorResult rv;
+  if (0 < mRecognition->GetMaxAlternatives(rv)) { // GetMaxAlternatives can't fail
+    SpeechRecognitionAlternative* alternative = new SpeechRecognitionAlternative(mRecognition);
 
-  alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
-  alternative->mConfidence = 0.0f;
+    alternative->mTranscript = NS_LITERAL_STRING("Mock final result");
+    alternative->mConfidence = 0.0f;
 
-  result->mItems.AppendElement(alternative);
+    result->mItems.AppendElement(alternative);
+  }
   resultList->mItems.AppendElement(result);
 
   return resultList;
 }
 
 } // namespace mozilla
--- a/dom/media/webspeech/recognition/test/head.js
+++ b/dom/media/webspeech/recognition/test/head.js
@@ -39,16 +39,21 @@ function EventManager(sr) {
   var eventDependencies = {
     "speechend": "speechstart",
     "soundend": "soundstart",
     "audioend": "audiostart"
   };
 
   var isDone = false;
 
+  // set up grammar
+  var sgl = new SpeechGrammarList();
+  sgl.addFromString("#JSGF V1.0; grammar test; public <simple> = hello ;", 1);
+  sr.grammars = sgl;
+
   // AUDIO_DATA events are asynchronous,
   // so we queue events requested while they are being
   // issued to make them seem synchronous
   var isSendingAudioData = false;
   var queuedEventRequests = [];
 
   // register default handlers
   for (var i = 0; i < allEvents.length; i++) {
--- a/dom/media/webspeech/recognition/test/test_audio_capture_error.html
+++ b/dom/media/webspeech/recognition/test/test_audio_capture_error.html
@@ -18,18 +18,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="text/javascript">
   SimpleTest.waitForExplicitFinish();
 
   performTest({
     eventsToRequest: ['EVENT_AUDIO_ERROR'],
     expectedEvents: {
+      'start': null,
+      'audiostart': null,
+      'speechstart': null,
+      'speechend': null,
+      'audioend': null,
       'error': buildErrorCallback(errorCodes.AUDIO_CAPTURE),
       'end': null
     },
     doneFunc: SimpleTest.finish,
-    prefs: [["media.webspeech.test.fake_fsm_events", true]]
+    prefs: [["media.webspeech.test.fake_fsm_events", true], ["media.webspeech.test.fake_recognition_service", true]]
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -459,23 +459,19 @@ public:
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
     nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
-      if (mEndpoint.IsEmpty()) {
-        promise->MaybeResolve(JS::NullHandleValue);
-      } else {
-        nsRefPtr<WorkerPushSubscription> sub =
-          new WorkerPushSubscription(mEndpoint, mScope);
-        promise->MaybeResolve(sub);
-      }
+      nsRefPtr<WorkerPushSubscription> sub =
+        new WorkerPushSubscription(mEndpoint, mScope);
+      promise->MaybeResolve(sub);
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
     proxy->CleanUp(aCx);
     return true;
   }
 private:
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -16,10 +16,8 @@ skip-if = os == "android" || toolkit == 
 [test_multiple_register_during_service_activation.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_unregister.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register_different_scope.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_try_registering_offline_disabled.html]
 skip-if = os == "android" || toolkit == "gonk"
-[test_push_manager_worker.html]
-skip-if = os == "android" || toolkit == "gonk"
deleted file mode 100644
--- a/dom/push/test/test_push_manager_worker.html
+++ /dev/null
@@ -1,101 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-Bug 1184574: Expose PushManager to workers.
-
-Any copyright is dedicated to the Public Domain.
-http://creativecommons.org/licenses/publicdomain/
-
--->
-<head>
-  <title>Test for Bug 1184574</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
-</head>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1184574">Mozilla Bug 1184574</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-
-<script class="testbody" type="text/javascript">
-
-  var registration;
-
-  function start() {
-    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
-    .then(swr => { registration = swr; return swr; });
-  }
-
-  function unregisterSW() {
-    return registration.unregister().then(function(result) {
-      ok(result, "Unregister should return true.");
-    }, function(e) {
-      dump("Unregistering the SW failed with " + e + "\n");
-    });
-  }
-
-  function setupPushNotification(swr) {
-    return swr.pushManager.subscribe().then(
-      pushSubscription => {
-        ok(true, "successful registered for push notification");
-        return pushSubscription;
-      }, error => {
-        ok(false, "could not register for push notification");
-      });
-  }
-
-  function getNewEndpointFromWorker(pushSubscription) {
-    return new Promise((resolve, reject) => {
-      var channel = new MessageChannel();
-      channel.port1.onmessage = e => {
-        (e.data.error ? reject : resolve)(e.data);
-      };
-      registration.active.postMessage({
-        endpoint: pushSubscription.endpoint,
-      }, [channel.port2]);
-    }).then(data => {
-      return registration.pushManager.getSubscription().then(
-        pushSubscription => {
-          is(data.endpoint, pushSubscription.endpoint,
-             "Wrong push endpoint in parent");
-          return pushSubscription;
-      });
-    });
-  }
-
-  function unregisterPushNotification(pushSubscription) {
-    return pushSubscription.unsubscribe().then(
-      result => {
-      ok(result, "unsubscribe() on existing subscription should return true.");
-      return pushSubscription;
-    }, error => {
-      ok(false, "unsubscribe() should never fail.");
-    });
-  }
-
-  function runTest() {
-    start()
-    .then(setupPushNotification)
-    .then(getNewEndpointFromWorker)
-    .then(unregisterPushNotification)
-    .then(unregisterSW)
-    .catch(function(e) {
-      ok(false, "Some test failed with error " + e);
-    }).then(SimpleTest.finish);
-  }
-
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.push.enabled", true],
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
-    ]}, runTest);
-  SpecialPowers.addPermission('push', true, document);
-  SimpleTest.waitForExplicitFinish();
-</script>
-</body>
-</html>
--- a/dom/push/test/worker.js
+++ b/dom/push/test/worker.js
@@ -1,19 +1,12 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
-// This worker is used for two types of tests. `handlePush` sends messages to
-// `frame.html`, which verifies that the worker can receive push messages.
-
-// `handleMessage` receives a message from `test_push_manager_worker.html`, and
-// verifies that `PushManager` can be used from the worker.
-
 this.onpush = handlePush;
-this.onmessage = handleMessage;
 
 function handlePush(event) {
 
   self.clients.matchAll().then(function(result) {
     if (event instanceof PushEvent &&
       event.data instanceof PushMessageData &&
       event.data.text === undefined &&
       event.data.json === undefined &&
@@ -21,31 +14,8 @@ function handlePush(event) {
       event.data.blob === undefined) {
 
       result[0].postMessage({type: "finished", okay: "yes"});
       return;
     }
     result[0].postMessage({type: "finished", okay: "no"});
   });
 }
-
-function handleMessage(event) {
-  self.registration.pushManager.getSubscription().then(subscription => {
-    if (subscription.endpoint != event.data.endpoint) {
-      throw new Error("Wrong push endpoint in worker");
-    }
-    return subscription.unsubscribe();
-  }).then(result => {
-    if (!result) {
-      throw new Error("Error dropping subscription in worker");
-    }
-    return self.registration.pushManager.getSubscription();
-  }).then(subscription => {
-    if (subscription) {
-      throw new Error("Subscription not dropped in worker");
-    }
-    return self.registration.pushManager.subscribe();
-  }).then(subscription => {
-    event.ports[0].postMessage({endpoint: subscription.endpoint});
-  }).catch(error => {
-    event.ports[0].postMessage({error: error});
-  });
-}
--- a/dom/svg/SVGContentUtils.h
+++ b/dom/svg/SVGContentUtils.h
@@ -34,16 +34,18 @@ class Element;
 class SVGSVGElement;
 } // namespace dom
 
 namespace gfx {
 class Matrix;
 } // namespace gfx
 } // namespace mozilla
 
+#define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512
+
 inline bool
 IsSVGWhitespace(char aChar)
 {
   return aChar == '\x20' || aChar == '\x9' ||
          aChar == '\xD'  || aChar == '\xA';
 }
 
 inline bool
--- a/dom/svg/SVGLineElement.cpp
+++ b/dom/svg/SVGLineElement.cpp
@@ -32,16 +32,33 @@ nsSVGElement::LengthInfo SVGLineElement:
 //----------------------------------------------------------------------
 // Implementation
 
 SVGLineElement::SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : SVGLineElementBase(aNodeInfo)
 {
 }
 
+void
+SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1,
+                                         float& aX2, float aY2)
+{
+  if (aX1 == aX2 && aY1 == aY2) {
+    SVGContentUtils::AutoStrokeOptions strokeOptions;
+    SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr,
+                                      SVGContentUtils::eIgnoreStrokeDashing);
+
+    if (strokeOptions.mLineCap != CapStyle::BUTT) {
+      float tinyLength =
+        strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
+      aX2 += tinyLength;
+    }
+  }
+}
+
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement)
 
 //----------------------------------------------------------------------
 
 already_AddRefed<SVGAnimatedLength>
@@ -107,25 +124,28 @@ SVGLineElement::GetMarkPoints(nsTArray<n
   aMarks->AppendElement(nsSVGMark(x2, y2, angle, nsSVGMark::eEnd));
 }
 
 void
 SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath)
 {
   float x1, y1, x2, y2;
   GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
+
+  MaybeAdjustForZeroLength(x1, y1, x2, y2);
   aSimplePath->SetLine(x1, y1, x2, y2);
 }
 
 already_AddRefed<Path>
 SVGLineElement::BuildPath(PathBuilder* aBuilder)
 {
   float x1, y1, x2, y2;
   GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
 
+  MaybeAdjustForZeroLength(x1, y1, x2, y2);
   aBuilder->MoveTo(Point(x1, y1));
   aBuilder->LineTo(Point(x2, y2));
 
   return aBuilder->Finish();
 }
 
 bool
 SVGLineElement::GetGeometryBounds(
--- a/dom/svg/SVGLineElement.h
+++ b/dom/svg/SVGLineElement.h
@@ -20,16 +20,20 @@ typedef nsSVGPathGeometryElement SVGLine
 
 class SVGLineElement final : public SVGLineElementBase
 {
 protected:
   explicit SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
   friend nsresult (::NS_NewSVGLineElement(nsIContent **aResult,
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
+  // If the input line has length zero and linecaps aren't butt, adjust |aX2| by
+  // a tiny amount to a barely-nonzero-length line that all of our draw targets
+  // will render
+  void MaybeAdjustForZeroLength(float aX1, float aY1, float& aX2, float aY2);
 
 public:
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
 
   // nsSVGPathGeometryElement methods:
   virtual bool IsMarkable() override { return true; }
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) override;
--- a/dom/svg/SVGPathData.cpp
+++ b/dom/svg/SVGPathData.cpp
@@ -250,17 +250,17 @@ ApproximateZeroLengthSubpathSquareCaps(P
   // not to.
   MOZ_ASSERT(aStrokeWidth > 0.0f,
              "Make the caller check for this, or check it here");
 
   // The fraction of the stroke width that we choose for the length of the
   // line is rather arbitrary, other than being chosen to meet the requirements
   // described in the comment above.
 
-  Float tinyLength = aStrokeWidth / 512;
+  Float tinyLength = aStrokeWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
 
   aPB->LineTo(aPoint + Point(tinyLength, 0));
   aPB->MoveTo(aPoint);
 }
 
 #define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT               \
   do {                                                                        \
     if (!subpathHasLength && hasLineCaps && aStrokeWidth > 0 &&               \
--- a/dom/webidl/SpeechRecognition.webidl
+++ b/dom/webidl/SpeechRecognition.webidl
@@ -10,23 +10,20 @@
  * liability, trademark and document use rules apply.
  */
 
 [Constructor,
  Pref="media.webspeech.recognition.enable",
  Func="SpeechRecognition::IsAuthorized"]
 interface SpeechRecognition : EventTarget {
     // recognition parameters
-    [Throws]
     attribute SpeechGrammarList grammars;
-    [Throws]
     attribute DOMString lang;
     [Throws]
     attribute boolean continuous;
-    [Throws]
     attribute boolean interimResults;
     [Throws]
     attribute unsigned long maxAlternatives;
     [Throws]
     attribute DOMString serviceURI;
 
     // methods to drive the speech interaction
     [Throws, UnsafeInPrerendering]
--- a/embedding/browser/nsWebBrowser.cpp
+++ b/embedding/browser/nsWebBrowser.cpp
@@ -1681,17 +1681,17 @@ nsWebBrowser::EnsureDocShellTreeOwner()
 
   return NS_OK;
 }
 
 static void
 DrawPaintedLayer(PaintedLayer* aLayer,
                  gfxContext* aContext,
                  const nsIntRegion& aRegionToDraw,
-                 const nsIntRegion& aDirtyRegion,
+                 const nsIntRegion* aDirtyRegion,
                  DrawRegionClip aClip,
                  const nsIntRegion& aRegionToInvalidate,
                  void* aCallbackData)
 {
   DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
 
   ColorPattern color(ToDeviceColor(*static_cast<nscolor*>(aCallbackData)));
   nsIntRect dirtyRect = aRegionToDraw.GetBounds();
--- a/gfx/2d/BasePoint.h
+++ b/gfx/2d/BasePoint.h
@@ -64,16 +64,20 @@ struct BasePoint {
   Sub operator/(T aScale) const {
     return Sub(x / aScale, y / aScale);
   }
 
   Sub operator-() const {
     return Sub(-x, -y);
   }
 
+  T DotProduct(const Sub& aPoint) const {
+      return x * aPoint.x + y * aPoint.y;
+  }
+
   T Length() const {
     return hypot(x, y);
   }
 
   // Round() is *not* rounding to nearest integer if the values are negative.
   // They are always rounding as floor(n + 0.5).
   // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
   Sub& Round() {
--- a/gfx/2d/BaseSize.h
+++ b/gfx/2d/BaseSize.h
@@ -25,16 +25,20 @@ struct BaseSize {
   MOZ_CONSTEXPR BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
 
   void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
 
   bool IsEmpty() const {
     return width == 0 || height == 0;
   }
 
+  bool IsSquare() const {
+    return width == height;
+  }
+
   // Note that '=' isn't defined so we'll get the
   // compiler generated default assignment operator
 
   bool operator==(const Sub& aSize) const {
     return width == aSize.width && height == aSize.height;
   }
   bool operator!=(const Sub& aSize) const {
     return width != aSize.width || height != aSize.height;
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -8,16 +8,90 @@
 
 #include "2D.h"
 #include "mozilla/Constants.h"
 #include "UserData.h"
 
 namespace mozilla {
 namespace gfx {
 
+// Kappa constant for 90-degree angle
+const Float kKappaFactor = 0.55191497064665766025f;
+
+// Calculate kappa constant for partial curve. The sign of angle in the
+// tangent will actually ensure this is negative for a counter clockwise
+// sweep, so changing signs later isn't needed.
+inline Float ComputeKappaFactor(Float aAngle)
+{
+  return (4.0f / 3.0f) * tanf(aAngle / 4.0f);
+}
+
+/**
+ * Draws a partial arc <= 90 degrees given exact start and end points.
+ * Assumes that it is continuing from an already specified start point.
+ */
+template <typename T>
+inline void PartialArcToBezier(T* aSink,
+                               const Size& aRadius,
+                               const Point& aStartPoint, const Point& aEndPoint,
+                               const Point& aStartOffset, const Point& aEndOffset,
+                               Float aKappaFactor = kKappaFactor)
+{
+  Float kappaX = aKappaFactor * aRadius.width;
+  Float kappaY = aKappaFactor * aRadius.height;
+
+  Point cp1 =
+    aStartPoint + Point(-aStartOffset.y * kappaX, aStartOffset.x * kappaY);
+
+  Point cp2 =
+    aEndPoint + Point(aEndOffset.y * kappaX, -aEndOffset.x * kappaY);
+
+  aSink->BezierTo(cp1, cp2, aEndPoint);
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ * Specialized version avoiding kappa calculation.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+                             const Point& aOrigin, const Size& aRadius,
+                             const Point& aStartPoint, const Point& aEndPoint,
+                             Float aKappaFactor = kKappaFactor)
+{
+  aSink->LineTo(aStartPoint);
+  if (!aRadius.IsEmpty()) {
+    Point startOffset = aStartPoint - aOrigin;
+    startOffset.x /= aRadius.width;
+    startOffset.y /= aRadius.height;
+    Point endOffset = aEndPoint - aOrigin;
+    endOffset.x /= aRadius.width;
+    endOffset.y /= aRadius.height;
+    PartialArcToBezier(aSink, aRadius,
+                       aStartPoint, aEndPoint,
+                       startOffset, endOffset,
+                       aKappaFactor);
+  } else if (aEndPoint != aStartPoint) {
+    aSink->LineTo(aEndPoint);
+  }
+}
+
+/**
+ * Draws an acute arc (<= 90 degrees) given exact start and end points.
+ */
+template <typename T>
+inline void AcuteArcToBezier(T* aSink,
+                             const Point& aOrigin, const Size& aRadius,
+                             const Point& aStartPoint, const Point& aEndPoint,
+                             Float aStartAngle, Float aEndAngle)
+{
+  AcuteArcToBezier(aSink, aOrigin, aRadius, aStartPoint, aEndPoint,
+                   ComputeKappaFactor(aEndAngle - aStartAngle));
+}
+
 template <typename T>
 void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
                  float aStartAngle, float aEndAngle, bool aAntiClockwise)
 {
   Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f;
 
   // Calculate the total arc we're going to sweep.
   Float arcSweepLeft = (aEndAngle - aStartAngle) * sweepDirection;
@@ -30,95 +104,70 @@ void ArcToBezier(T* aSink, const Point &
     // Recalculate the start angle to land closer to end angle.
     aStartAngle = aEndAngle - arcSweepLeft * sweepDirection;
   } else if (arcSweepLeft > Float(2.0f * M_PI)) {
     // Sweeping more than 2 * pi is a full circle.
     arcSweepLeft = Float(2.0f * M_PI);
   }
 
   Float currentStartAngle = aStartAngle;
-  Point currentStartPoint(aOrigin.x + cosf(aStartAngle) * aRadius.width,
-                          aOrigin.y + sinf(aStartAngle) * aRadius.height);
+  Point currentStartOffset(cosf(aStartAngle), sinf(aStartAngle));
+  Point currentStartPoint(aOrigin.x + currentStartOffset.x * aRadius.width,
+                          aOrigin.y + currentStartOffset.y * aRadius.height);
 
   aSink->LineTo(currentStartPoint);
 
   while (arcSweepLeft > 0) {
-    // We guarantee here the current point is the start point of the next
-    // curve segment.
     Float currentEndAngle =
       currentStartAngle + std::min(arcSweepLeft, Float(M_PI / 2.0f)) * sweepDirection;
 
-    Point currentEndPoint(aOrigin.x + cosf(currentEndAngle) * aRadius.width,
-                          aOrigin.y + sinf(currentEndAngle) * aRadius.height);
-
-    // Calculate kappa constant for partial curve. The sign of angle in the
-    // tangent will actually ensure this is negative for a counter clockwise
-    // sweep, so changing signs later isn't needed.
-    Float kappaFactor = (4.0f / 3.0f) * tanf((currentEndAngle - currentStartAngle) / 4.0f);
-    Float kappaX = kappaFactor * aRadius.width;
-    Float kappaY = kappaFactor * aRadius.height;
+    Point currentEndOffset(cosf(currentEndAngle), sinf(currentEndAngle));
+    Point currentEndPoint(aOrigin.x + currentEndOffset.x * aRadius.width,
+                          aOrigin.y + currentEndOffset.y * aRadius.height);
 
-    Point tangentStart(-sinf(currentStartAngle), cosf(currentStartAngle));
-    Point cp1 = currentStartPoint;
-    cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
+    PartialArcToBezier(aSink, aRadius,
+                       currentStartPoint, currentEndPoint,
+                       currentStartOffset, currentEndOffset,
+                       ComputeKappaFactor(currentEndAngle - currentStartAngle));
 
-    Point revTangentEnd(sinf(currentEndAngle), -cosf(currentEndAngle));
-    Point cp2 = currentEndPoint;
-    cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
-
-    aSink->BezierTo(cp1, cp2, currentEndPoint);
-
+    // We guarantee here the current point is the start point of the next
+    // curve segment.
     arcSweepLeft -= Float(M_PI / 2.0f);
     currentStartAngle = currentEndAngle;
+    currentStartOffset = currentEndOffset;
     currentStartPoint = currentEndPoint;
   }
 }
 
 /* This is basically the ArcToBezier with the parameters for drawing a circle
  * inlined which vastly simplifies it and avoids a bunch of transcedental function
  * calls which should make it faster. */
 template <typename T>
 void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
 {
-  Point startPoint(aOrigin.x + aRadius.width,
-                   aOrigin.y);
+  Point currentStartOffset(1, 0);
+  Point currentStartPoint(aOrigin.x + aRadius.width, aOrigin.y);
 
-  aSink->LineTo(startPoint);
+  aSink->LineTo(currentStartPoint);
 
-  // Calculate kappa constant for partial curve. The sign of angle in the
-  // tangent will actually ensure this is negative for a counter clockwise
-  // sweep, so changing signs later isn't needed.
-  Float kappaFactor = (4.0f / 3.0f) * tan((M_PI/2.0f) / 4.0f);
-  Float kappaX = kappaFactor * aRadius.width;
-  Float kappaY = kappaFactor * aRadius.height;
-  Float cosStartAngle = 1;
-  Float sinStartAngle = 0;
   for (int i = 0; i < 4; i++) {
+    // cos(x+pi/2) == -sin(x)
+    // sin(x+pi/2) == cos(x)
+    Point currentEndOffset(-currentStartOffset.y, currentStartOffset.x);
+    Point currentEndPoint(aOrigin.x + currentEndOffset.x * aRadius.width,
+                          aOrigin.y + currentEndOffset.y * aRadius.height);
+
+    PartialArcToBezier(aSink, aRadius,
+                       currentStartPoint, currentEndPoint,
+                       currentStartOffset, currentEndOffset);
+
     // We guarantee here the current point is the start point of the next
     // curve segment.
-    Point currentStartPoint(aOrigin.x + cosStartAngle * aRadius.width,
-                            aOrigin.y + sinStartAngle * aRadius.height);
-    Point currentEndPoint(aOrigin.x + -sinStartAngle * aRadius.width,
-                          aOrigin.y + cosStartAngle * aRadius.height);
-
-    Point tangentStart(-sinStartAngle, cosStartAngle);
-    Point cp1 = currentStartPoint;
-    cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
-
-    Point revTangentEnd(cosStartAngle, sinStartAngle);
-    Point cp2 = currentEndPoint;
-    cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
-
-    aSink->BezierTo(cp1, cp2, currentEndPoint);
-
-    // cos(x+pi/2) == -sin(x)
-    // sin(x+pi/2) == cos(x)
-    Float tmp = cosStartAngle;
-    cosStartAngle = -sinStartAngle;
-    sinStartAngle = tmp;
+    currentStartOffset = currentEndOffset;
+    currentStartPoint = currentEndPoint;
   }
 }
 
 /**
  * Appends a path represending a rectangle to the path being built by
  * aPathBuilder.
  *
  * aRect           The rectangle to append.
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -261,16 +261,24 @@ public:
   }
 
   uint32_t ToARGB() const
   {
     return uint32_t(b * 255.0f) | uint32_t(g * 255.0f) << 8 |
            uint32_t(r * 255.0f) << 16 | uint32_t(a * 255.0f) << 24;
   }
 
+  bool operator==(const Color& aColor) const {
+    return r == aColor.r && g == aColor.g && b == aColor.b && a == aColor.a;
+  }
+
+  bool operator!=(const Color& aColor) const {
+    return !(*this == aColor);
+  }
+
   Float r, g, b, a;
 };
 
 struct GradientStop
 {
   bool operator<(const GradientStop& aOther) const {
     return offset < aOther.offset;
   }
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -9,17 +9,17 @@ CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
 
 # The following warning options are useful for debugging: -Wpadded
 #AM_CXXFLAGS =
 
 # Convenience targets:
-lib: libharfbuzz.la
+lib: $(BUILT_SOURCES) libharfbuzz.la
 
 lib_LTLIBRARIES = libharfbuzz.la
 
 HBCFLAGS =
 HBLIBS =
 HBSOURCES =  \
 	hb-atomic-private.hh \
 	hb-blob.cc \
@@ -99,20 +99,22 @@ HBSOURCES += \
 	hb-ot-shape-complex-hangul.cc \
 	hb-ot-shape-complex-hebrew.cc \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.cc \
 	hb-ot-shape-complex-myanmar.cc \
 	hb-ot-shape-complex-myanmar-machine.hh \
-	hb-ot-shape-complex-sea.cc \
-	hb-ot-shape-complex-sea-machine.hh \
 	hb-ot-shape-complex-thai.cc \
 	hb-ot-shape-complex-tibetan.cc \
+	hb-ot-shape-complex-use.cc \
+	hb-ot-shape-complex-use-machine.hh \
+	hb-ot-shape-complex-use-private.hh \
+	hb-ot-shape-complex-use-table.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
 	hb-ot-shape-fallback-private.hh \
 	hb-ot-shape-fallback.cc \
 	hb-ot-shape-private.hh \
 	$(NULL)
 HBHEADERS += \
@@ -271,47 +273,52 @@ harfbuzz.def: $(HBHEADERS) $(HBNODISTHEA
 	) >"$@"
 	@ ! grep -q hb_ERROR "$@" \
 	|| ($(RM) "$@"; false)
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
+	gen-use-table.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
-unicode-tables: arabic-table indic-table
-
-indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
-	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \
-	|| ($(RM) hb-ot-shape-complex-indic-table.cc; false)
+unicode-tables: arabic-table indic-table use-table
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh \
 	|| ($(RM) hb-ot-shape-complex-arabic-table.hh; false)
 
+indic-table: gen-indic-table.py IndicSyllabicCategory-7.0.0.txt IndicMatraCategory-7.0.0.txt Blocks.txt
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \
+	|| ($(RM) hb-ot-shape-complex-indic-table.cc; false)
+
+use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-use-table.cc \
+	|| ($(RM) hb-ot-shape-complex-use-table.cc; false)
+
 built-sources: $(BUILT_SOURCES)
 
-.PHONY: unicode-tables arabic-table indic-table built-sources
+.PHONY: unicode-tables arabic-table indic-table use-table built-sources
 
 RAGEL_GENERATED = \
 	$(srcdir)/hb-buffer-deserialize-json.hh \
 	$(srcdir)/hb-buffer-deserialize-text.hh \
 	$(srcdir)/hb-ot-shape-complex-indic-machine.hh \
 	$(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \
-	$(srcdir)/hb-ot-shape-complex-sea-machine.hh \
+	$(srcdir)/hb-ot-shape-complex-use-machine.hh \
 	$(NULL)
 BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
 	hb-ot-shape-complex-indic-machine.rl \
 	hb-ot-shape-complex-myanmar-machine.rl \
-	hb-ot-shape-complex-sea-machine.rl \
+	hb-ot-shape-complex-use-machine.rl \
 	$(NULL)
 MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
 $(srcdir)/%.hh: $(srcdir)/%.rl
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
 
 noinst_PROGRAMS = \
 	main \
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -0,0 +1,476 @@
+#!/usr/bin/python
+
+import sys
+
+if len (sys.argv) != 5:
+	print >>sys.stderr, "usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt"
+	sys.exit (1)
+
+BLACKLISTED_BLOCKS = ["Thai", "Lao", "Tibetan"]
+
+files = [file (x) for x in sys.argv[1:]]
+
+headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 2]
+headers.append (["UnicodeData.txt does not have a header."])
+
+data = [{} for f in files]
+values = [{} for f in files]
+for i, f in enumerate (files):
+	for line in f:
+
+		j = line.find ('#')
+		if j >= 0:
+			line = line[:j]
+
+		fields = [x.strip () for x in line.split (';')]
+		if len (fields) == 1:
+			continue
+
+		uu = fields[0].split ('..')
+		start = int (uu[0], 16)
+		if len (uu) == 1:
+			end = start
+		else:
+			end = int (uu[1], 16)
+
+		t = fields[1 if i != 2 else 2]
+
+		for u in range (start, end + 1):
+			data[i][u] = t
+		values[i][t] = values[i].get (t, 0) + end - start + 1
+
+defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')
+
+# TODO Characters that are not in Unicode Indic files, but used in USE
+data[0][0x034F] = defaults[0]
+data[0][0x2060] = defaults[0]
+for u in range (0xFE00, 0xFE0F + 1):
+	data[0][u] = defaults[0]
+
+# Merge data into one dict:
+for i,v in enumerate (defaults):
+	values[i][v] = values[i].get (v, 0) + 1
+combined = {}
+for i,d in enumerate (data):
+	for u,v in d.items ():
+		if i >= 2 and not u in combined:
+			continue
+		if not u in combined:
+			combined[u] = list (defaults)
+		combined[u][i] = v
+combined = {k:v for k,v in combined.items() if v[3] not in BLACKLISTED_BLOCKS}
+data = combined
+del combined
+num = len (data)
+
+
+property_names = [
+	# General_Category
+	'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc',
+	'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po',
+	'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',
+	# Indic_Syllabic_Category
+	'Other',
+	'Bindu',
+	'Visarga',
+	'Avagraha',
+	'Nukta',
+	'Virama',
+	'Pure_Killer',
+	'Invisible_Stacker',
+	'Vowel_Independent',
+	'Vowel_Dependent',
+	'Vowel',
+	'Consonant_Placeholder',
+	'Consonant',
+	'Consonant_Dead',
+	'Consonant_With_Stacker',
+	'Consonant_Prefixed',
+	'Consonant_Preceding_Repha',
+	'Consonant_Succeeding_Repha',
+	'Consonant_Subjoined',
+	'Consonant_Medial',
+	'Consonant_Final',
+	'Consonant_Head_Letter',
+	'Modifying_Letter',
+	'Tone_Letter',
+	'Tone_Mark',
+	'Gemination_Mark',
+	'Cantillation_Mark',
+	'Register_Shifter',
+	'Syllable_Modifier',
+	'Consonant_Killer',
+	'Non_Joiner',
+	'Joiner',
+	'Number_Joiner',
+	'Number',
+	'Brahmi_Joining_Number',
+	# Indic_Positional_Category
+	'Not_Applicable',
+	'Right',
+	'Left',
+	'Visual_Order_Left',
+	'Left_And_Right',
+	'Top',
+	'Bottom',
+	'Top_And_Bottom',
+	'Top_And_Right',
+	'Top_And_Left',
+	'Top_And_Left_And_Right',
+	'Bottom_And_Right',
+	'Top_And_Bottom_And_Right',
+	'Overstruck',
+]
+
+class PropertyValue(object):
+	def __init__(self, name_):
+		self.name = name_
+	def __str__(self):
+		return self.name
+	def __eq__(self, other):
+		return self.name == (other if isinstance(other, basestring) else other.name)
+	def __ne__(self, other):
+		return not (self == other)
+
+property_values = {}
+
+for name in property_names:
+	value = PropertyValue(name)
+	assert value not in property_values
+	assert value not in globals()
+	property_values[name] = value
+globals().update(property_values)
+
+
+def is_BASE(U, UISC, UGC):
+	return (UISC in [Number, Consonant, Consonant_Head_Letter,
+			#SPEC-OUTDATED Consonant_Placeholder,
+			Tone_Letter] or
+		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
+					Consonant_Subjoined, Vowel, Vowel_Dependent]))
+def is_BASE_VOWEL(U, UISC, UGC):
+	return UISC == Vowel_Independent
+def is_BASE_IND(U, UISC, UGC):
+	#SPEC-BROKEN return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
+	return (UISC in [Consonant_Dead, Modifying_Letter] or
+		(UGC == Po and not is_BASE_OTHER(U, UISC, UGC))) # for 104E
+def is_BASE_NUM(U, UISC, UGC):
+	return UISC == Brahmi_Joining_Number
+def is_BASE_OTHER(U, UISC, UGC):
+	if UISC == Consonant_Placeholder: return True #SPEC-OUTDATED
+	return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC,
+		     0x25FB, 0x25FC, 0x25FD, 0x25FE]
+def is_CGJ(U, UISC, UGC):
+	return U == 0x034F
+def is_CONS_FINAL(U, UISC, UGC):
+	return ((UISC == Consonant_Final and UGC != Lo) or
+		UISC == Consonant_Succeeding_Repha)
+def is_CONS_FINAL_MOD(U, UISC, UGC):
+	#SPEC-OUTDATED return  UISC in [Consonant_Final_Modifier, Syllable_Modifier]
+	return  UISC == Syllable_Modifier
+def is_CONS_MED(U, UISC, UGC):
+	return UISC == Consonant_Medial and UGC != Lo
+def is_CONS_MOD(U, UISC, UGC):
+	return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
+def is_CONS_SUB(U, UISC, UGC):
+	#SPEC-OUTDATED return UISC == Consonant_Subjoined
+	return UISC == Consonant_Subjoined and UGC != Lo
+def is_HALANT(U, UISC, UGC):
+	return UISC in [Virama, Invisible_Stacker]
+def is_HALANT_NUM(U, UISC, UGC):
+	return UISC == Number_Joiner
+def is_ZWNJ(U, UISC, UGC):
+	return UISC == Non_Joiner
+def is_ZWJ(U, UISC, UGC):
+	return UISC == Joiner
+def is_Word_Joiner(U, UISC, UGC):
+	return U == 0x2060
+def is_OTHER(U, UISC, UGC):
+	#SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters
+	return (UISC == Other
+		and not is_SYM_MOD(U, UISC, UGC)
+		and not is_CGJ(U, UISC, UGC)
+		and not is_Word_Joiner(U, UISC, UGC)
+		and not is_VARIATION_SELECTOR(U, UISC, UGC)
+	)
+def is_Reserved(U, UISC, UGC):
+	return UGC == 'Cn'
+def is_REPHA(U, UISC, UGC):
+	#return UISC == Consonant_Preceding_Repha
+	#SPEC-OUTDATED hack to categorize Consonant_With_Stacker and Consonant_Prefixed
+	return UISC in [Consonant_Preceding_Repha, Consonant_With_Stacker, Consonant_Prefixed]
+def is_SYM(U, UISC, UGC):
+	if U == 0x25CC: return False #SPEC-OUTDATED
+	#SPEC-OUTDATED return UGC in [So, Sc] or UISC == Symbol_Letter
+	return UGC in [So, Sc]
+def is_SYM_MOD(U, UISC, UGC):
+	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
+def is_VARIATION_SELECTOR(U, UISC, UGC):
+	return 0xFE00 <= U <= 0xFE0F
+def is_VOWEL(U, UISC, UGC):
+	return (UISC == Pure_Killer or
+		(UGC != Lo and UISC in [Vowel, Vowel_Dependent]))
+def is_VOWEL_MOD(U, UISC, UGC):
+	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
+		(UGC != Lo and UISC == Bindu))
+
+use_mapping = {
+	'B':	is_BASE,
+	'IV':	is_BASE_VOWEL,
+	'IND':	is_BASE_IND,
+	'N':	is_BASE_NUM,
+	'GB':	is_BASE_OTHER,
+	'CGJ':	is_CGJ,
+	'F':	is_CONS_FINAL,
+	'FM':	is_CONS_FINAL_MOD,
+	'M':	is_CONS_MED,
+	'CM':	is_CONS_MOD,
+	'SUB':	is_CONS_SUB,
+	'H':	is_HALANT,
+	'HN':	is_HALANT_NUM,
+	'ZWNJ':	is_ZWNJ,
+	'ZWJ':	is_ZWJ,
+	'WJ':	is_Word_Joiner,
+	'O':	is_OTHER,
+	'Rsv':	is_Reserved,
+	'R':	is_REPHA,
+	'S':	is_SYM,
+	'SM':	is_SYM_MOD,
+	'VS':	is_VARIATION_SELECTOR,
+	'V':	is_VOWEL,
+	'VM':	is_VOWEL_MOD,
+}
+
+use_positions = {
+	'F': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+		'Pst': [Right],
+	},
+	'M': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+		'Pst': [Right],
+		'Pre': [Left],
+	},
+	'CM': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+	},
+	'V': {
+		'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right],
+		'Blw': [Bottom, Overstruck, Bottom_And_Right],
+		'Pst': [Right],
+		'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
+	},
+	'VM': {
+		'Abv': [Top],
+		'Blw': [Bottom, Overstruck],
+		'Pst': [Right],
+		'Pre': [Left],
+	},
+	'SM': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+	},
+	'H': None,
+	'B': None,
+	'FM': None,
+	'SUB': None,
+}
+
+def map_to_use(data):
+	out = {}
+	items = use_mapping.items()
+	for U,(UISC,UIPC,UGC,UBlock) in data.items():
+
+		# Resolve Indic_Syllabic_Category
+
+		# TODO: These don't have UISC assigned in Unicode 8.0, but
+		# have UIPC
+		if U == 0x17DD: UISC = Vowel_Dependent
+		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
+
+		# TODO: U+1CED should only be allowed after some of
+		# the nasalization marks, maybe only for U+1CE9..U+1CF1.
+		if U == 0x1CED: UISC = Tone_Mark
+
+		evals = [(k, v(U,UISC,UGC)) for k,v in items]
+		values = [k for k,v in evals if v]
+		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
+		USE = values[0]
+
+		# Resolve Indic_Positional_Category
+
+		# TODO: Not in Unicode 8.0 yet, but in spec.
+		if U == 0x1B6C: UIPC = Bottom
+
+		# TODO: These should die, but have UIPC in Unicode 8.0
+		if U in [0x953, 0x954]: UIPC = Not_Applicable
+
+		# TODO: In USE's override list but not in Unicode 8.0
+		if U == 0x103C: UIPC = Left
+
+		# TODO: These are not in USE's override list that we have, nor are they in Unicode 8.0
+		if 0xA926 <= U <= 0xA92A: UIPC = Top
+		if U == 0x111CA: UIPC = Bottom
+		if U == 0x11300: UIPC = Top
+		if U == 0x1133C: UIPC = Bottom
+		if U == 0x1171E: UIPC = Left # Correct?!
+		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
+		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
+
+		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
+			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
+
+		pos_mapping = use_positions.get(USE, None)
+		if pos_mapping:
+			values = [k for k,v in pos_mapping.items() if v and UIPC in v]
+			assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC, values)
+			USE = USE + values[0]
+
+		out[U] = (USE, UBlock)
+	return out
+
+defaults = ('O', 'No_Block')
+data = map_to_use(data)
+
+# Remove the outliers
+singles = {}
+for u in [0x034F, 0x25CC, 0x1107F]:
+	singles[u] = data[u]
+	del data[u]
+
+print "/* == Start of generated table == */"
+print "/*"
+print " * The following table is generated by running:"
+print " *"
+print " *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt"
+print " *"
+print " * on files with these headers:"
+print " *"
+for h in headers:
+	for l in h:
+		print " * %s" % (l.strip())
+print " */"
+print
+print '#include "hb-ot-shape-complex-use-private.hh"'
+print
+
+total = 0
+used = 0
+last_block = None
+def print_block (block, start, end, data):
+	global total, used, last_block
+	if block and block != last_block:
+		print
+		print
+		print "  /* %s */" % block
+		if start % 16:
+			print ' ' * (20 + (start % 16 * 6)),
+	num = 0
+	assert start % 8 == 0
+	assert (end+1) % 8 == 0
+	for u in range (start, end+1):
+		if u % 16 == 0:
+			print
+			print "  /* %04X */" % u,
+		if u in data:
+			num += 1
+		d = data.get (u, defaults)
+		sys.stdout.write ("%6s," % d[0])
+
+	total += end - start + 1
+	used += num
+	if block:
+		last_block = block
+
+uu = data.keys ()
+uu.sort ()
+
+last = -100000
+num = 0
+offset = 0
+starts = []
+ends = []
+for k,v in sorted(use_mapping.items()):
+	if k in use_positions and use_positions[k]: continue
+	print "#define %s	USE_%s	/* %s */" % (k, k, v.__name__[3:])
+for k,v in sorted(use_positions.items()):
+	if not v: continue
+	for suf in v.keys():
+		tag = k + suf
+		print "#define %s	USE_%s" % (tag, tag)
+print ""
+print "static const USE_TABLE_ELEMENT_TYPE use_table[] = {"
+for u in uu:
+	if u <= last:
+		continue
+	block = data[u][1]
+
+	start = u//8*8
+	end = start+1
+	while end in uu and block == data[end][1]:
+		end += 1
+	end = (end-1)//8*8 + 7
+
+	if start != last + 1:
+		if start - last <= 1+16*3:
+			print_block (None, last+1, start-1, data)
+			last = start-1
+		else:
+			if last >= 0:
+				ends.append (last + 1)
+				offset += ends[-1] - starts[-1]
+			print
+			print
+			print "#define use_offset_0x%04xu %d" % (start, offset)
+			starts.append (start)
+
+	print_block (block, start, end, data)
+	last = end
+ends.append (last + 1)
+offset += ends[-1] - starts[-1]
+print
+print
+occupancy = used * 100. / total
+page_bits = 12
+print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)
+print
+print "USE_TABLE_ELEMENT_TYPE"
+print "hb_use_get_categories (hb_codepoint_t u)"
+print "{"
+print "  switch (u >> %d)" % page_bits
+print "  {"
+pages = set([u>>page_bits for u in starts+ends+singles.keys()])
+for p in sorted(pages):
+	print "    case 0x%0Xu:" % p
+	for (start,end) in zip (starts, ends):
+		if p not in [start>>page_bits, end>>page_bits]: continue
+		offset = "use_offset_0x%04xu" % start
+		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
+	for u,d in singles.items ():
+		if p != u>>page_bits: continue
+		print "      if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0])
+	print "      break;"
+	print ""
+print "    default:"
+print "      break;"
+print "  }"
+print "  return USE_O;"
+print "}"
+print
+for k in sorted(use_mapping.keys()):
+	if k in use_positions and use_positions[k]: continue
+	print "#undef %s" % k
+for k,v in sorted(use_positions.items()):
+	if not v: continue
+	for suf in v.keys():
+		tag = k + suf
+		print "#undef %s" % tag
+print
+print "/* == End of generated table == */"
+
+# Maintain at least 50% occupancy in the table */
+if occupancy < 50:
+	raise Exception ("Table too sparse, please investigate: ", occupancy)
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -0,0 +1,13 @@
+prefix=/usr/local
+exec_prefix=/usr/local
+libdir=/usr/local/lib
+includedir=/usr/local/include
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library ICU integration
+Version: 1.0.1
+
+Requires: harfbuzz
+Requires.private: icu-uc
+Libs: -L${libdir} -lharfbuzz-icu
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -0,0 +1,11 @@
+prefix=/usr/local
+exec_prefix=/usr/local
+libdir=/usr/local/lib
+includedir=/usr/local/include
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library
+Version: 1.0.1
+
+Libs: -L${libdir} -lharfbuzz
+Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -45,16 +45,17 @@ ASSERT_STATIC (sizeof (hb_glyph_info_t) 
 
 struct hb_buffer_t {
   hb_object_header_t header;
   ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_buffer_flags_t flags; /* BOT / EOT / etc. */
+  hb_buffer_cluster_level_t cluster_level;
   hb_codepoint_t replacement; /* U+FFFD or something else. */
 
   /* Buffer contents */
   hb_buffer_content_type_t content_type;
   hb_segment_properties_t props; /* Script, language, direction */
 
   bool in_error; /* Allocation failed */
   bool have_output; /* Whether we have an output buffer going on */
@@ -166,19 +167,28 @@ struct hb_buffer_t {
       info[j].mask |= mask;
   }
   HB_INTERNAL void set_masks (hb_mask_t value,
 			      hb_mask_t mask,
 			      unsigned int cluster_start,
 			      unsigned int cluster_end);
 
   HB_INTERNAL void merge_clusters (unsigned int start,
-				   unsigned int end);
+				   unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    merge_clusters_impl (start, end);
+  }
+  HB_INTERNAL void merge_clusters_impl (unsigned int start,
+					unsigned int end);
   HB_INTERNAL void merge_out_clusters (unsigned int start,
 				       unsigned int end);
+  /* Merge clusters for deleting current glyph, and skip it. */
+  HB_INTERNAL void delete_glyph (void);
 
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
   { return likely (!size || size < allocated) ? true : enlarge (size); }
 
   inline bool ensure_inplace (unsigned int size)
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -31,28 +31,34 @@
 #include "hb-utf-private.hh"
 
 
 #ifndef HB_DEBUG_BUFFER
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
 
+/**
+ * Since: 0.9.7
+ **/
 hb_bool_t
 hb_segment_properties_equal (const hb_segment_properties_t *a,
 			     const hb_segment_properties_t *b)
 {
   return a->direction == b->direction &&
 	 a->script    == b->script    &&
 	 a->language  == b->language  &&
 	 a->reserved1 == b->reserved1 &&
 	 a->reserved2 == b->reserved2;
 
 }
 
+/**
+ * Since: 0.9.7
+ **/
 unsigned int
 hb_segment_properties_hash (const hb_segment_properties_t *p)
 {
   return (unsigned int) p->direction ^
 	 (unsigned int) p->script ^
 	 (intptr_t) (p->language);
 }
 
@@ -493,24 +499,20 @@ hb_buffer_t::reverse_clusters (void)
       start = i;
       last_cluster = info[i].cluster;
     }
   }
   reverse_range (start, i);
 }
 
 void
-hb_buffer_t::merge_clusters (unsigned int start,
-			     unsigned int end)
+hb_buffer_t::merge_clusters_impl (unsigned int start,
+				  unsigned int end)
 {
-#ifdef HB_NO_MERGE_CLUSTERS
-  return;
-#endif
-
-  if (unlikely (end - start < 2))
+  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     return;
 
   unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, info[i].cluster);
 
   /* Extend end */
@@ -518,29 +520,28 @@ hb_buffer_t::merge_clusters (unsigned in
     end++;
 
   /* Extend start */
   while (idx < start && info[start - 1].cluster == info[start].cluster)
     start--;
 
   /* If we hit the start of buffer, continue in out-buffer. */
   if (idx == start)
-    for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
+    for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
       out_info[i - 1].cluster = cluster;
 
   for (unsigned int i = start; i < end; i++)
     info[i].cluster = cluster;
 }
 void
 hb_buffer_t::merge_out_clusters (unsigned int start,
 				 unsigned int end)
 {
-#ifdef HB_NO_MERGE_CLUSTERS
-  return;
-#endif
+  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+    return;
 
   if (unlikely (end - start < 2))
     return;
 
   unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, out_info[i].cluster);
@@ -550,22 +551,54 @@ hb_buffer_t::merge_out_clusters (unsigne
     start--;
 
   /* Extend end */
   while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
     end++;
 
   /* If we hit the end of out-buffer, continue in buffer. */
   if (end == out_len)
-    for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
+    for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
       info[i].cluster = cluster;
 
   for (unsigned int i = start; i < end; i++)
     out_info[i].cluster = cluster;
 }
+void
+hb_buffer_t::delete_glyph ()
+{
+  unsigned int cluster = info[idx].cluster;
+  if (idx + 1 < len && cluster == info[idx + 1].cluster)
+  {
+    /* Cluster survives; do nothing. */
+    goto done;
+  }
+
+  if (out_len)
+  {
+    /* Merge cluster backward. */
+    if (cluster < out_info[out_len - 1].cluster)
+    {
+      unsigned int old_cluster = out_info[out_len - 1].cluster;
+      for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--)
+	out_info[i - 1].cluster = cluster;
+    }
+    goto done;
+  }
+
+  if (idx + 1 < len)
+  {
+    /* Merge cluster forward. */
+    merge_clusters (idx, idx + 2);
+    goto done;
+  }
+
+done:
+  skip_glyph ();
+}
 
 void
 hb_buffer_t::guess_segment_properties (void)
 {
   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
 	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   /* If script is set to INVALID, guess from buffer contents */
@@ -698,16 +731,17 @@ hb_buffer_create (void)
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
   static const hb_buffer_t _hb_buffer_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     HB_BUFFER_FLAG_DEFAULT,
+    HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
     HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
     HB_SEGMENT_PROPERTIES_DEFAULT,
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
 
@@ -799,34 +833,34 @@ hb_buffer_get_user_data (hb_buffer_t    
 
 /**
  * hb_buffer_set_content_type:
  * @buffer: a buffer.
  * @content_type: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 void
 hb_buffer_set_content_type (hb_buffer_t              *buffer,
 			    hb_buffer_content_type_t  content_type)
 {
   buffer->content_type = content_type;
 }
 
 /**
  * hb_buffer_get_content_type:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 hb_buffer_content_type_t
 hb_buffer_get_content_type (hb_buffer_t *buffer)
 {
   return buffer->content_type;
 }
 
 
@@ -979,17 +1013,17 @@ hb_buffer_get_language (hb_buffer_t *buf
 
 /**
  * hb_buffer_set_segment_properties:
  * @buffer: a buffer.
  * @props: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
 				  const hb_segment_properties_t *props)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -998,34 +1032,34 @@ hb_buffer_set_segment_properties (hb_buf
 
 /**
  * hb_buffer_get_segment_properties:
  * @buffer: a buffer.
  * @props: (out):
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_get_segment_properties (hb_buffer_t *buffer,
 				  hb_segment_properties_t *props)
 {
   *props = buffer->props;
 }
 
 
 /**
  * hb_buffer_set_flags:
  * @buffer: a buffer.
  * @flags: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -1035,33 +1069,68 @@ hb_buffer_set_flags (hb_buffer_t       *
 /**
  * hb_buffer_get_flags:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer)
 {
   return buffer->flags;
 }
 
+/**
+ * hb_buffer_set_cluster_level:
+ * @buffer: a buffer.
+ * @cluster_level: 
+ *
+ * 
+ *
+ * Since: 0.9.42
+ **/
+void
+hb_buffer_set_cluster_level (hb_buffer_t       *buffer,
+		     hb_buffer_cluster_level_t  cluster_level)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->cluster_level = cluster_level;
+}
+
+/**
+ * hb_buffer_get_cluster_level:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 0.9.42
+ **/
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (hb_buffer_t *buffer)
+{
+  return buffer->cluster_level;
+}
+
 
 /**
  * hb_buffer_set_replacement_codepoint:
  * @buffer: a buffer.
  * @replacement: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.31
  **/
 void
 hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
 				     hb_codepoint_t  replacement)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -1071,17 +1140,17 @@ hb_buffer_set_replacement_codepoint (hb_
 /**
  * hb_buffer_get_replacement_codepoint:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.31
  **/
 hb_codepoint_t
 hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer)
 {
   return buffer->replacement;
 }
 
 
@@ -1100,17 +1169,17 @@ hb_buffer_reset (hb_buffer_t *buffer)
 }
 
 /**
  * hb_buffer_clear_contents:
  * @buffer: a buffer.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.11
  **/
 void
 hb_buffer_clear_contents (hb_buffer_t *buffer)
 {
   buffer->clear ();
 }
 
 /**
@@ -1278,16 +1347,33 @@ hb_buffer_get_glyph_positions (hb_buffer
  **/
 void
 hb_buffer_reverse (hb_buffer_t *buffer)
 {
   buffer->reverse ();
 }
 
 /**
+ * hb_buffer_reverse_range:
+ * @buffer: a buffer.
+ * @start: start index.
+ * @end: end index.
+ *
+ * Reverses buffer contents between  start to end.
+ *
+ * Since: 1.0
+ **/
+void
+hb_buffer_reverse_range (hb_buffer_t *buffer,
+			 unsigned int start, unsigned int end)
+{
+  buffer->reverse_range (start, end);
+}
+
+/**
  * hb_buffer_reverse_clusters:
  * @buffer: a buffer.
  *
  * Reverses buffer clusters.  That is, the buffer contents are
  * reversed, then each cluster (consecutive items having the
  * same cluster number) are reversed again.
  *
  * Since: 1.0
@@ -1315,17 +1401,17 @@ hb_buffer_reverse_clusters (hb_buffer_t 
  * it will be set to the natural horizontal direction of the
  * buffer script as returned by hb_script_get_horizontal_direction().
  *
  * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
  * it will be set to the process's default language as returned by
  * hb_language_get_default().  This may change in the future by
  * taking buffer script into consideration when choosing a language.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
 {
   buffer->guess_segment_properties ();
 }
 
 template <typename utf_t>
@@ -1468,17 +1554,17 @@ hb_buffer_add_utf32 (hb_buffer_t    *buf
  * @buffer: a buffer.
  * @text: (array length=text_length) (element-type uint8_t):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.39
  **/
 void
 hb_buffer_add_latin1 (hb_buffer_t   *buffer,
 		      const uint8_t *text,
 		      int            text_length,
 		      unsigned int   item_offset,
 		      int            item_length)
 {
@@ -1490,17 +1576,17 @@ hb_buffer_add_latin1 (hb_buffer_t   *buf
  * @buffer: a buffer.
  * @text: (array length=text_length):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.31
  **/
 void
 hb_buffer_add_codepoints (hb_buffer_t          *buffer,
 			  const hb_codepoint_t *text,
 			  int                   text_length,
 			  unsigned int          item_offset,
 			  int                   item_length)
 {
@@ -1564,17 +1650,17 @@ normalize_glyphs_cluster (hb_buffer_t *b
 }
 
 /**
  * hb_buffer_normalize_glyphs:
  * @buffer: a buffer.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
 {
   assert (buffer->have_positions);
   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -180,17 +180,29 @@ typedef enum { /*< flags >*/
 
 void
 hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags);
 
 hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer);
 
+typedef enum {
+  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES	= 0,
+  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS	= 1,
+  HB_BUFFER_CLUSTER_LEVEL_CHARACTERS		= 2,
+  HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES
+} hb_buffer_cluster_level_t;
 
+void
+hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
+			     hb_buffer_cluster_level_t  cluster_level);
+
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (hb_buffer_t *buffer);
 
 #define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu
 
 /* Sets codepoint used to replace invalid UTF-8/16/32 entries.
  * Default is 0xFFFDu. */
 void
 hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
 				     hb_codepoint_t  replacement);
@@ -217,16 +229,20 @@ hb_buffer_pre_allocate (hb_buffer_t  *bu
 /* Returns false if allocation has failed before */
 hb_bool_t
 hb_buffer_allocation_successful (hb_buffer_t  *buffer);
 
 void
 hb_buffer_reverse (hb_buffer_t *buffer);
 
 void
+hb_buffer_reverse_range (hb_buffer_t *buffer,
+			 unsigned int start, unsigned int end);
+
+void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer);
 
 
 /* Filling the buffer in */
 
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -87,17 +87,17 @@ hb_tag_from_string (const char *str, int
 
 /**
  * hb_tag_to_string:
  * @tag: 
  * @buf: (array fixed-size=4): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 void
 hb_tag_to_string (hb_tag_t tag, char *buf)
 {
   buf[0] = (char) (uint8_t) (tag >> 24);
   buf[1] = (char) (uint8_t) (tag >> 16);
   buf[2] = (char) (uint8_t) (tag >>  8);
   buf[3] = (char) (uint8_t) (tag >>  0);
@@ -329,17 +329,17 @@ hb_language_to_string (hb_language_t lan
   return language->s;
 }
 
 /**
  * hb_language_get_default:
  *
  * 
  *
- * Return value: 
+ * Return value: (transfer none):
  *
  * Since: 1.0
  **/
 hb_language_t
 hb_language_get_default (void)
 {
   static hb_language_t default_language = HB_LANGUAGE_INVALID;
 
@@ -488,16 +488,19 @@ hb_script_get_horizontal_direction (hb_s
     /* Unicode-7.0 additions */
     case HB_SCRIPT_MANICHAEAN:
     case HB_SCRIPT_MENDE_KIKAKUI:
     case HB_SCRIPT_NABATAEAN:
     case HB_SCRIPT_OLD_NORTH_ARABIAN:
     case HB_SCRIPT_PALMYRENE:
     case HB_SCRIPT_PSALTER_PAHLAVI:
 
+    /* Unicode-8.0 additions */
+    case HB_SCRIPT_OLD_HUNGARIAN:
+
       return HB_DIRECTION_RTL;
   }
 
   return HB_DIRECTION_LTR;
 }
 
 
 /* hb_user_data_array_t */
@@ -574,17 +577,17 @@ hb_version_string (void)
  * @major: 
  * @minor: 
  * @micro: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.30
  **/
 hb_bool_t
 hb_version_atleast (unsigned int major,
 		    unsigned int minor,
 		    unsigned int micro)
 {
   return HB_VERSION_ATLEAST (major, minor, micro);
 }
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -291,22 +291,22 @@ typedef enum
   /*7.0*/ HB_SCRIPT_PAHAWH_HMONG		= HB_TAG ('H','m','n','g'),
   /*7.0*/ HB_SCRIPT_PALMYRENE			= HB_TAG ('P','a','l','m'),
   /*7.0*/ HB_SCRIPT_PAU_CIN_HAU			= HB_TAG ('P','a','u','c'),
   /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI		= HB_TAG ('P','h','l','p'),
   /*7.0*/ HB_SCRIPT_SIDDHAM			= HB_TAG ('S','i','d','d'),
   /*7.0*/ HB_SCRIPT_TIRHUTA			= HB_TAG ('T','i','r','h'),
   /*7.0*/ HB_SCRIPT_WARANG_CITI			= HB_TAG ('W','a','r','a'),
 
-  /*8.0*/ HB_SCRIPT_AHOM			= HB_TAG ('A','h','o','m'),
-  /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS	= HB_TAG ('H','l','u','w'),
-  /*8.0*/ HB_SCRIPT_HATRAN			= HB_TAG ('H','a','t','r'),
-  /*8.0*/ HB_SCRIPT_MULTANI			= HB_TAG ('M','u','l','t'),
-  /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN		= HB_TAG ('H','u','n','g'),
-  /*8.0*/ HB_SCRIPT_SIGNWRITING			= HB_TAG ('S','g','n','w'),
+  /*8.0*/ HB_SCRIPT_AHOM                        = HB_TAG ('A','h','o','m'),
+  /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS       = HB_TAG ('H','l','u','w'),
+  /*8.0*/ HB_SCRIPT_HATRAN                      = HB_TAG ('H','a','t','r'),
+  /*8.0*/ HB_SCRIPT_MULTANI                     = HB_TAG ('M','u','l','t'),
+  /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN               = HB_TAG ('H','u','n','g'),
+  /*8.0*/ HB_SCRIPT_SIGNWRITING                 = HB_TAG ('S','g','n','w'),
 
   /* No script set. */
   HB_SCRIPT_INVALID				= HB_TAG_NONE,
 
   /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t
    * without risking undefined behavior.  Include both a signed and unsigned max,
    * since technically enums are int, and indeed, hb_script_t ends up being signed.
    * See this thread for technicalities:
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -342,32 +342,32 @@ hb_face_reference_table (hb_face_t *face
 /**
  * hb_face_reference_blob:
  * @face: a face.
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_face_reference_blob (hb_face_t *face)
 {
   return face->reference_table (HB_TAG_NONE);
 }
 
 /**
  * hb_face_set_index:
  * @face: a face.
  * @index: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
   if (face->immutable)
     return;
 
@@ -377,32 +377,32 @@ hb_face_set_index (hb_face_t    *face,
 /**
  * hb_face_get_index:
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_face_get_index (hb_face_t    *face)
 {
   return face->index;
 }
 
 /**
  * hb_face_set_upem:
  * @face: a face.
  * @upem: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem)
 {
   if (face->immutable)
     return;
 
@@ -436,17 +436,17 @@ hb_face_t::load_upem (void) const
 
 /**
  * hb_face_set_glyph_count:
  * @face: a face.
  * @glyph_count: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_face_set_glyph_count (hb_face_t    *face,
 			 unsigned int  glyph_count)
 {
   if (face->immutable)
     return;
 
@@ -456,17 +456,17 @@ hb_face_set_glyph_count (hb_face_t    *f
 /**
  * hb_face_get_glyph_count:
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 unsigned int
 hb_face_get_glyph_count (hb_face_t *face)
 {
   return face->get_num_glyphs ();
 }
 
 void
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -599,17 +599,17 @@ hb_font_get_glyph_contour_point (hb_font
  * @glyph: 
  * @name: (array length=size): 
  * @size: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_name (hb_font_t *font,
 			hb_codepoint_t glyph,
 			char *name, unsigned int size)
 {
   return font->get_glyph_name (glyph, name, size);
 }
@@ -620,17 +620,17 @@ hb_font_get_glyph_name (hb_font_t *font,
  * @name: (array length=len): 
  * @len: 
  * @glyph: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_from_name (hb_font_t *font,
 			     const char *name, int len, /* -1 means nul-terminated */
 			     hb_codepoint_t *glyph)
 {
   return font->get_glyph_from_name (name, len, glyph);
 }
@@ -795,17 +795,17 @@ hb_font_get_glyph_contour_point_for_orig
  * hb_font_glyph_to_string:
  * @font: a font.
  * @glyph: 
  * @s: (array length=size): 
  * @size: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_glyph_to_string (hb_font_t *font,
 			 hb_codepoint_t glyph,
 			 char *s, unsigned int size)
 {
   font->glyph_to_string (glyph, s, size);
 }
@@ -817,17 +817,17 @@ hb_font_glyph_to_string (hb_font_t *font
  * @s: (array length=len) (element-type uint8_t): 
  * @len: 
  * @glyph: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_glyph_from_string (hb_font_t *font,
 			   const char *s, int len, /* -1 means nul-terminated */
 			   hb_codepoint_t *glyph)
 {
   return font->glyph_from_string (s, len, glyph);
 }
@@ -1073,17 +1073,17 @@ hb_font_get_parent (hb_font_t *font)
 /**
  * hb_font_get_face:
  * @font: a font.
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_font_get_face (hb_font_t *font)
 {
   return font->face;
 }
 
 
@@ -1091,17 +1091,17 @@ hb_font_get_face (hb_font_t *font)
  * hb_font_set_funcs:
  * @font: a font.
  * @klass: (closure font_data) (destroy destroy) (scope notified):
  * @font_data: 
  * @destroy: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_set_funcs (hb_font_t         *font,
 		   hb_font_funcs_t   *klass,
 		   void              *font_data,
 		   hb_destroy_func_t  destroy)
 {
   if (font->immutable) {
@@ -1126,17 +1126,17 @@ hb_font_set_funcs (hb_font_t         *fo
 /**
  * hb_font_set_funcs_data:
  * @font: a font.
  * @font_data: (destroy destroy) (scope notified):
  * @destroy: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_set_funcs_data (hb_font_t         *font,
 		        void              *font_data,
 		        hb_destroy_func_t  destroy)
 {
   /* Destroy user_data? */
   if (font->immutable) {
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -287,33 +287,33 @@ hb_font_funcs_set_glyph_contour_point_fu
  * hb_font_funcs_set_glyph_name_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs,
 				   hb_font_get_glyph_name_func_t func,
 				   void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_from_name_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_from_name_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 
 /* func dispatch */
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -70,25 +70,29 @@ static hb_bool_t
 hb_ft_get_glyph (hb_font_t *font HB_UNUSED,
 		 void *font_data,
 		 hb_codepoint_t unicode,
 		 hb_codepoint_t variation_selector,
 		 hb_codepoint_t *glyph,
 		 void *user_data HB_UNUSED)
 
 {
+  unsigned int g;
   FT_Face ft_face = (FT_Face) font_data;
 
-  if (unlikely (variation_selector)) {
-    *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
-    return *glyph != 0;
-  }
+  if (likely (!variation_selector))
+    g = FT_Get_Char_Index (ft_face, unicode);
+  else
+    g = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
 
-  *glyph = FT_Get_Char_Index (ft_face, unicode);
-  return *glyph != 0;
+  if (unlikely (!g))
+    return false;
+
+  *glyph = g;
+  return true;
 }
 
 static hb_position_t
 hb_ft_get_glyph_h_advance (hb_font_t *font HB_UNUSED,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
@@ -372,17 +376,17 @@ hb_ft_face_create (FT_Face           ft_
 
 /**
  * hb_ft_face_create_referenced:
  * @ft_face:
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.38
  **/
 hb_face_t *
 hb_ft_face_create_referenced (FT_Face ft_face)
 {
   FT_Reference_Face (ft_face);
   return hb_ft_face_create (ft_face, (hb_destroy_func_t) FT_Done_Face);
 }
 
@@ -459,17 +463,17 @@ hb_ft_font_create (FT_Face           ft_
 
 /**
  * hb_ft_font_create_referenced:
  * @ft_face:
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.38
  **/
 hb_font_t *
 hb_ft_font_create_referenced (FT_Face ft_face)
 {
   FT_Reference_Face (ft_face);
   return hb_ft_font_create (ft_face, (hb_destroy_func_t) FT_Done_Face);
 }
 
--- a/gfx/harfbuzz/src/hb-glib.cc
+++ b/gfx/harfbuzz/src/hb-glib.cc
@@ -377,16 +377,19 @@ hb_glib_get_unicode_funcs (void)
       HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
     }
   };
 
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
+/**
+ * Since: 0.9.38
+ **/
 hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes)
 {
   gsize size = 0;
   gconstpointer data = g_bytes_get_data (gbytes, &size);
   return hb_blob_create ((const char *) data,
 			 size,
 			 HB_MEMORY_MODE_READONLY,
--- a/gfx/harfbuzz/src/hb-gobject-structs.cc
+++ b/gfx/harfbuzz/src/hb-gobject-structs.cc
@@ -49,72 +49,32 @@ hb_gobject_##name##_get_type (void) \
       g_once_init_leave (&type_id, id); \
    } \
    return type_id; \
 }
 
 #define HB_DEFINE_OBJECT_TYPE(name) \
 	HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy);
 
+#define HB_DEFINE_VALUE_TYPE(name) \
+	static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \
+	{ \
+	  hb_##name##_t *c = (hb_##name##_t *) calloc (1, sizeof (hb_##name##_t)); \
+	  if (unlikely (!c)) return NULL; \
+	  *c = *l; \
+	  return c; \
+	} \
+	static void _hb_##name##_destroy (hb_##name##_t *l) { free (l); } \
+	HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy);
+
 HB_DEFINE_OBJECT_TYPE (buffer)
 HB_DEFINE_OBJECT_TYPE (blob)
 HB_DEFINE_OBJECT_TYPE (face)
 HB_DEFINE_OBJECT_TYPE (font)
 HB_DEFINE_OBJECT_TYPE (font_funcs)
 HB_DEFINE_OBJECT_TYPE (set)
 HB_DEFINE_OBJECT_TYPE (shape_plan)
 HB_DEFINE_OBJECT_TYPE (unicode_funcs)
-
-
-static hb_feature_t *feature_reference (hb_feature_t *g)
-{
-  hb_feature_t *c = (hb_feature_t *) calloc (1, sizeof (hb_feature_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void feature_destroy (hb_feature_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (feature, feature_reference, feature_destroy)
-
-static hb_glyph_info_t *glyph_info_reference (hb_glyph_info_t *g)
-{
-  hb_glyph_info_t *c = (hb_glyph_info_t *) calloc (1, sizeof (hb_glyph_info_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void glyph_info_destroy (hb_glyph_info_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (glyph_info, glyph_info_reference, glyph_info_destroy)
-
-static hb_glyph_position_t *glyph_position_reference (hb_glyph_position_t *g)
-{
-  hb_glyph_position_t *c = (hb_glyph_position_t *) calloc (1, sizeof (hb_glyph_position_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void glyph_position_destroy (hb_glyph_position_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (glyph_position, glyph_position_reference, glyph_position_destroy)
-
-static hb_segment_properties_t *segment_properties_reference (hb_segment_properties_t *g)
-{
-  hb_segment_properties_t *c = (hb_segment_properties_t *) calloc (1, sizeof (hb_segment_properties_t));
-  if (unlikely (!c)) return NULL;
-  *c = *g;
-  return c;
-}
-static void segment_properties_destroy (hb_segment_properties_t *g) { free (g); }
-HB_DEFINE_BOXED_TYPE (segment_properties, segment_properties_reference, segment_properties_destroy)
-
-static hb_user_data_key_t user_data_key_reference (hb_user_data_key_t l) { return l; }
-static void user_data_key_destroy (hb_user_data_key_t l) { }
-HB_DEFINE_BOXED_TYPE (user_data_key, user_data_key_reference, user_data_key_destroy)
-
-
-static hb_language_t *language_reference (hb_language_t *l)
-{
-  hb_language_t *c = (hb_language_t *) calloc (1, sizeof (hb_language_t));
-  if (unlikely (!c)) return NULL;
-  *c = *l;
-  return c;
-}
-static void language_destroy (hb_language_t *l) { free (l); }
-HB_DEFINE_BOXED_TYPE (language, language_reference, language_destroy)
+HB_DEFINE_VALUE_TYPE (feature)
+HB_DEFINE_VALUE_TYPE (glyph_info)
+HB_DEFINE_VALUE_TYPE (glyph_position)
+HB_DEFINE_VALUE_TYPE (segment_properties)
+HB_DEFINE_VALUE_TYPE (user_data_key)
--- a/gfx/harfbuzz/src/hb-gobject-structs.h
+++ b/gfx/harfbuzz/src/hb-gobject-structs.h
@@ -35,37 +35,55 @@
 
 #include <glib-object.h>
 
 HB_BEGIN_DECLS
 
 
 /* Object types */
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_blob_get_type (void);
 #define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_buffer_get_type (void);
 #define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_face_get_type (void);
 #define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_font_get_type (void);
 #define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_font_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ())
 
 GType hb_gobject_set_get_type (void);
 #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())
 
 GType hb_gobject_shape_plan_get_type (void);
 #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())
 
+/**
+ * Since: 0.9.2
+ **/
 GType hb_gobject_unicode_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ())
 
 /* Value types */
 
 GType hb_gobject_feature_get_type (void);
 #define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ())
 
@@ -76,20 +94,12 @@ GType hb_gobject_glyph_position_get_type
 #define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ())
 
 GType hb_gobject_segment_properties_get_type (void);
 #define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ())
 
 GType hb_gobject_user_data_key_get_type (void);
 #define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ())
 
-/* Currently gobject-introspection doesn't understand that hb_language_t
- * can be passed by-value.  As such we box it up.  May remove in the
- * future.
- *
- *   https://bugzilla.gnome.org/show_bug.cgi?id=707656
- */
-GType hb_gobject_language_get_type (void);
-#define HB_GOBJECT_TYPE_LANGUAGE (hb_gobject_language_get_type ())
 
 HB_END_DECLS
 
 #endif /* HB_GOBJECT_H */
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -223,37 +223,38 @@ hb_bool_t
   gr_face *grface = HB_SHAPER_DATA_GET (face)->grface;
   gr_font *grfont = HB_SHAPER_DATA_GET (font);
 
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
   const char *lang_end = lang ? strchr (lang, '-') : NULL;
   int lang_len = lang_end ? lang_end - lang : -1;
   gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
 
-  while (num_features--)
+  for (unsigned int i = 0; i < num_features; i++)
   {
-    const gr_feature_ref *fref = gr_face_find_fref (grface, features->tag);
+    const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag);
     if (fref)
-      gr_fref_set_feature_value (fref, features->value, feats);
-    features++;
+      gr_fref_set_feature_value (fref, features[i].value, feats);
   }
 
   gr_segment *seg = NULL;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
 
   unsigned int scratch_size;
   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
 
   uint32_t *chars = (uint32_t *) scratch;
 
   for (unsigned int i = 0; i < buffer->len; ++i)
     chars[i] = buffer->info[i].codepoint;
 
+  /* TODO ensure_native_direction. */
+
   hb_tag_t script_tag[2];
   hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
 
   seg = gr_make_seg (grfont, grface,
 		     script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
 		     feats,
 		     gr_utf32, chars, buffer->len,
 		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
@@ -262,19 +263,21 @@ hb_bool_t
     if (feats) gr_featureval_destroy (feats);
     return false;
   }
 
   unsigned int glyph_count = gr_seg_n_slots (seg);
   if (unlikely (!glyph_count)) {
     if (feats) gr_featureval_destroy (feats);
     gr_seg_destroy (seg);
-    return false;
+    buffer->len = 0;
+    return true;
   }
 
+  buffer->ensure (glyph_count);
   scratch = buffer->get_scratch_buffer (&scratch_size);
   while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) +
 	  DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size)
   {
     if (unlikely (!buffer->ensure (buffer->allocated * 2)))
     {
       if (feats) gr_featureval_destroy (feats);
       gr_seg_destroy (seg);
@@ -326,51 +329,75 @@ hb_bool_t
     }
     clusters[ci].num_glyphs++;
 
     if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
 	clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
   }
   ci++;
 
-  //buffer->clear_output ();
   for (unsigned int i = 0; i < ci; ++i)
   {
     for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
     {
       hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
       info->codepoint = gids[clusters[i].base_glyph + j];
       info->cluster = clusters[i].cluster;
     }
   }
   buffer->len = glyph_count;
-  //buffer->swap_buffers ();
 
-  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
-    curradvx = gr_seg_advance_X(seg);
-
-  hb_glyph_position_t *pPos;
-  for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
-       is; pPos++, is = gr_slot_next_in_segment (is))
+  /* Positioning. */
+  if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+  {
+    hb_glyph_position_t *pPos;
+    for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
+         is; pPos++, is = gr_slot_next_in_segment (is))
+    {
+      pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+      pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
+      pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+      pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
+      curradvx += pPos->x_advance;
+      curradvy += pPos->y_advance;
+    }
+    pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
+  }
+  else
   {
-    pPos->x_offset = gr_slot_origin_X (is) - curradvx;
-    pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
-    pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
-    pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
-    if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL) + buffer->len - 1;
+    const hb_glyph_info_t *info = buffer->info + buffer->len - 1;
+    const hb_glyph_info_t *tinfo;
+    const gr_slot *tis;
+    int currclus = -1;
+    float clusx = 0., clusy = 0.;
+    for (is = gr_seg_last_slot (seg); is; pPos--, info--, is = gr_slot_prev_in_segment (is))
+    {
+      if (info->cluster != currclus)
+      {
+        curradvx += clusx;
+        curradvy += clusy;
+        currclus = info->cluster;
+        clusx = 0.;
+        clusy = 0.;
+        for (tis = is, tinfo = info; tis && tinfo->cluster == currclus; tis = gr_slot_prev_in_segment (tis), tinfo--)
+        {
+          clusx += gr_slot_advance_X (tis, grface, grfont);
+          clusy += gr_slot_advance_Y (tis, grface, grfont);
+        }
+        curradvx += clusx;
+        curradvy += clusy;
+      }
+      pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+      pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
       curradvx -= pPos->x_advance;
-    pPos->x_offset = gr_slot_origin_X (is) - curradvx;
-    if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-      curradvx += pPos->x_advance;
-    pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
-    curradvy += pPos->y_advance;
+      curradvy -= pPos->y_advance;
+      pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+      pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
+    }
+    hb_buffer_reverse_clusters (buffer);
   }
-  if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-    pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
-
-  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-    hb_buffer_reverse_clusters (buffer);
 
   if (feats) gr_featureval_destroy (feats);
   gr_seg_destroy (seg);
 
   return true;
 }
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -215,17 +215,17 @@ hb_ot_get_glyph_h_advance (hb_font_t *fo
 
 static hb_position_t
 hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_y (-ot_font->v_metrics.get_advance (glyph));
+  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph));
 }
 
 static hb_bool_t
 hb_ot_get_glyph_h_origin (hb_font_t *font HB_UNUSED,
 			  void *font_data HB_UNUSED,
 			  hb_codepoint_t glyph HB_UNUSED,
 			  hb_position_t *x HB_UNUSED,
 			  hb_position_t *y HB_UNUSED,
@@ -330,16 +330,19 @@ static hb_font_funcs_t *
 #undef HB_FONT_FUNC_IMPLEMENT
     }
   };
 
   return const_cast<hb_font_funcs_t *> (&ot_ffuncs);
 }
 
 
+/**
+ * Since: 0.9.28
+ **/
 void
 hb_ot_font_set_funcs (hb_font_t *font)
 {
   hb_ot_font_t *ot_font = _hb_ot_font_create (font);
   if (unlikely (!ot_font))
     return;
 
   hb_font_set_funcs (font,
--- a/gfx/harfbuzz/src/hb-ot-font.h
+++ b/gfx/harfbuzz/src/hb-ot-font.h
@@ -19,16 +19,20 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
 #ifndef HB_OT_FONT_H
 #define HB_OT_FONT_H
 
 #include "hb.h"
 
 HB_BEGIN_DECLS
 
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -323,18 +323,17 @@ struct hb_apply_context_t
     may_skip (const hb_apply_context_t *c,
 	      const hb_glyph_info_t    &info) const
     {
       if (!c->check_glyph_property (&info, lookup_props))
 	return SKIP_YES;
 
       if (unlikely (_hb_glyph_info_is_default_ignorable (&info) &&
 		    (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) &&
-		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) &&
-		    !_hb_glyph_info_ligated (&info)))
+		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info))))
 	return SKIP_MAYBE;
 
       return SKIP_NO;
     }
 
     protected:
     unsigned int lookup_props;
     bool ignore_zwnj;
@@ -715,17 +714,17 @@ static inline bool match_input (hb_apply
 				const void *match_data,
 				unsigned int *end_offset,
 				unsigned int match_positions[MAX_CONTEXT_LENGTH],
 				bool *p_is_mark_ligature = NULL,
 				unsigned int *p_total_component_count = NULL)
 {
   TRACE_APPLY (NULL);
 
-  if (unlikely (count > MAX_CONTEXT_LENGTH)) TRACE_RETURN (false);
+  if (unlikely (count > MAX_CONTEXT_LENGTH)) return TRACE_RETURN (false);
 
   hb_buffer_t *buffer = c->buffer;
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
   skippy_iter.reset (buffer->idx, count - 1);
   skippy_iter.set_match_func (match_func, match_data, input);
 
   /*
@@ -2160,17 +2159,17 @@ struct ExtensionFormat1
     return StructAtOffset<typename T::LookupSubTable> (this, offset);
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, format);
     if (unlikely (!c->may_dispatch (this, this))) TRACE_RETURN (c->default_return_value ());
-    return get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ());
+    return TRACE_RETURN (get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ()));
   }
 
   /* This is called from may_dispatch() above with hb_sanitize_context_t. */
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && extensionOffset != 0);
   }
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -31,16 +31,25 @@
 
 #include "hb-private.hh"
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-set-private.hh"
 
 
+/* Private API corresponding to hb-ot-layout.h: */
+
+HB_INTERNAL hb_bool_t
+hb_ot_layout_table_find_feature (hb_face_t    *face,
+				 hb_tag_t      table_tag,
+				 hb_tag_t      feature_tag,
+				 unsigned int *feature_index);
+
+
 /*
  * GDEF
  */
 
 typedef enum
 {
   /* The following three match LookupFlags::Ignore* numbers. */
   HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH	= 0x02u,
@@ -174,16 +183,40 @@ HB_INTERNAL void
 #define unicode_props0()	var2.u8[0]
 #define unicode_props1()	var2.u8[1]
 
 /* buffer var allocations, used during the GSUB/GPOS processing */
 #define glyph_props()		var1.u16[0] /* GDEF glyph properties */
 #define lig_props()		var1.u8[2] /* GSUB/GPOS ligature tracking */
 #define syllable()		var1.u8[3] /* GSUB/GPOS shaping boundaries */
 
+
+/* loop over syllables */
+
+#define foreach_syllable(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_syllable (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_syllable (buffer, start))
+
+static inline unsigned int
+_next_syllable (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  unsigned int syllable = info[start].syllable();
+  while (++start < count && syllable == info[start].syllable())
+    ;
+
+  return start;
+}
+
+
 /* unicode_props */
 
 enum {
   MASK0_ZWJ       = 0x20u,
   MASK0_ZWNJ      = 0x40u,
   MASK0_IGNORABLE = 0x80u,
   MASK0_GEN_CAT   = 0x1Fu
 };
@@ -220,20 +253,22 @@ static inline void
 }
 
 static inline unsigned int
 _hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
 {
   return info->unicode_props1();
 }
 
+static inline bool _hb_glyph_info_ligated (const hb_glyph_info_t *info);
+
 static inline hb_bool_t
 _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 {
-  return !!(info->unicode_props0() & MASK0_IGNORABLE);
+  return (info->unicode_props0() & MASK0_IGNORABLE) && !_hb_glyph_info_ligated (info);
 }
 
 static inline hb_bool_t
 _hb_glyph_info_is_zwnj (const hb_glyph_info_t *info)
 {
   return !!(info->unicode_props0() & MASK0_ZWNJ);
 }
 
@@ -401,16 +436,24 @@ static inline bool
 
 static inline void
 _hb_glyph_info_clear_ligated_and_multiplied (hb_glyph_info_t *info)
 {
   info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_LIGATED |
 			   HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED);
 }
 
+static inline void
+_hb_glyph_info_clear_substituted_and_ligated_and_multiplied (hb_glyph_info_t *info)
+{
+  info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED |
+			   HB_OT_LAYOUT_GLYPH_PROPS_LIGATED |
+			   HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED);
+}
+
 
 /* Allocation / deallocation. */
 
 static inline void
 _hb_buffer_allocate_unicode_vars (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props0);
   HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props1);
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -123,23 +123,29 @@ static inline const OT::GPOS&
  */
 
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face)
 {
   return _get_gdef (face).has_glyph_classes ();
 }
 
+/**
+ * Since: 0.9.7
+ **/
 hb_ot_layout_glyph_class_t
 hb_ot_layout_get_glyph_class (hb_face_t      *face,
 			      hb_codepoint_t  glyph)
 {
   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
 }
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
 				  hb_ot_layout_glyph_class_t  klass,
 				  hb_set_t                   *glyphs /* OUT */)
 {
   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
 }
 
@@ -280,16 +286,38 @@ hb_ot_layout_table_get_feature_tags (hb_
 				     unsigned int *feature_count /* IN/OUT */,
 				     hb_tag_t     *feature_tags /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 }
 
+hb_bool_t
+hb_ot_layout_table_find_feature (hb_face_t    *face,
+				 hb_tag_t      table_tag,
+				 hb_tag_t      feature_tag,
+				 unsigned int *feature_index)
+{
+  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  unsigned int num_features = g.get_feature_count ();
+  for (unsigned int i = 0; i < num_features; i++)
+  {
+    if (feature_tag == g.get_feature_tag (i)) {
+      if (feature_index) *feature_index = i;
+      return true;
+    }
+  }
+
+  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
+  return false;
+}
+
 
 unsigned int
 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
 				       hb_tag_t      table_tag,
 				       unsigned int  script_index,
 				       unsigned int  start_offset,
 				       unsigned int *language_count /* IN/OUT */,
 				       hb_tag_t     *language_tags /* OUT */)
@@ -330,16 +358,19 @@ hb_ot_layout_language_get_required_featu
   return hb_ot_layout_language_get_required_feature (face,
 						     table_tag,
 						     script_index,
 						     language_index,
 						     feature_index,
 						     NULL);
 }
 
+/**
+ * Since: 0.9.30
+ **/
 hb_bool_t
 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
 					    hb_tag_t      table_tag,
 					    unsigned int  script_index,
 					    unsigned int  language_index,
 					    unsigned int *feature_index,
 					    hb_tag_t     *feature_tag)
 {
@@ -414,30 +445,36 @@ hb_ot_layout_language_find_feature (hb_f
       return true;
     }
   }
 
   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
   return false;
 }
 
+/**
+ * Since: 0.9.7
+ **/
 unsigned int
 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
 				  hb_tag_t      table_tag,
 				  unsigned int  feature_index,
 				  unsigned int  start_offset,
 				  unsigned int *lookup_count /* IN/OUT */,
 				  unsigned int *lookup_indexes /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   const OT::Feature &f = g.get_feature (feature_index);
 
   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
 }
 
+/**
+ * Since: 0.9.22
+ **/
 unsigned int
 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 				     hb_tag_t      table_tag)
 {
   switch (table_tag)
   {
     case HB_OT_TAG_GSUB:
     {
@@ -585,16 +622,19 @@ static void
 						script_index,
 						language_index,
 						features,
 						lookup_indexes);
     }
   }
 }
 
+/**
+ * Since: 0.9.8
+ **/
 void
 hb_ot_layout_collect_lookups (hb_face_t      *face,
 			      hb_tag_t        table_tag,
 			      const hb_tag_t *scripts,
 			      const hb_tag_t *languages,
 			      const hb_tag_t *features,
 			      hb_set_t       *lookup_indexes /* OUT */)
 {
@@ -626,16 +666,19 @@ hb_ot_layout_collect_lookups (hb_face_t 
 						 script_index,
 						 languages,
 						 features,
 						 lookup_indexes);
     }
   }
 }
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  lookup_index,
 				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
 				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
 				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
 				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
@@ -671,16 +714,19 @@ hb_ot_layout_lookup_collect_glyphs (hb_f
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
   return &_get_gsub (face) != &OT::Null(OT::GSUB);
 }
 
+/**
+ * Since: 0.9.7
+ **/
 hb_bool_t
 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 				      unsigned int          lookup_index,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
 				      hb_bool_t             zero_context)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
@@ -709,16 +755,19 @@ hb_ot_layout_substitute_start (hb_font_t
 }
 
 void
 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GSUB::substitute_finish (font, buffer);
 }
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 				        unsigned int  lookup_index,
 				        hb_set_t     *glyphs)
 {
   OT::hb_closure_context_t c (face, glyphs);
 
   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
@@ -743,16 +792,19 @@ hb_ot_layout_position_start (hb_font_t *
 }
 
 void
 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GPOS::position_finish (font, buffer);
 }
 
+/**
+ * Since: 0.9.8
+ **/
 hb_bool_t
 hb_ot_layout_get_size_params (hb_face_t    *face,
 			      unsigned int *design_size,       /* OUT.  May be NULL */
 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
 			      unsigned int *range_start,       /* OUT.  May be NULL */
 			      unsigned int *range_end          /* OUT.  May be NULL */)
 {
@@ -870,17 +922,17 @@ apply_backward (OT::hb_apply_context_t *
 
   }
   while ((int) buffer->idx >= 0);
   return ret;
 }
 
 struct hb_apply_forward_context_t
 {
-  inline const char *get_name (void) { return "APPLY_FORWARD"; }
+  inline const char *get_name (void) { return "APPLY_FWD"; }
   static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
   typedef bool return_t;
   template <typename T, typename F>
   inline bool may_dispatch (const T *obj, const F *format) { return true; }
   template <typename T>
   inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; }
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -149,19 +149,20 @@ struct hb_ot_map_t
 
   hb_prealloced_array_t<feature_map_t, 8> features;
   hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<stage_map_t, 4> stages[2]; /* GSUB/GPOS */
 };
 
 enum hb_ot_map_feature_flags_t {
   F_NONE		= 0x0000u,
-  F_GLOBAL		= 0x0001u,
-  F_HAS_FALLBACK	= 0x0002u,
-  F_MANUAL_ZWJ		= 0x0004u
+  F_GLOBAL		= 0x0001u, /* Feature applies to all characters; results in no mask allocated for it. */
+  F_HAS_FALLBACK	= 0x0002u, /* Has fallback implementation, so include mask bit even if feature not found. */
+  F_MANUAL_ZWJ		= 0x0004u, /* Don't skip over ZWJ when matching. */
+  F_GLOBAL_SEARCH	= 0x0008u  /* If feature not found in LangSys, look for it in global feature list and pick one. */
 };
 /* Macro version for where const is desired. */
 #define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r)))
 static inline hb_ot_map_feature_flags_t
 operator | (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r)
 { return hb_ot_map_feature_flags_t ((unsigned int) l | (unsigned int) r); }
 static inline hb_ot_map_feature_flags_t
 operator & (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r)
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -211,16 +211,26 @@ hb_ot_map_builder_t::compile (hb_ot_map_
       }
       found |= hb_ot_layout_language_find_feature (face,
 						   table_tags[table_index],
 						   script_index[table_index],
 						   language_index[table_index],
 						   info->tag,
 						   &feature_index[table_index]);
     }
+    if (!found && (info->flags & F_GLOBAL_SEARCH))
+    {
+      for (unsigned int table_index = 0; table_index < 2; table_index++)
+      {
+	found |= hb_ot_layout_table_find_feature (face,
+						  table_tags[table_index],
+						  info->tag,
+						  &feature_index[table_index]);
+      }
+    }
     if (!found && !(info->flags & F_HAS_FALLBACK))
       continue;
 
 
     hb_ot_map_t::feature_map_t *map = m.features.push ();
     if (unlikely (!map))
       break;
 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+struct arabic_shape_plan_t;
+
+HB_INTERNAL void *
+data_create_arabic (const hb_ot_shape_plan_t *plan);
+
+HB_INTERNAL void
+data_destroy_arabic (void *data);
+
+HB_INTERNAL void
+setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
+			 hb_buffer_t               *buffer,
+			 hb_script_t                script);
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
@@ -1,20 +1,20 @@
 /* == Start of generated table == */
 /*
  * The following table is generated by running:
  *
  *   ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
  *
  * on files with these headers:
  *
- * # ArabicShaping-7.0.0.txt
- * # Date: 2014-02-14, 21:00:00 GMT [RP, KW, LI]
- * # Blocks-7.0.0.txt
- * # Date: 2014-04-03, 23:23:00 GMT [RP, KW]
+ * # ArabicShaping-8.0.0.txt
+ * # Date: 2015-02-17, 23:33:00 GMT [RP]
+ * # Blocks-8.0.0.txt
+ * # Date: 2014-11-10, 23:04:00 GMT [KW]
  * UnicodeData.txt does not have a header.
  */
 
 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
 #define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
 
 
 #define X	JOINING_TYPE_X
@@ -71,72 +71,72 @@ static const uint8_t joining_table[] =
   /* Mandaic */
 
   /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X,
   /* 0860 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 0880 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
 
   /* Arabic Extended-A */
 
-  /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,
+  /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,
 
-#define joining_offset_0x1806u 691
+#define joining_offset_0x1806u 693
 
   /* Mongolian */
 
   /* 1800 */             U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,
   /* 1880 */ U,U,U,U,U,U,U,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 18A0 */ D,D,D,D,D,D,D,D,D,X,D,
 
-#define joining_offset_0x200cu 856
+#define joining_offset_0x200cu 858
 
   /* General Punctuation */
 
   /* 2000 */                         U,C,
 
-#define joining_offset_0x2066u 858
+#define joining_offset_0x2066u 860
 
   /* General Punctuation */
 
   /* 2060 */             U,U,U,U,
 
-#define joining_offset_0xa840u 862
+#define joining_offset_0xa840u 864
 
   /* Phags-pa */
 
   /* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U,
 
-#define joining_offset_0x10ac0u 914
+#define joining_offset_0x10ac0u 916
 
   /* Manichaean */
 
   /* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D,
   /* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R,
 
-#define joining_offset_0x10b80u 962
+#define joining_offset_0x10b80u 964
 
   /* Psalter Pahlavi */
 
   /* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U,
 
-}; /* Table items: 1010; occupancy: 57% */
+}; /* Table items: 1012; occupancy: 57% */
 
 
 static unsigned int
 joining_type (hb_codepoint_t u)
 {
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0600u, 0x08B2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
+      if (hb_in_range (u, 0x0600u, 0x08B4u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
       break;
 
     case 0x1u:
       if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
       break;
 
     case 0x2u:
       if (hb_in_range (u, 0x200Cu, 0x200Du)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -137,26 +137,26 @@
 
 #define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \
 	OT_SUBLOOKUP(Name, 2, \
 		OT_OFFSET(Name, Name##Coverage) \
 		OT_LABEL_END \
 		OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \
 	) \
 	OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \
-	/* ASSERT_STATIC_EXPR len(FromGlyphs) == len(ToGlyphs) */
+	/* ASSERT_STATIC_EXPR_ZERO (len(FromGlyphs) == len(ToGlyphs)) */
 
 #define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \
 	OT_SUBLOOKUP(Name, 1, \
 		OT_OFFSET(Name, Name##Coverage) \
 		OT_LABEL_END \
 		OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \
 	) \
 	OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \
-	/* ASSERT_STATIC_EXPR len(FirstGlyphs) == len(LigatureSetOffsets) */
+	/* ASSERT_STATIC_EXPR_ZERO (len(FirstGlyphs) == len(LigatureSetOffsets)) */
 
 #define OT_LIGATURE_SET(Name, LigatureSetOffsets) \
 	OT_UARRAY(Name, OT_LIST(LigatureSetOffsets))
 
 #define OT_LIGATURE(Name, Components, LigGlyph) \
 	OT_LABEL_START(Name) \
 	LigGlyph \
 	OT_LABEL_END \
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -19,54 +19,54 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex-arabic-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 
 /*
+ * Joining types:
+ */
+
+/*
  * Bits used in the joining tables
  */
-enum {
+enum hb_arabic_joining_type_t {
   JOINING_TYPE_U		= 0,
   JOINING_TYPE_L		= 1,
   JOINING_TYPE_R		= 2,
   JOINING_TYPE_D		= 3,
   JOINING_TYPE_C		= JOINING_TYPE_D,
   JOINING_GROUP_ALAPH		= 4,
   JOINING_GROUP_DALATH_RISH	= 5,
   NUM_STATE_MACHINE_COLS	= 6,
 
   JOINING_TYPE_T = 7,
   JOINING_TYPE_X = 8  /* means: use general-category to choose between U or T. */
 };
 
-/*
- * Joining types:
- */
-
 #include "hb-ot-shape-complex-arabic-table.hh"
 
 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
 {
   unsigned int j_type = joining_type(u);
   if (likely (j_type != JOINING_TYPE_X))
     return j_type;
 
-  return (FLAG(gen_cat) &
+  return (FLAG_SAFE(gen_cat) &
 	  (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) |
 	   FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
 	   FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))
 	 ) ?  JOINING_TYPE_T : JOINING_TYPE_U;
 }
 
 #define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3')
 
@@ -207,17 +207,17 @@ struct arabic_shape_plan_t
    * having to do a "if (... < NONE) ..." and just rely on the fact that
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
 
   bool do_fallback;
   arabic_fallback_plan_t *fallback_plan;
 };
 
-static void *
+void *
 data_create_arabic (const hb_ot_shape_plan_t *plan)
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t));
   if (unlikely (!arabic_plan))
     return NULL;
 
   arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
@@ -225,17 +225,17 @@ data_create_arabic (const hb_ot_shape_pl
     arabic_plan->do_fallback = arabic_plan->do_fallback &&
 			       (FEATURE_IS_SYRIAC (arabic_features[i]) ||
 			        plan->map.needs_fallback (arabic_features[i]));
   }
 
   return arabic_plan;
 }
 
-static void
+void
 data_destroy_arabic (void *data)
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
   arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
 
   free (data);
 }
@@ -300,37 +300,44 @@ mongolian_variation_selectors (hb_buffer
   /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
     if (unlikely (hb_in_range (info[i].codepoint, 0x180Bu, 0x180Du)))
       info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action();
 }
 
-static void
-setup_masks_arabic (const hb_ot_shape_plan_t *plan,
-		    hb_buffer_t              *buffer,
-		    hb_font_t                *font HB_UNUSED)
+void
+setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
+			 hb_buffer_t               *buffer,
+			 hb_script_t                script)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
-  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
-
   arabic_joining (buffer);
-  if (plan->props.script == HB_SCRIPT_MONGOLIAN)
+  if (script == HB_SCRIPT_MONGOLIAN)
     mongolian_variation_selectors (buffer);
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
     info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()];
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
+static void
+setup_masks_arabic (const hb_ot_shape_plan_t *plan,
+		    hb_buffer_t              *buffer,
+		    hb_font_t                *font HB_UNUSED)
+{
+  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+  setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script);
+}
+
 
 static void
 nuke_joiners (const hb_ot_shape_plan_t *plan HB_UNUSED,
 	      hb_font_t *font HB_UNUSED,
 	      hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -204,23 +204,18 @@ preprocess_text_hangul (const hb_ot_shap
 	/* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
 	buffer->next_glyph ();
 	if (!is_zero_width_char (font, u))
 	{
 	  hb_glyph_info_t *info = buffer->out_info;
 	  hb_glyph_info_t tone = info[end];
 	  memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t));
 	  info[start] = tone;
+	  buffer->merge_out_clusters (start, end + 1);
 	}
-	/* Merge clusters across the (possibly reordered) syllable+tone.
-	 * We want to merge even in the zero-width tone mark case here,
-	 * so that clustering behavior isn't dependent on how the tone mark
-	 * is handled by the font.
-	 */
-	buffer->merge_out_clusters (start, end + 1);
       }
       else
       {
 	/* No valid syllable as base for tone mark; try to insert dotted circle. */
 	if (font->has_glyph (0x25CCu))
 	{
 	  hb_codepoint_t chars[2];
 	  if (!is_zero_width_char (font, u)) {
@@ -291,17 +286,18 @@ preprocess_text_hangul (const hb_ot_shap
 	if (t)
 	{
 	  buffer->cur().hangul_shaping_feature() = TJMO;
 	  buffer->next_glyph ();
 	  end = start + 3;
 	}
 	else
 	  end = start + 2;
-	buffer->merge_out_clusters (start, end);
+	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	  buffer->merge_out_clusters (start, end);
 	continue;
       }
     }
 
     else if (isCombinedS (u))
     {
       /* Have <LV>, <LVT>, or <LV,T> */
       hb_codepoint_t s = u;
@@ -363,17 +359,18 @@ preprocess_text_hangul (const hb_ot_shap
           }
           end = start + s_len;
 
 	  unsigned int i = start;
 	  info[i++].hangul_shaping_feature() = LJMO;
 	  info[i++].hangul_shaping_feature() = VJMO;
 	  if (i < end)
 	    info[i++].hangul_shaping_feature() = TJMO;
-	  buffer->merge_out_clusters (start, end);
+	  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	    buffer->merge_out_clusters (start, end);
 	  continue;
 	}
       }
 
       if (has_glyph)
       {
         /* We didn't decompose the S, so just advance past it. */
 	end = start + 1;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -156,18 +156,16 @@ enum indic_matra_category_t {
   INDIC_MATRA_CATEGORY_TOP_AND_LEFT			= INDIC_MATRA_CATEGORY_TOP,
   INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT		= INDIC_MATRA_CATEGORY_RIGHT,
   INDIC_MATRA_CATEGORY_TOP_AND_RIGHT			= INDIC_MATRA_CATEGORY_RIGHT,
 
   INDIC_MATRA_CATEGORY_OVERSTRUCK			= POS_AFTER_MAIN,
   INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT		= POS_PRE_M
 };
 
-/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
- * because gcc fails to optimize the latter and fills the table in at runtime. */
 #define INDIC_COMBINE_CATEGORIES(S,M) \
   (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || \
 			    ( \
 			     S == INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL || \
 			     S == INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK || \
 			     S == INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER || \
 			     S == INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA || \
 			     S == INDIC_SYLLABIC_CATEGORY_VIRAMA || \
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -137,17 +137,17 @@ is_ra (hb_codepoint_t u)
   return false;
 }
 
 static inline bool
 is_one_of (const hb_glyph_info_t &info, unsigned int flags)
 {
   /* If it ligated, all bets are off. */
   if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG (info.indic_category()) & flags);
+  return !!(FLAG_SAFE (info.indic_category()) & flags);
 }
 
 static inline bool
 is_joiner (const hb_glyph_info_t &info)
 {
   return is_one_of (info, JOINER_FLAGS);
 }
 
@@ -232,27 +232,27 @@ set_indic_properties (hb_glyph_info_t &i
   else if (unlikely (u == 0xA9BEu)) cat = OT_CM2; /* Javanese medial ya. */
   else if (unlikely (u == 0xA9BDu)) { cat = OT_M; pos = POS_POST_C; } /* Javanese vocalic r. */
 
 
   /*
    * Re-assign position.
    */
 
-  if ((FLAG (cat) & CONSONANT_FLAGS))
+  if ((FLAG_SAFE (cat) & CONSONANT_FLAGS))
   {
     pos = POS_BASE_C;
     if (is_ra (u))
       cat = OT_Ra;
   }
   else if (cat == OT_M)
   {
     pos = matra_position (u, pos);
   }
-  else if ((FLAG (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol))))
+  else if ((FLAG_SAFE (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol))))
   {
     pos = POS_SMVD;
   }
 
   if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
 
 
 
@@ -958,17 +958,17 @@ initial_reordering_consonant_syllable (c
       }
   }
 
   /* Attach misc marks to previous char to move with them. */
   {
     indic_position_t last_pos = POS_START;
     for (unsigned int i = start; i < end; i++)
     {
-      if ((FLAG (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS)))
+      if ((FLAG_SAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS)))
       {
 	info[i].indic_position() = last_pos;
 	if (unlikely (info[i].indic_category() == OT_H &&
 		      info[i].indic_position() == POS_PRE_M))
 	{
 	  /*
 	   * Uniscribe doesn't move the Halant with Left Matra.
 	   * TEST: U+092B,U+093F,U+094DE
@@ -1156,27 +1156,16 @@ initial_reordering_consonant_syllable (c
 	/* A ZWNJ disables HALF. */
 	if (non_joiner)
 	  info[j].mask &= ~indic_plan->mask_array[HALF];
 
       } while (j > start && !is_consonant (info[j]));
     }
 }
 
-
-static void
-initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We made the vowels look like consonants.  So let's call the consonant logic! */
-  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
-}
-
 static void
 initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
 				       hb_face_t *face,
 				       hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
   /* We treat placeholder/dotted-circle as if they are consonants, so we
    * should just chain.  Only if not in compatibility mode that is... */
@@ -1189,60 +1178,37 @@ initial_reordering_standalone_cluster (c
     if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
       return;
   }
 
   initial_reordering_consonant_syllable (plan, face, buffer, start, end);
 }
 
 static void
-initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We already inserted dotted-circles, so just call the standalone_cluster. */
-  initial_reordering_standalone_cluster (plan, face, buffer, start, end);
-}
-
-static void
-initial_reordering_symbol_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-				   hb_face_t *face HB_UNUSED,
-				   hb_buffer_t *buffer HB_UNUSED,
-				   unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-static void
-initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-				      hb_face_t *face HB_UNUSED,
-				      hb_buffer_t *buffer HB_UNUSED,
-				      unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-
-static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			     hb_face_t *face,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
-  switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable (plan, face, buffer, start, end); return;
-  case vowel_syllable:		initial_reordering_vowel_syllable     (plan, face, buffer, start, end); return;
-  case standalone_cluster:	initial_reordering_standalone_cluster (plan, face, buffer, start, end); return;
-  case symbol_cluster:		initial_reordering_symbol_cluster     (plan, face, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster     (plan, face, buffer, start, end); return;
-  case non_indic_cluster:	initial_reordering_non_indic_cluster  (plan, face, buffer, start, end); return;
+  switch (syllable_type)
+  {
+    case vowel_syllable: /* We made the vowels look like consonants.  So let's call the consonant logic! */
+    case consonant_syllable:
+     initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+     break;
+
+    case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */
+    case standalone_cluster:
+     initial_reordering_standalone_cluster (plan, face, buffer, start, end);
+     break;
+
+    case symbol_cluster:
+    case non_indic_cluster:
+      break;
   }
 }
 
 static inline void
 insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
@@ -1305,28 +1271,18 @@ insert_dotted_circles (const hb_ot_shape
 static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer)
 {
   update_consonant_positions (plan, font, buffer);
   insert_dotted_circles (plan, font, buffer);
 
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, font->face, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  initial_reordering_syllable (plan, font->face, buffer, last, count);
+  foreach_syllable (buffer, start, end)
+    initial_reordering_syllable (plan, font->face, buffer, start, end);
 }
 
 static void
 final_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t *buffer,
 			   unsigned int start, unsigned int end)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
@@ -1545,17 +1501,17 @@ final_reordering_syllable (const hb_ot_s
      *          consonant is found, the target position should be before the
      *          first matra, syllable modifier sign or vedic sign.
      */
     /* This is our take on what step 4 is trying to say (and failing, BADLY). */
     if (reph_pos == REPH_POS_AFTER_SUB)
     {
       new_reph_pos = base;
       while (new_reph_pos < end &&
-	     !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
+	     !( FLAG_SAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
 	new_reph_pos++;
       if (new_reph_pos < end)
         goto reph_move;
     }
 
     /*       5. If no consonant is found in steps 3 or 4, move reph to a position
      *          immediately before the first post-base matra, syllable modifier
      *          sign or vedic sign that has a reordering class after the intended
@@ -1696,17 +1652,17 @@ final_reordering_syllable (const hb_ot_s
         break;
       }
   }
 
 
   /* Apply 'init' to the Left Matra if it's a word start. */
   if (info[start].indic_position () == POS_PRE_M &&
       (!start ||
-       !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) &
+       !(FLAG_SAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
 	 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
     info[start].mask |= indic_plan->mask_array[INIT];
 
 
   /*
    * Finish off the clusters and go home!
    */
   if (hb_options ().uniscribe_bug_compatible)
@@ -1732,26 +1688,18 @@ final_reordering_syllable (const hb_ot_s
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font HB_UNUSED,
 		  hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
 
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      final_reordering_syllable (plan, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  final_reordering_syllable (plan, buffer, last, count);
+  foreach_syllable (buffer, start, end)
+    final_reordering_syllable (plan, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
 
 
 static void
 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -149,17 +149,17 @@ enum myanmar_category_t {
 };
 
 
 static inline bool
 is_one_of (const hb_glyph_info_t &info, unsigned int flags)
 {
   /* If it ligated, all bets are off. */
   if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG (info.myanmar_category()) & flags);
+  return !!(FLAG_SAFE (info.myanmar_category()) & flags);
 }
 
 static inline bool
 is_consonant (const hb_glyph_info_t &info)
 {
   return is_one_of (info, CONSONANT_FLAGS);
 }
 
@@ -299,19 +299,17 @@ compare_myanmar_order (const hb_glyph_in
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
 
 /* Rules from:
  * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm */
 
 static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
-				       hb_face_t *face,
-				       hb_buffer_t *buffer,
+initial_reordering_consonant_syllable (hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
   hb_glyph_info_t *info = buffer->info;
 
   unsigned int base = end;
   bool has_reph = false;
 
   {
@@ -394,58 +392,32 @@ initial_reordering_consonant_syllable (c
   }
 
   buffer->merge_clusters (start, end);
   /* Sit tight, rock 'n roll! */
   hb_bubble_sort (info + start, end - start, compare_myanmar_order);
 }
 
 static void
-initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We already inserted dotted-circles, so just call the consonant_syllable. */
-  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
-}
-
-static void
-initial_reordering_punctuation_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-					hb_face_t *face HB_UNUSED,
-					hb_buffer_t *buffer HB_UNUSED,
-					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-static void
-initial_reordering_non_myanmar_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-					hb_face_t *face HB_UNUSED,
-					hb_buffer_t *buffer HB_UNUSED,
-					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-
-static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			     hb_face_t *face,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
-  case punctuation_cluster:	initial_reordering_punctuation_cluster (plan, face, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
-  case non_myanmar_cluster:	initial_reordering_non_myanmar_cluster (plan, face, buffer, start, end); return;
+
+    case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
+    case consonant_syllable:
+      initial_reordering_consonant_syllable  (buffer, start, end);
+      break;
+
+    case punctuation_cluster:
+    case non_myanmar_cluster:
+      break;
   }
 }
 
 static inline void
 insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
@@ -500,28 +472,18 @@ insert_dotted_circles (const hb_ot_shape
 
 static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer)
 {
   insert_dotted_circles (plan, font, buffer);
 
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, font->face, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  initial_reordering_syllable (plan, font->face, buffer, last, count);
+  foreach_syllable (buffer, start, end)
+    initial_reordering_syllable (plan, font->face, buffer, start, end);
 }
 
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font HB_UNUSED,
 		  hb_buffer_t *buffer)
 {
   hb_glyph_info_t *info = buffer->info;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -54,19 +54,19 @@ enum hb_ot_shape_zero_width_marks_type_t
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (sea) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (use) \
   /* ^--- Add new shapers here */
 
 
 struct hb_ot_complex_shaper_t
 {
   char name[8];
 
   /* collect_features()
@@ -212,109 +212,40 @@ hb_ot_shape_complex_categorize (const hb
     /* Unicode-1.1 additions */
     case HB_SCRIPT_HEBREW:
 
       return &_hb_ot_complex_shaper_hebrew;
 
 
     /* ^--- Add new shapers here */
 
-
 #if 0
-    /* Note:
-     *
-     * These disabled scripts are listed in ucd/IndicSyllabicCategory.txt, but according
-     * to Martin Hosken and Jonathan Kew do not require complex shaping.
-     *
-     * TODO We should automate figuring out which scripts do not need complex shaping
-     *
-     * TODO We currently keep data for these scripts in our indic table.  Need to fix the
-     * generator to not do that.
-     */
-
-
-    /* Simple? */
-
-    /* Unicode-3.2 additions */
-    case HB_SCRIPT_BUHID:
-    case HB_SCRIPT_HANUNOO:
-
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_SAURASHTRA:
-
-    /* Unicode-6.0 additions */
-    case HB_SCRIPT_BATAK:
-    case HB_SCRIPT_BRAHMI:
-
-
-    /* Simple */
-
-    /* Unicode-1.1 additions */
-    /* These have their own shaper now. */
-    case HB_SCRIPT_LAO:
-    case HB_SCRIPT_THAI:
-
-    /* Unicode-3.2 additions */
-    case HB_SCRIPT_TAGALOG:
-    case HB_SCRIPT_TAGBANWA:
-
-    /* Unicode-4.0 additions */
-    case HB_SCRIPT_LIMBU:
-    case HB_SCRIPT_TAI_LE:
-
     /* Unicode-4.1 additions */
-    case HB_SCRIPT_KHAROSHTHI:
     case HB_SCRIPT_NEW_TAI_LUE:
-    case HB_SCRIPT_SYLOTI_NAGRI:
-
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_KAYAH_LI:
-
-    /* Unicode-5.2 additions */
-    case HB_SCRIPT_TAI_VIET:
-
-
 #endif
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_BENGALI:
     case HB_SCRIPT_DEVANAGARI:
     case HB_SCRIPT_GUJARATI:
     case HB_SCRIPT_GURMUKHI:
     case HB_SCRIPT_KANNADA:
     case HB_SCRIPT_MALAYALAM:
     case HB_SCRIPT_ORIYA:
     case HB_SCRIPT_TAMIL:
     case HB_SCRIPT_TELUGU:
 
     /* Unicode-3.0 additions */
     case HB_SCRIPT_SINHALA:
 
-    /* Unicode-5.0 additions */
-    case HB_SCRIPT_BALINESE:
-
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_LEPCHA:
-    case HB_SCRIPT_REJANG:
-    case HB_SCRIPT_SUNDANESE:
-
     /* Unicode-5.2 additions */
     case HB_SCRIPT_JAVANESE:
-    case HB_SCRIPT_KAITHI:
-    case HB_SCRIPT_MEETEI_MAYEK:
-
-    /* Unicode-6.0 additions */
-
-    /* Unicode-6.1 additions */
-    case HB_SCRIPT_CHAKMA:
-    case HB_SCRIPT_SHARADA:
-    case HB_SCRIPT_TAKRI:
 
       /* If the designer designed the font for the 'DFLT' script,
-       * use the default shaper.  Otherwise, use the Indic shaper.
+       * use the default shaper.  Otherwise, use the specific shaper.
        * Note that for some simple scripts, there may not be *any*
        * GSUB/GPOS needed, so there may be no scripts found! */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
 	return &_hb_ot_complex_shaper_default;
       else
 	return &_hb_ot_complex_shaper_indic;
 
     case HB_SCRIPT_KHMER:
@@ -336,30 +267,89 @@ hb_ot_shape_complex_categorize (const hb
     case HB_SCRIPT_MYANMAR:
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
 	return &_hb_ot_complex_shaper_myanmar;
       else if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
 	return &_hb_ot_complex_shaper_myanmar_old;
       else
 	return &_hb_ot_complex_shaper_default;
 
+
+    /* Unicode-2.0 additions */
+    //case HB_SCRIPT_TIBETAN:
+
+    /* Unicode-3.0 additions */
+    //case HB_SCRIPT_MONGOLIAN:
+    //case HB_SCRIPT_SINHALA:
+
+    /* Unicode-3.2 additions */
+    case HB_SCRIPT_BUHID:
+    case HB_SCRIPT_HANUNOO:
+    case HB_SCRIPT_TAGALOG:
+    case HB_SCRIPT_TAGBANWA:
+
+    /* Unicode-4.0 additions */
+    case HB_SCRIPT_LIMBU:
+    case HB_SCRIPT_TAI_LE:
+
     /* Unicode-4.1 additions */
     case HB_SCRIPT_BUGINESE:
+    case HB_SCRIPT_KHAROSHTHI:
+    case HB_SCRIPT_SYLOTI_NAGRI:
+    case HB_SCRIPT_TIFINAGH:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_BALINESE:
+    //case HB_SCRIPT_NKO:
+    //case HB_SCRIPT_PHAGS_PA:
 
     /* Unicode-5.1 additions */
     case HB_SCRIPT_CHAM:
+    case HB_SCRIPT_KAYAH_LI:
+    case HB_SCRIPT_LEPCHA:
+    case HB_SCRIPT_REJANG:
+    case HB_SCRIPT_SAURASHTRA:
+    case HB_SCRIPT_SUNDANESE:
 
     /* Unicode-5.2 additions */
+    case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS:
+    //case HB_SCRIPT_JAVANESE:
+    case HB_SCRIPT_KAITHI:
+    case HB_SCRIPT_MEETEI_MAYEK:
     case HB_SCRIPT_TAI_THAM:
+    case HB_SCRIPT_TAI_VIET:
+
+    /* Unicode-6.0 additions */
+    case HB_SCRIPT_BATAK:
+    case HB_SCRIPT_BRAHMI:
+    //case HB_SCRIPT_MANDAIC:
+
+    /* Unicode-6.1 additions */
+    case HB_SCRIPT_CHAKMA:
+    case HB_SCRIPT_SHARADA:
+    case HB_SCRIPT_TAKRI:
+
+    /* Unicode-7.0 additions */
+    case HB_SCRIPT_DUPLOYAN:
+    case HB_SCRIPT_GRANTHA:
+    case HB_SCRIPT_KHOJKI:
+    case HB_SCRIPT_KHUDAWADI:
+    case HB_SCRIPT_MAHAJANI:
+    //case HB_SCRIPT_MANICHAEAN:
+    case HB_SCRIPT_MODI:
+    case HB_SCRIPT_PAHAWH_HMONG:
+    //case HB_SCRIPT_PSALTER_PAHLAVI:
+    case HB_SCRIPT_SIDDHAM:
+    case HB_SCRIPT_TIRHUTA:
 
       /* If the designer designed the font for the 'DFLT' script,
-       * use the default shaper.  Otherwise, use the Indic shaper.
+       * use the default shaper.  Otherwise, use the specific shaper.
        * Note that for some simple scripts, there may not be *any*
        * GSUB/GPOS needed, so there may be no scripts found! */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
 	return &_hb_ot_complex_shaper_default;
       else
-	return &_hb_ot_complex_shaper_sea;
+	return &_hb_ot_complex_shaper_use;
   }
 }
 
 
 #endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.hh
+++ /dev/null
@@ -1,224 +0,0 @@
-
-#line 1 "hb-ot-shape-complex-sea-machine.rl"
-/*
- * Copyright © 2011,2012,2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-
-#include "hb-private.hh"
-
-
-#line 36 "hb-ot-shape-complex-sea-machine.hh"
-static const unsigned char _sea_syllable_machine_trans_keys[] = {
-	1u, 1u, 1u, 1u, 1u, 29u, 3u, 29u, 3u, 29u, 1u, 1u, 0
-};
-
-static const char _sea_syllable_machine_key_spans[] = {
-	1, 1, 29, 27, 27, 1
-};
-
-static const char _sea_syllable_machine_index_offsets[] = {
-	0, 2, 4, 34, 62, 90
-};
-
-static const char _sea_syllable_machine_indicies[] = {
-	1, 0, 3, 2, 1, 1, 3, 5, 
-	4, 4, 4, 4, 4, 3, 4, 1, 
-	4, 4, 4, 4, 3, 4, 4, 4, 
-	4, 3, 4, 4, 4, 3, 3, 3, 
-	3, 4, 1, 7, 6, 6, 6, 6, 
-	6, 1, 6, 6, 6, 6, 6, 6, 
-	1, 6, 6, 6, 6, 1, 6, 6, 
-	6, 1, 1, 1, 1, 6, 3, 9, 
-	8, 8, 8, 8, 8, 3, 8, 8, 
-	8, 8, 8, 8, 3, 8, 8, 8, 
-	8, 3, 8, 8, 8, 3, 3, 3, 
-	3, 8, 3, 10, 0
-};
-
-static const char _sea_syllable_machine_trans_targs[] = {
-	2, 3, 2, 4, 2, 5, 2, 0, 
-	2, 1, 2
-};
-
-static const char _sea_syllable_machine_trans_actions[] = {
-	1, 2, 3, 2, 6, 0, 7, 0, 
-	8, 0, 9
-};
-
-static const char _sea_syllable_machine_to_state_actions[] = {
-	0, 0, 4, 0, 0, 0
-};
-
-static const char _sea_syllable_machine_from_state_actions[] = {
-	0, 0, 5, 0, 0, 0
-};
-
-static const char _sea_syllable_machine_eof_trans[] = {
-	1, 3, 0, 7, 9, 11
-};
-
-static const int sea_syllable_machine_start = 2;
-static const int sea_syllable_machine_first_final = 2;
-static const int sea_syllable_machine_error = -1;
-
-static const int sea_syllable_machine_en_main = 2;
-
-
-#line 36 "hb-ot-shape-complex-sea-machine.rl"
-
-
-
-#line 67 "hb-ot-shape-complex-sea-machine.rl"
-
-
-#define found_syllable(syllable_type) \
-  HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
-
-static void
-find_syllables (hb_buffer_t *buffer)
-{
-  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  
-#line 117 "hb-ot-shape-complex-sea-machine.hh"
-	{
-	cs = sea_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 88 "hb-ot-shape-complex-sea-machine.rl"
-
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int last = 0;
-  unsigned int syllable_serial = 1;
-  
-#line 134 "hb-ot-shape-complex-sea-machine.hh"
-	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-_resume:
-	switch ( _sea_syllable_machine_from_state_actions[cs] ) {
-	case 5:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 148 "hb-ot-shape-complex-sea-machine.hh"
-	}
-
-	_keys = _sea_syllable_machine_trans_keys + (cs<<1);
-	_inds = _sea_syllable_machine_indicies + _sea_syllable_machine_index_offsets[cs];
-
-	_slen = _sea_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].sea_category()) &&
-		( info[p].sea_category()) <= _keys[1] ?
-		( info[p].sea_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _sea_syllable_machine_trans_targs[_trans];
-
-	if ( _sea_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _sea_syllable_machine_trans_actions[_trans] ) {
-	case 2:
-#line 1 "NONE"
-	{te = p+1;}
-	break;
-	case 6:
-#line 63 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p+1;{ found_syllable (non_sea_cluster); }}
-	break;
-	case 7:
-#line 61 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p;p--;{ found_syllable (consonant_syllable); }}
-	break;
-	case 8:
-#line 62 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p;p--;{ found_syllable (broken_cluster); }}
-	break;
-	case 9:
-#line 63 "hb-ot-shape-complex-sea-machine.rl"
-	{te = p;p--;{ found_syllable (non_sea_cluster); }}
-	break;
-	case 1:
-#line 61 "hb-ot-shape-complex-sea-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
-	break;
-	case 3:
-#line 62 "hb-ot-shape-complex-sea-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
-	break;
-#line 194 "hb-ot-shape-complex-sea-machine.hh"
-	}
-
-_again:
-	switch ( _sea_syllable_machine_to_state_actions[cs] ) {
-	case 4:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 203 "hb-ot-shape-complex-sea-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _sea_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _sea_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 97 "hb-ot-shape-complex-sea-machine.rl"
-
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea-machine.rl
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright © 2011,2012,2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
-
-#include "hb-private.hh"
-
-%%{
-  machine sea_syllable_machine;
-  alphtype unsigned char;
-  write data;
-}%%
-
-%%{
-
-# Same order as enum sea_category_t.  Not sure how to avoid duplication.
-C    = 1;
-GB   = 12; # Generic Base
-H    = 4;  # Halant
-IV   = 2;  # Independent Vowel
-MR   = 22; # Medial Ra
-CM   = 17; # Consonant Medial
-VAbv = 26;
-VBlw = 27;
-VPre = 28;
-VPst = 29;
-T    = 3;  # Tone Marks
-A    = 10; # Anusvara
-
-syllable_tail = (VPre|VAbv|VBlw|VPst|H.C|CM|MR|T|A)*;
-
-consonant_syllable =	(C|IV|GB) syllable_tail;
-broken_cluster =	syllable_tail;
-other =			any;
-
-main := |*
-	consonant_syllable	=> { found_syllable (consonant_syllable); };
-	broken_cluster		=> { found_syllable (broken_cluster); };
-	other			=> { found_syllable (non_sea_cluster); };
-*|;
-
-
-}%%
-
-#define found_syllable(syllable_type) \
-  HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
-
-static void
-find_syllables (hb_buffer_t *buffer)
-{
-  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  %%{
-    write init;
-    getkey info[p].sea_category();
-  }%%
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int last = 0;
-  unsigned int syllable_serial = 1;
-  %%{
-    write exec;
-  }%%
-}
-
-#undef found_syllable
-
-#endif /* HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-sea.cc
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright © 2011,2012,2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ot-shape-complex-indic-private.hh"
-
-/* buffer var allocations */
-#define sea_category() complex_var_u8_0() /* indic_category_t */
-#define sea_position() complex_var_u8_1() /* indic_position_t */
-
-
-/*
- * South-East Asian shaper.
- * Loosely based on the Myanmar spec / shaper.
- * There is no OpenType spec for this.
- */
-
-static const hb_tag_t
-basic_features[] =
-{
-  /*
-   * Basic features.
-   * These features are applied in order, one at a time, after initial_reordering.
-   */
-  HB_TAG('p','r','e','f'),
-  HB_TAG('a','b','v','f'),
-  HB_TAG('b','l','w','f'),
-  HB_TAG('p','s','t','f'),
-};
-static const hb_tag_t
-other_features[] =
-{
-  /*
-   * Other features.
-   * These features are applied all at once, after final_reordering.
-   */
-  HB_TAG('p','r','e','s'),
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('p','s','t','s'),
-  /* Positioning features, though we don't care about the types. */
-  HB_TAG('d','i','s','t'),
-};
-
-static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
-static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer);
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
-		  hb_buffer_t *buffer);
-
-static void
-collect_features_sea (hb_ot_shape_planner_t *plan)
-{
-  hb_ot_map_builder_t *map = &plan->map;
-
-  /* Do this before any lookups have been applied. */
-  map->add_gsub_pause (setup_syllables);
-
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
-  /* The Indic specs do not require ccmp, but we apply it here since if
-   * there is a use of it, it's typically at the beginning. */
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
-
-  map->add_gsub_pause (initial_reordering);
-  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
-  {
-    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
-    map->add_gsub_pause (NULL);
-  }
-  map->add_gsub_pause (final_reordering);
-  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
-    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
-}
-
-static void
-override_features_sea (hb_ot_shape_planner_t *plan)
-{
-  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
-}
-
-
-enum syllable_type_t {
-  consonant_syllable,
-  broken_cluster,
-  non_sea_cluster,
-};
-
-#include "hb-ot-shape-complex-sea-machine.hh"
-
-
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum sea_category_t {
-//  OT_C    = 1,
-  OT_GB   = 12, /* Generic Base XXX DOTTED CIRCLE only for now */
-//  OT_H    = 4,  /* Halant */
-  OT_IV   = 2,  /* Independent Vowel */
-  OT_MR   = 22, /* Medial Ra */
-//  OT_CM   = 17, /* Consonant Medial */
-  OT_VAbv = 26,
-  OT_VBlw = 27,
-  OT_VPre = 28,
-  OT_VPst = 29,
-  OT_T    = 3,  /* Tone Marks */
-//  OT_A    = 10, /* Anusvara */
-};
-
-static inline void
-set_sea_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
-  indic_position_t pos = (indic_position_t) (type >> 8);
-
-  /* Medial Ra */
-  if (u == 0x1A55u || u == 0xAA34u)
-    cat = (indic_category_t) OT_MR;
-
-  if (cat == OT_M)
-  {
-    switch ((int) pos)
-    {
-      case POS_PRE_C:	cat = (indic_category_t) OT_VPre; break;
-      case POS_ABOVE_C:	cat = (indic_category_t) OT_VAbv; break;
-      case POS_BELOW_C:	cat = (indic_category_t) OT_VBlw; break;
-      case POS_POST_C:	cat = (indic_category_t) OT_VPst; break;
-    }
-  }
-
-  info.sea_category() = (sea_category_t) cat;
-  info.sea_position() = pos;
-}
-
-
-static void
-setup_masks_sea (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_buffer_t              *buffer,
-		 hb_font_t                *font HB_UNUSED)
-{
-  HB_BUFFER_ALLOCATE_VAR (buffer, sea_category);
-  HB_BUFFER_ALLOCATE_VAR (buffer, sea_position);
-
-  /* We cannot setup masks here.  We save information about characters
-   * and setup masks later on in a pause-callback. */
-
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    set_sea_properties (info[i]);
-}
-
-static void
-setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
-{
-  find_syllables (buffer);
-}
-
-static int
-compare_sea_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
-{
-  int a = pa->sea_position();
-  int b = pb->sea_position();
-
-  return a < b ? -1 : a == b ? 0 : +1;
-}
-
-
-static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
-				       hb_face_t *face,
-				       hb_buffer_t *buffer,
-				       unsigned int start, unsigned int end)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int base = start;
-
-  /* Reorder! */
-  unsigned int i = start;
-  for (; i < base; i++)
-    info[i].sea_position() = POS_PRE_C;
-  if (i < end)
-  {
-    info[i].sea_position() = POS_BASE_C;
-    i++;
-  }
-  for (; i < end; i++)
-  {
-    if (info[i].sea_category() == OT_MR) /* Pre-base reordering */
-    {
-      info[i].sea_position() = POS_PRE_C;
-      continue;
-    }
-    if (info[i].sea_category() == OT_VPre) /* Left matra */
-    {
-      info[i].sea_position() = POS_PRE_M;
-      continue;
-    }
-
-    info[i].sea_position() = POS_AFTER_MAIN;
-  }
-
-  buffer->merge_clusters (start, end);
-  /* Sit tight, rock 'n roll! */
-  hb_bubble_sort (info + start, end - start, compare_sea_order);
-}
-
-static void
-initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
-				   hb_face_t *face,
-				   hb_buffer_t *buffer,
-				   unsigned int start, unsigned int end)
-{
-  /* We already inserted dotted-circles, so just call the consonant_syllable. */
-  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
-}
-
-static void
-initial_reordering_non_sea_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
-				    hb_face_t *face HB_UNUSED,
-				    hb_buffer_t *buffer HB_UNUSED,
-				    unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
-{
-  /* Nothing to do right now.  If we ever switch to using the output
-   * buffer in the reordering process, we'd need to next_glyph() here. */
-}
-
-
-static void
-initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			     hb_face_t *face,
-			     hb_buffer_t *buffer,
-			     unsigned int start, unsigned int end)
-{
-  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
-  switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
-  case non_sea_cluster:		initial_reordering_non_sea_cluster     (plan, face, buffer, start, end); return;
-  }
-}
-
-static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		       hb_font_t *font,
-		       hb_buffer_t *buffer)
-{
-  /* Note: This loop is extra overhead, but should not be measurable. */
-  bool has_broken_syllables = false;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == broken_cluster)
-    {
-      has_broken_syllables = true;
-      break;
-    }
-  if (likely (!has_broken_syllables))
-    return;
-
-
-  hb_codepoint_t dottedcircle_glyph;
-  if (!font->get_glyph (0x25CCu, 0, &dottedcircle_glyph))
-    return;
-
-  hb_glyph_info_t dottedcircle = {0};
-  dottedcircle.codepoint = 0x25CCu;
-  set_sea_properties (dottedcircle);
-  dottedcircle.codepoint = dottedcircle_glyph;
-
-  buffer->clear_output ();
-
-  buffer->idx = 0;
-  unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len)
-  {
-    unsigned int syllable = buffer->cur().syllable();
-    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
-    {
-      last_syllable = syllable;
-
-      hb_glyph_info_t info = dottedcircle;
-      info.cluster = buffer->cur().cluster;
-      info.mask = buffer->cur().mask;
-      info.syllable() = buffer->cur().syllable();
-
-      buffer->output_info (info);
-    }
-    else
-      buffer->next_glyph ();
-  }
-
-  buffer->swap_buffers ();
-}
-
-static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer)
-{
-  insert_dotted_circles (plan, font, buffer);
-
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  unsigned int last = 0;
-  unsigned int last_syllable = info[0].syllable();
-  for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, font->face, buffer, last, i);
-      last = i;
-      last_syllable = info[last].syllable();
-    }
-  initial_reordering_syllable (plan, font->face, buffer, last, count);
-}
-
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font HB_UNUSED,
-		  hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-
-  /* Zero syllables now... */
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
-
-  HB_BUFFER_DEALLOCATE_VAR (buffer, sea_category);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, sea_position);
-}
-
-
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea =
-{
-  "sea",
-  collect_features_sea,
-  override_features_sea,
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
-  NULL, /* decompose */
-  NULL, /* compose */
-  setup_masks_sea,
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
-  false, /* fallback_position */
-};
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
@@ -0,0 +1,548 @@
+
+#line 1 "hb-ot-shape-complex-use-machine.rl"
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+
+#include "hb-private.hh"
+
+
+#line 38 "hb-ot-shape-complex-use-machine.hh"
+static const unsigned char _use_syllable_machine_trans_keys[] = {
+	0u, 0u, 4u, 4u, 1u, 1u, 0u, 39u, 21u, 21u, 8u, 39u, 8u, 39u, 1u, 1u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 1u, 1u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 
+	8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 12u, 21u, 12u, 13u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 
+	8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 1u, 39u, 8u, 39u, 21u, 42u, 41u, 42u, 
+	42u, 42u, 0
+};
+
+static const char _use_syllable_machine_key_spans[] = {
+	0, 1, 1, 40, 1, 32, 32, 1, 
+	32, 32, 32, 19, 19, 19, 32, 32, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 1, 32, 32, 19, 19, 
+	19, 32, 32, 32, 32, 32, 32, 32, 
+	32, 10, 2, 32, 32, 32, 32, 19, 
+	19, 19, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 39, 32, 22, 2, 
+	1
+};
+
+static const short _use_syllable_machine_index_offsets[] = {
+	0, 0, 2, 4, 45, 47, 80, 113, 
+	115, 148, 181, 214, 234, 254, 274, 307, 
+	340, 373, 406, 439, 472, 505, 538, 571, 
+	604, 637, 670, 703, 705, 738, 771, 791, 
+	811, 831, 864, 897, 930, 963, 996, 1029, 
+	1062, 1095, 1106, 1109, 1142, 1175, 1208, 1241, 
+	1261, 1281, 1301, 1334, 1367, 1400, 1433, 1466, 
+	1499, 1532, 1565, 1598, 1631, 1671, 1704, 1727, 
+	1730
+};
+
+static const char _use_syllable_machine_indicies[] = {
+	1, 0, 3, 2, 4, 5, 6, 
+	4, 1, 5, 8, 8, 7, 8, 8, 
+	3, 9, 8, 8, 8, 4, 4, 10, 
+	11, 8, 8, 12, 13, 14, 15, 16, 
+	17, 18, 12, 19, 20, 21, 22, 23, 
+	24, 8, 25, 26, 27, 8, 29, 28, 
+	31, 30, 30, 32, 33, 30, 30, 30, 
+	30, 30, 30, 30, 30, 34, 35, 36, 
+	37, 38, 39, 40, 41, 35, 42, 34, 
+	43, 44, 45, 46, 30, 47, 48, 49, 
+	30, 31, 30, 30, 32, 33, 30, 30, 
+	30, 30, 30, 30, 30, 30, 50, 35, 
+	36, 37, 38, 39, 40, 41, 35, 42, 
+	43, 43, 44, 45, 46, 30, 47, 48, 
+	49, 30, 32, 51, 31, 30, 30, 32, 
+	33, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 35, 36, 37, 38, 39, 40, 
+	41, 35, 42, 43, 43, 44, 45, 46, 
+	30, 47, 48, 49, 30, 31, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 35, 36, 37, 38, 39, 
+	30, 30, 30, 30, 30, 30, 44, 45, 
+	46, 30, 47, 48, 49, 30, 31, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 36, 37, 38, 
+	39, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 47, 48, 49, 30, 31, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 37, 
+	38, 39, 30, 31, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 38, 39, 30, 31, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 39, 30, 31, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 37, 38, 39, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	47, 48, 49, 30, 31, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 37, 38, 39, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 48, 49, 30, 31, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 37, 38, 39, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 49, 30, 31, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 36, 37, 38, 
+	39, 30, 30, 30, 30, 30, 30, 44, 
+	45, 46, 30, 47, 48, 49, 30, 31, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 36, 37, 
+	38, 39, 30, 30, 30, 30, 30, 30, 
+	30, 45, 46, 30, 47, 48, 49, 30, 
+	31, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 36, 
+	37, 38, 39, 30, 30, 30, 30, 30, 
+	30, 30, 30, 46, 30, 47, 48, 49, 
+	30, 31, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 35, 
+	36, 37, 38, 39, 30, 41, 35, 30, 
+	30, 30, 44, 45, 46, 30, 47, 48, 
+	49, 30, 31, 30, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	35, 36, 37, 38, 39, 30, 30, 35, 
+	30, 30, 30, 44, 45, 46, 30, 47, 
+	48, 49, 30, 31, 30, 30, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 30, 
+	30, 35, 36, 37, 38, 39, 40, 41, 
+	35, 30, 30, 30, 44, 45, 46, 30, 
+	47, 48, 49, 30, 31, 30, 30, 32, 
+	33, 30, 30, 30, 30, 30, 30, 30, 
+	30, 30, 35, 36, 37, 38, 39, 40, 
+	41, 35, 42, 30, 43, 44, 45, 46, 
+	30, 47, 48, 49, 30, 31, 30, 30, 
+	32, 33, 30, 30, 30, 30, 30, 30, 
+	30, 30, 30, 35, 36, 37, 38, 39, 
+	40, 41, 35, 42, 34, 43, 44, 45, 
+	46, 30, 47, 48, 49, 30, 53, 52, 
+	52, 54, 55, 52, 52, 52, 52, 52, 
+	52, 52, 52, 56, 52, 57, 58, 59, 
+	60, 61, 62, 57, 63, 56, 64, 52, 
+	52, 52, 52, 65, 66, 67, 52, 53, 
+	52, 52, 54, 55, 52, 52, 52, 52, 
+	52, 52, 52, 52, 68, 52, 57, 58, 
+	59, 60, 61, 62, 57, 63, 64, 64, 
+	52, 52, 52, 52, 65, 66, 67, 52, 
+	54, 51, 53, 52, 52, 54, 55, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 57, 58, 59, 60, 61, 62, 57, 
+	63, 64, 64, 52, 52, 52, 52, 65, 
+	66, 67, 52, 53, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 57, 58, 59, 60, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	65, 66, 67, 52, 53, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 58, 59, 60, 52, 
+	53, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 59, 60, 52, 53, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 60, 52, 
+	53, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	58, 59, 60, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 65, 66, 67, 
+	52, 53, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 58, 59, 60, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 66, 
+	67, 52, 53, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 58, 59, 60, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 67, 52, 53, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 57, 58, 59, 60, 52, 62, 
+	57, 52, 52, 52, 52, 52, 52, 52, 
+	65, 66, 67, 52, 53, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 57, 58, 59, 60, 52, 
+	52, 57, 52, 52, 52, 52, 52, 52, 
+	52, 65, 66, 67, 52, 53, 52, 52, 
+	52, 52, 52, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 57, 58, 59, 60, 
+	61, 62, 57, 52, 52, 52, 52, 52, 
+	52, 52, 65, 66, 67, 52, 53, 52, 
+	52, 54, 55, 52, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 57, 58, 59, 
+	60, 61, 62, 57, 63, 52, 64, 52, 
+	52, 52, 52, 65, 66, 67, 52, 53, 
+	52, 52, 54, 55, 52, 52, 52, 52, 
+	52, 52, 52, 52, 52, 52, 57, 58, 
+	59, 60, 61, 62, 57, 63, 56, 64, 
+	52, 52, 52, 52, 65, 66, 67, 52, 
+	70, 71, 69, 69, 69, 69, 69, 69, 
+	69, 72, 69, 70, 71, 69, 7, 73, 
+	73, 3, 9, 73, 73, 73, 73, 73, 
+	73, 73, 73, 74, 12, 13, 14, 15, 
+	16, 17, 18, 12, 19, 21, 21, 22, 
+	23, 24, 73, 25, 26, 27, 73, 7, 
+	73, 73, 3, 9, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 12, 13, 14, 
+	15, 16, 17, 18, 12, 19, 21, 21, 
+	22, 23, 24, 73, 25, 26, 27, 73, 
+	7, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 12, 13, 
+	14, 15, 16, 73, 73, 73, 73, 73, 
+	73, 22, 23, 24, 73, 25, 26, 27, 
+	73, 7, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	13, 14, 15, 16, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 25, 26, 
+	27, 73, 7, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 14, 15, 16, 73, 7, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 15, 
+	16, 73, 7, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 16, 73, 7, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 14, 15, 
+	16, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 25, 26, 27, 73, 7, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 14, 
+	15, 16, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 26, 27, 73, 
+	7, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	14, 15, 16, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 27, 
+	73, 7, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	13, 14, 15, 16, 73, 73, 73, 73, 
+	73, 73, 22, 23, 24, 73, 25, 26, 
+	27, 73, 7, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 13, 14, 15, 16, 73, 73, 73, 
+	73, 73, 73, 73, 23, 24, 73, 25, 
+	26, 27, 73, 7, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 13, 14, 15, 16, 73, 73, 
+	73, 73, 73, 73, 73, 73, 24, 73, 
+	25, 26, 27, 73, 7, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 12, 13, 14, 15, 16, 73, 
+	18, 12, 73, 73, 73, 22, 23, 24, 
+	73, 25, 26, 27, 73, 7, 73, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 12, 13, 14, 15, 16, 
+	73, 73, 12, 73, 73, 73, 22, 23, 
+	24, 73, 25, 26, 27, 73, 7, 73, 
+	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 12, 13, 14, 15, 
+	16, 17, 18, 12, 73, 73, 73, 22, 
+	23, 24, 73, 25, 26, 27, 73, 7, 
+	73, 73, 3, 9, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 12, 13, 14, 
+	15, 16, 17, 18, 12, 19, 73, 21, 
+	22, 23, 24, 73, 25, 26, 27, 73, 
+	5, 6, 73, 73, 5, 73, 73, 7, 
+	73, 73, 3, 9, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 12, 13, 14, 
+	15, 16, 17, 18, 12, 19, 20, 21, 
+	22, 23, 24, 73, 25, 26, 27, 73, 
+	7, 73, 73, 3, 9, 73, 73, 73, 
+	73, 73, 73, 73, 73, 73, 12, 13, 
+	14, 15, 16, 17, 18, 12, 19, 20, 
+	21, 22, 23, 24, 73, 25, 26, 27, 
+	73, 76, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 75, 75, 76, 77, 75, 
+	76, 77, 75, 77, 75, 0
+};
+
+static const char _use_syllable_machine_trans_targs[] = {
+	3, 41, 3, 43, 4, 5, 25, 3, 
+	0, 2, 60, 62, 45, 46, 47, 48, 
+	49, 56, 57, 58, 61, 59, 53, 54, 
+	55, 50, 51, 52, 3, 3, 3, 3, 
+	6, 7, 24, 9, 10, 11, 12, 13, 
+	20, 21, 22, 23, 17, 18, 19, 14, 
+	15, 16, 8, 3, 3, 3, 26, 27, 
+	40, 29, 30, 31, 32, 36, 37, 38, 
+	39, 33, 34, 35, 28, 3, 3, 1, 
+	42, 3, 44, 3, 63, 64
+};
+
+static const char _use_syllable_machine_trans_actions[] = {
+	1, 2, 3, 4, 0, 0, 0, 7, 
+	0, 0, 4, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 4, 4, 0, 0, 
+	0, 0, 0, 0, 8, 9, 10, 11, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 12, 13, 14, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 15, 16, 0, 
+	2, 17, 4, 18, 0, 0
+};
+
+static const char _use_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 5, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const char _use_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 6, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const short _use_syllable_machine_eof_trans[] = {
+	0, 1, 3, 0, 29, 31, 31, 52, 
+	31, 31, 31, 31, 31, 31, 31, 31, 
+	31, 31, 31, 31, 31, 31, 31, 31, 
+	31, 53, 53, 52, 53, 53, 53, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
+	53, 70, 70, 74, 74, 74, 74, 74, 
+	74, 74, 74, 74, 74, 74, 74, 74, 
+	74, 74, 74, 74, 74, 74, 76, 76, 
+	76
+};
+
+static const int use_syllable_machine_start = 3;
+static const int use_syllable_machine_first_final = 3;
+static const int use_syllable_machine_error = 0;
+
+static const int use_syllable_machine_en_main = 3;
+
+
+#line 38 "hb-ot-shape-complex-use-machine.rl"
+
+
+
+#line 145 "hb-ot-shape-complex-use-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 388 "hb-ot-shape-complex-use-machine.hh"
+	{
+	cs = use_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 166 "hb-ot-shape-complex-use-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  
+#line 405 "hb-ot-shape-complex-use-machine.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	switch ( _use_syllable_machine_from_state_actions[cs] ) {
+	case 6:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 421 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+	_keys = _use_syllable_machine_trans_keys + (cs<<1);
+	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
+
+	_slen = _use_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].use_category()) &&
+		( info[p].use_category()) <= _keys[1] ?
+		( info[p].use_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _use_syllable_machine_trans_targs[_trans];
+
+	if ( _use_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _use_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 9:
+#line 134 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (independent_cluster); }}
+	break;
+	case 11:
+#line 136 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (consonant_cluster); }}
+	break;
+	case 14:
+#line 137 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (vowel_cluster); }}
+	break;
+	case 16:
+#line 138 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (number_joiner_terminated_cluster); }}
+	break;
+	case 7:
+#line 141 "hb-ot-shape-complex-use-machine.rl"
+	{te = p+1;{ found_syllable (broken_cluster); }}
+	break;
+	case 8:
+#line 134 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (independent_cluster); }}
+	break;
+	case 12:
+#line 135 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (virama_terminated_cluster); }}
+	break;
+	case 10:
+#line 136 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (consonant_cluster); }}
+	break;
+	case 13:
+#line 137 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (vowel_cluster); }}
+	break;
+	case 15:
+#line 139 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (numeral_cluster); }}
+	break;
+	case 18:
+#line 140 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (symbol_cluster); }}
+	break;
+	case 17:
+#line 141 "hb-ot-shape-complex-use-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 1:
+#line 139 "hb-ot-shape-complex-use-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (numeral_cluster); }}
+	break;
+	case 3:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 0:
+	{{cs = 0; goto _again;}}
+	break;
+	case 8:
+	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	break;
+	}
+	}
+	break;
+	case 4:
+#line 1 "NONE"
+	{te = p+1;}
+#line 141 "hb-ot-shape-complex-use-machine.rl"
+	{act = 8;}
+	break;
+#line 513 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+_again:
+	switch ( _use_syllable_machine_to_state_actions[cs] ) {
+	case 5:
+#line 1 "NONE"
+	{ts = 0;}
+#line 1 "NONE"
+	{act = 0;}
+	break;
+#line 524 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _use_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	_out: {}
+	}
+
+#line 175 "hb-ot-shape-complex-use-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+
+#include "hb-private.hh"
+
+%%{
+  machine use_syllable_machine;
+  alphtype unsigned char;
+  write data;
+}%%
+
+%%{
+
+# Same order as enum use_category_t.  Not sure how to avoid duplication.
+
+O	= 0; # OTHER
+
+B	= 1; # BASE
+IV	= 2; # BASE_VOWEL
+IND	= 3; # BASE_IND
+N	= 4; # BASE_NUM
+GB	= 5; # BASE_OTHER
+CGJ	= 6; # CGJ
+#F	= 7; # CONS_FINAL
+FM	= 8; # CONS_FINAL_MOD
+#M	= 9; # CONS_MED
+#CM	= 10; # CONS_MOD
+SUB	= 11; # CONS_SUB
+H	= 12; # HALANT
+
+HN	= 13; # HALANT_NUM
+ZWNJ	= 14; # Zero width non-joiner
+ZWJ	= 15; # Zero width joiner
+WJ	= 16; # Word joiner
+Rsv	= 17; # Reserved characters
+R	= 18; # REPHA
+S	= 19; # SYM
+#SM	= 20; # SYM_MOD
+VS	= 21; # VARIATION_SELECTOR
+#V	= 36; # VOWEL
+#VM	= 40; # VOWEL_MOD
+
+FAbv	= 24; # CONS_FINAL_ABOVE
+FBlw	= 25; # CONS_FINAL_BELOW
+FPst	= 26; # CONS_FINAL_POST
+MAbv	= 27; # CONS_MED_ABOVE
+MBlw	= 28; # CONS_MED_BELOW
+MPst	= 29; # CONS_MED_POST
+MPre	= 30; # CONS_MED_PRE
+CMAbv	= 31; # CONS_MOD_ABOVE
+CMBlw	= 32; # CONS_MOD_BELOW
+VAbv	= 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST
+VBlw	= 34; # VOWEL_BELOW / VOWEL_BELOW_POST
+VPst	= 35; # VOWEL_POST	UIPC = Right
+VPre	= 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST
+VMAbv	= 37; # VOWEL_MOD_ABOVE
+VMBlw	= 38; # VOWEL_MOD_BELOW
+VMPst	= 39; # VOWEL_MOD_POST
+VMPre	= 23; # VOWEL_MOD_PRE
+SMAbv	= 41; # SYM_MOD_ABOVE
+SMBlw	= 42; # SYM_MOD_BELOW
+
+
+consonant_modifiers = CMAbv* CMBlw* ((H B | SUB) VS? CMAbv? CMBlw*)*;
+medial_consonants = MPre? MAbv? MBlw? MPst?;
+dependent_vowels = VPre* VAbv* VBlw* VPst*;
+vowel_modifiers = VMPre* VMAbv* VMBlw* VMPst*;
+final_consonants = FAbv* FBlw* FPst* FM?;
+
+virama_terminated_cluster =
+	R? (B | GB | IV) VS?
+	consonant_modifiers
+	H
+;
+consonant_cluster =
+	R? (B | GB) VS?
+	consonant_modifiers
+	medial_consonants
+	dependent_vowels
+	vowel_modifiers
+	final_consonants
+;
+vowel_cluster =
+	R? (IV) VS?
+	consonant_modifiers
+	medial_consonants
+	vowel_modifiers
+	final_consonants
+;
+
+broken_cluster =
+	R?
+	consonant_modifiers
+	medial_consonants
+	dependent_vowels
+	vowel_modifiers
+	final_consonants
+;
+
+number_joiner_terminated_cluster = N VS? (HN N VS?)* H;
+numeral_cluster = N VS? (HN N VS?)*;
+symbol_cluster = S VS? SMAbv* SMBlw*;
+independent_cluster = (IND | O | Rsv | WJ) VS?;
+
+main := |*
+	independent_cluster			=> { found_syllable (independent_cluster); };
+	virama_terminated_cluster		=> { found_syllable (virama_terminated_cluster); };
+	consonant_cluster			=> { found_syllable (consonant_cluster); };
+	vowel_cluster				=> { found_syllable (vowel_cluster); };
+	number_joiner_terminated_cluster	=> { found_syllable (number_joiner_terminated_cluster); };
+	numeral_cluster				=> { found_syllable (numeral_cluster); };
+	symbol_cluster				=> { found_syllable (symbol_cluster); };
+	broken_cluster				=> { found_syllable (broken_cluster); };
+*|;
+
+
+}%%
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  %%{
+    write init;
+    getkey info[p].use_category();
+  }%%
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  %%{
+    write exec;
+  }%%
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-private.hh
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+#define USE_TABLE_ELEMENT_TYPE uint8_t
+
+/* Cateories used in the Universal Shaping Engine spec:
+ * https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum use_category_t {
+  USE_O		= 0,	/* OTHER */
+
+  USE_B		= 1,	/* BASE */
+  USE_IV	= 2,	/* BASE_VOWEL */
+  USE_IND	= 3,	/* BASE_IND */
+  USE_N		= 4,	/* BASE_NUM */
+  USE_GB	= 5,	/* BASE_OTHER */
+  USE_CGJ	= 6,	/* CGJ */
+//  USE_F		= 7,	/* CONS_FINAL */
+  USE_FM	= 8,	/* CONS_FINAL_MOD */
+//  USE_M		= 9,	/* CONS_MED */
+//  USE_CM	= 10,	/* CONS_MOD */
+  USE_SUB	= 11,	/* CONS_SUB */
+  USE_H		= 12,	/* HALANT */
+
+  USE_HN	= 13,	/* HALANT_NUM */
+  USE_ZWNJ	= 14,	/* Zero width non-joiner */
+  USE_ZWJ	= 15,	/* Zero width joiner */
+  USE_WJ	= 16,	/* Word joiner */
+  USE_Rsv	= 17,	/* Reserved characters */
+  USE_R		= 18,	/* REPHA */
+  USE_S		= 19,	/* SYM */
+//  USE_SM	= 20,	/* SYM_MOD */
+  USE_VS	= 21,	/* VARIATION_SELECTOR */
+//  USE_V	= 36,	/* VOWEL */
+//  USE_VM	= 40,	/* VOWEL_MOD */
+
+  USE_FAbv	= 24,	/* CONS_FINAL_ABOVE */
+  USE_FBlw	= 25,	/* CONS_FINAL_BELOW */
+  USE_FPst	= 26,	/* CONS_FINAL_POST */
+  USE_MAbv	= 27,	/* CONS_MED_ABOVE */
+  USE_MBlw	= 28,	/* CONS_MED_BELOW */
+  USE_MPst	= 29,	/* CONS_MED_POST */
+  USE_MPre	= 30,	/* CONS_MED_PRE */
+  USE_CMAbv	= 31,	/* CONS_MOD_ABOVE */
+  USE_CMBlw	= 32,	/* CONS_MOD_BELOW */
+  USE_VAbv	= 33,	/* VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST */
+  USE_VBlw	= 34,	/* VOWEL_BELOW / VOWEL_BELOW_POST */
+  USE_VPst	= 35,	/* VOWEL_POST	UIPC = Right */
+  USE_VPre	= 22,	/* VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST */
+  USE_VMAbv	= 37,	/* VOWEL_MOD_ABOVE */
+  USE_VMBlw	= 38,	/* VOWEL_MOD_BELOW */
+  USE_VMPst	= 39,	/* VOWEL_MOD_POST */
+  USE_VMPre	= 23,	/* VOWEL_MOD_PRE */
+  USE_SMAbv	= 41,	/* SYM_MOD_ABOVE */
+  USE_SMBlw	= 42	/* SYM_MOD_BELOW */
+};
+
+HB_INTERNAL USE_TABLE_ELEMENT_TYPE
+hb_use_get_categories (hb_codepoint_t u);
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
@@ -0,0 +1,696 @@
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
+ *
+ * on files with these headers:
+ *
+ * # IndicSyllabicCategory-8.0.0.txt
+ * # Date: 2015-05-12, 10:00:00 GMT [RP, KW, LI]
+ * # IndicPositionalCategory-8.0.0.txt
+ * # Date: 2015-05-12, 10:00:00 GMT [RP, KW, LI]
+ * # Blocks-8.0.0.txt
+ * # Date: 2014-11-10, 23:04:00 GMT [KW]
+ * UnicodeData.txt does not have a header.
+ */
+
+#include "hb-ot-shape-complex-use-private.hh"
+
+#define B	USE_B	/* BASE */
+#define CGJ	USE_CGJ	/* CGJ */
+#define FM	USE_FM	/* CONS_FINAL_MOD */
+#define GB	USE_GB	/* BASE_OTHER */
+#define H	USE_H	/* HALANT */
+#define HN	USE_HN	/* HALANT_NUM */
+#define IND	USE_IND	/* BASE_IND */
+#define IV	USE_IV	/* BASE_VOWEL */
+#define N	USE_N	/* BASE_NUM */
+#define O	USE_O	/* OTHER */
+#define R	USE_R	/* REPHA */
+#define Rsv	USE_Rsv	/* Reserved */
+#define S	USE_S	/* SYM */
+#define SUB	USE_SUB	/* CONS_SUB */
+#define VS	USE_VS	/* VARIATION_SELECTOR */
+#define WJ	USE_WJ	/* Word_Joiner */
+#define ZWJ	USE_ZWJ	/* ZWJ */
+#define ZWNJ	USE_ZWNJ	/* ZWNJ */
+#define CMBlw	USE_CMBlw
+#define CMAbv	USE_CMAbv
+#define FBlw	USE_FBlw
+#define FPst	USE_FPst
+#define FAbv	USE_FAbv
+#define MPre	USE_MPre
+#define MBlw	USE_MBlw
+#define MPst	USE_MPst
+#define MAbv	USE_MAbv
+#define SMBlw	USE_SMBlw
+#define SMAbv	USE_SMAbv
+#define VPre	USE_VPre
+#define VBlw	USE_VBlw
+#define VPst	USE_VPst
+#define VAbv	USE_VAbv
+#define VMPre	USE_VMPre
+#define VMBlw	USE_VMBlw
+#define VMPst	USE_VMPst
+#define VMAbv	USE_VMAbv
+
+static const USE_TABLE_ELEMENT_TYPE use_table[] = {
+
+
+#define use_offset_0x0028u 0
+
+
+  /* Basic Latin */
+                                                                         O,     O,     O,     O,     O,    GB,     O,     O,
+  /* 0030 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x00a0u 24
+
+
+  /* Latin-1 Supplement */
+
+  /* 00A0 */    GB,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 00B0 */     O,     O,    FM,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 00C0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 00D0 */     O,     O,     O,     O,     O,     O,     O,    GB,
+
+#define use_offset_0x0900u 80
+
+
+  /* Devanagari */
+
+  /* 0900 */ VMAbv, VMAbv, VMAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 0910 */    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0920 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0930 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VPst, CMBlw,     B,  VPst,  VPre,
+  /* 0940 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,  VPst,  VPst,  VPst,  VPst,     H,  VPre,  VPst,
+  /* 0950 */     O, VMAbv, VMBlw,     O,     O,  VAbv,  VBlw,  VBlw,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0960 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0970 */     O,     O,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,
+
+  /* Bengali */
+
+  /* 0980 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,    IV,
+  /* 0990 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 09A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 09B0 */     B,     O,     B,     O,     O,     O,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPre,
+  /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,   IND,     O,
+  /* 09D0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     B,     B,     O,     B,
+  /* 09E0 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 09F0 */     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Gurmukhi */
+
+  /* 0A00 */     O, VMAbv, VMAbv, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,     O,     O,    IV,
+  /* 0A10 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0A30 */     B,     O,     B,     B,     O,     B,     B,     O,     B,     B,     O,     O, CMBlw,     O,  VPst,  VPre,
+  /* 0A40 */  VPst,  VBlw,  VBlw,     O,     O,     O,     O,  VAbv,  VAbv,     O,     O,  VAbv,  VAbv,     H,     O,     O,
+  /* 0A50 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     O,     B,     O,
+  /* 0A60 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0A70 */ VMAbv, CMAbv,    GB,    GB,     O,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Gujarati */
+
+  /* 0A80 */     O, VMAbv, VMAbv, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,
+  /* 0A90 */    IV,    IV,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0AA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0AB0 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPre,
+  /* 0AC0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,     O,  VAbv,  VAbv,  VAbv,     O,  VPst,  VPst,     H,     O,     O,
+  /* 0AD0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 0AE0 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0AF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Oriya */
+
+  /* 0B00 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,    IV,
+  /* 0B10 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0B30 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VAbv,
+  /* 0B40 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
+  /* 0B50 */     O,     O,     O,     O,     O,     O,  VAbv,  VAbv,     O,     O,     O,     O,     B,     B,     O,     B,
+  /* 0B60 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0B70 */     O,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Tamil */
+
+  /* 0B80 */     O,     O, VMAbv,   IND,     O,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,     O,    IV,    IV,
+  /* 0B90 */    IV,     O,    IV,    IV,    IV,     B,     O,     O,     O,     B,     B,     O,     B,     O,     B,     B,
+  /* 0BA0 */     O,     O,     O,     B,     B,     O,     O,     O,     B,     B,     B,     O,     O,     O,     B,     B,
+  /* 0BB0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,  VPst,  VPst,
+  /* 0BC0 */  VAbv,  VPst,  VPst,     O,     O,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     O,     O,
+  /* 0BD0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 0BE0 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0BF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Telugu */
+
+  /* 0C00 */ VMAbv, VMPst, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,    IV,
+  /* 0C10 */    IV,     O,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0C20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0C30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,  VAbv,  VAbv,
+  /* 0C40 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,  VAbv,  VAbv,  VAbv,     O,  VAbv,  VAbv,  VAbv,     H,     O,     O,
+  /* 0C50 */     O,     O,     O,     O,     O,  VAbv,  VBlw,     O,     B,     B,     B,     O,     O,     O,     O,     O,
+  /* 0C60 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0C70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Kannada */
+
+  /* 0C80 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,    IV,
+  /* 0C90 */    IV,     O,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0CA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 0CB0 */     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VAbv,
+  /* 0CC0 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,  VAbv,  VAbv,  VAbv,     O,  VAbv,  VAbv,  VAbv,     H,     O,     O,
+  /* 0CD0 */     O,     O,     O,     O,     O,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     B,     O,
+  /* 0CE0 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0CF0 */     O,     R,     R,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Malayalam */
+
+  /* 0D00 */     O, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,    IV,    IV,
+  /* 0D10 */    IV,     O,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0D20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0D30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     B,  VPst,  VPst,
+  /* 0D40 */  VPst,  VPst,  VPst,  VBlw,  VBlw,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     R,     O,
+  /* 0D50 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,    IV,
+  /* 0D60 */    IV,    IV,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0D70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,   IND,   IND,   IND,   IND,   IND,   IND,
+
+  /* Sinhala */
+
+  /* 0D80 */     O,     O, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 0D90 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,     O,     B,     B,     B,     B,     B,     B,
+  /* 0DA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0DB0 */     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     O,     O,
+  /* 0DC0 */     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     H,     O,     O,     O,     O,  VPst,
+  /* 0DD0 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,     O,  VBlw,     O,  VPst,  VPre,  VPre,  VPre,  VPre,  VPre,  VPre,  VPst,
+  /* 0DE0 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0DF0 */     O,     O,  VPst,  VPst,     O,     O,     O,     O,
+
+#define use_offset_0x1000u 1352
+
+
+  /* Myanmar */
+
+  /* 1000 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1010 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1020 */     B,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,  VPst,  VPst,  VAbv,  VAbv,  VBlw,
+  /* 1030 */  VBlw,  VPre,  VAbv,  VAbv,  VAbv,  VAbv, VMAbv, VMBlw, VMPst,     H,  VAbv,  MPst,  MPre,  MBlw,  MBlw,     B,
+  /* 1040 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,    GB,     O,
+  /* 1050 */     B,     B,    IV,    IV,    IV,    IV,  VPst,  VPst,  VBlw,  VBlw,     B,     B,     B,     B,  MBlw,  MBlw,
+  /* 1060 */  MBlw,     B,  VPst, VMPst, VMPst,     B,     B,  VPst,  VPst, VMPst, VMPst, VMPst, VMPst, VMPst,     B,     B,
+  /* 1070 */     B,  VAbv,  VAbv,  VAbv,  VAbv,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1080 */     B,     B,  MBlw,  VPst,  VPre,  VAbv,  VAbv, VMPst, VMPst, VMPst, VMPst, VMPst, VMPst, VMBlw,     B, VMPst,
+  /* 1090 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMPst, VMPst,  VPst,  VAbv,     O,     O,
+
+#define use_offset_0x1700u 1512
+
+
+  /* Tagalog */
+
+  /* 1700 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,
+  /* 1710 */     B,     B,  VAbv,  VBlw,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Hanunoo */
+
+  /* 1720 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1730 */     B,     B,  VAbv,  VBlw,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Buhid */
+
+  /* 1740 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1750 */     B,     B,  VAbv,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Tagbanwa */
+
+  /* 1760 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,
+  /* 1770 */     B,     O,  VAbv,  VBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Khmer */
+
+  /* 1780 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1790 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 17A0 */     B,     B,     B,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 17B0 */    IV,    IV,    IV,    IV,     O,     O,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VPre,  VPre,
+  /* 17C0 */  VPre,  VPre,  VPre,  VPre,  VPre,  VPre, VMAbv, VMPst,  VPst, VMAbv, VMAbv,    FM,  FAbv, CMAbv,    FM,    FM,
+  /* 17D0 */    FM,  VAbv,     H,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     B,  VAbv,     O,     O,
+  /* 17E0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x1900u 1752
+
+
+  /* Limbu */
+
+  /* 1900 */    GB,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1910 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,
+  /* 1920 */  VAbv,  VAbv,  VBlw,  VPst,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,   SUB,   SUB,   SUB,     O,     O,     O,     O,
+  /* 1930 */  FPst,  FPst, VMBlw,  FPst,  FPst,  FPst,  FPst,  FPst,  FPst,  FBlw,  VAbv,    FM,     O,     O,     O,     O,
+  /* 1940 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+
+  /* Tai Le */
+
+  /* 1950 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1960 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,
+  /* 1970 */     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* New Tai Lue */
+
+  /* 1980 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1990 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 19A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+  /* 19B0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 19C0 */     B,     B,     B,     B,     B,     B,     B,     B, VMPst, VMPst,     O,     O,     O,     O,     O,     O,
+  /* 19D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 19E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 19F0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Buginese */
+
+  /* 1A00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1A10 */     B,     B,     B,     B,     B,     B,     B,  VAbv,  VBlw,  VPre,  VPst,  VAbv,     O,     O,     O,     O,
+
+  /* Tai Tham */
+
+  /* 1A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1A30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,    IV,    IV,    IV,
+  /* 1A50 */    IV,    IV,    IV,     B,     B,  MPre,  MBlw,  FPst,  FAbv,  FAbv,  FAbv,  FBlw,  FBlw,  FBlw,  FBlw,     O,
+  /* 1A60 */     H,  VPst,  VAbv,  VPst,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VAbv,  VBlw,  VPst,  VPre,  VPre,
+  /* 1A70 */  VPre,  VPre,  VPre,  VAbv,  VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,    FM,    FM,    FM,     O,     O,    FM,
+  /* 1A80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 1A90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x1b00u 2168
+
+
+  /* Balinese */
+
+  /* 1B00 */ VMAbv, VMAbv, VMAbv,  FAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 1B10 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1B30 */     B,     B,     B,     B, CMAbv,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPre,  VPre,
+  /* 1B40 */  VPre,  VPre,  VAbv,  VAbv,     H,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+  /* 1B50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 1B60 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
+  /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Sundanese */
+
+  /* 1B80 */ VMAbv,  FAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,
+  /* 1B90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1BA0 */     B,   SUB,   SUB,   SUB,  VAbv,  VBlw,  VPre,  VPst,  VAbv,  VAbv,  VPst,     H,   SUB,   SUB,     B,     B,
+  /* 1BB0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+
+  /* Batak */
+
+  /* 1BC0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1BD0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1BE0 */     B,     B,     B,     B,    IV,    IV, CMAbv,  VPst,  VAbv,  VAbv,  VPst,  VPst,  VPst,  VAbv,  VPst,  VAbv,
+  /* 1BF0 */  FAbv,  FAbv,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Lepcha */
+
+  /* 1C00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1C10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 1C20 */     B,     B,     B,     B,   SUB,   SUB,  VPst,  VPre,  VPre,  VPre,  VPst,  VPst,  VBlw,  FAbv,  FAbv,  FAbv,
+  /* 1C30 */  FAbv,  FAbv,  FAbv,  FAbv, VMPre, VMPre,    FM, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 1C40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,     B,     B,
+
+#define use_offset_0x1cd0u 2504
+
+
+  /* Vedic Extensions */
+
+  /* 1CD0 */ VMAbv, VMAbv, VMAbv,     O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw,
+  /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw,     O,     O,     O,     O, VMBlw,     O,     O,
+  /* 1CF0 */     O,     O, VMPst, VMPst, VMAbv,     O,     O,     O, VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x2008u 2552
+
+
+  /* General Punctuation */
+                                                                         O,     O,     O,     O,  ZWNJ,   ZWJ,     O,     O,
+  /* 2010 */    GB,    GB,    GB,    GB,    GB,     O,     O,     O,
+
+#define use_offset_0x2060u 2568
+
+  /* 2060 */    WJ,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Superscripts and Subscripts */
+
+  /* 2070 */     O,     O,     O,     O,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 2080 */     O,     O,    FM,    FM,    FM,     O,     O,     O,
+
+#define use_offset_0xa800u 2608
+
+
+  /* Syloti Nagri */
+
+  /* A800 */    IV,    IV,     O,    IV,    IV,    IV,  VAbv,     B,     B,     B,     B, VMAbv,     B,     B,     B,     B,
+  /* A810 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A820 */     B,     B,     B,  VPst,  VPst,  VBlw,  VAbv,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A830 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Phags-pa */
+
+  /* A840 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A850 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A860 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A870 */     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Saurashtra */
+
+  /* A880 */ VMPst, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* A890 */    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A8A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A8B0 */     B,     B,     B,     B,  FPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,
+  /* A8C0 */  VPst,  VPst,  VPst,  VPst,     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A8D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Devanagari Extended */
+
+  /* A8E0 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,
+  /* A8F0 */ VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Kayah Li */
+
+  /* A900 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A910 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A920 */     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VAbv, VMBlw, VMBlw, VMBlw,     O,     O,
+
+  /* Rejang */
+
+  /* A930 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A940 */     B,     B,     B,     B,     B,     B,     B,  VBlw,  VBlw,  VBlw,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  FAbv,
+  /* A950 */  FAbv,  FAbv,  FPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A960 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A970 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Javanese */
+
+  /* A980 */ VMAbv, VMAbv,  FAbv, VMPst,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,    IV,    IV,    IV,     B,
+  /* A990 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A9A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A9B0 */     B,     B,     B, CMAbv,  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VPre,  VAbv,   SUB,  MPst,  MPst,
+  /* A9C0 */     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A9D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Myanmar Extended-B */
+
+  /* A9E0 */     B,     B,     B,     B,     B,  VAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* A9F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,
+
+  /* Cham */
+
+  /* AA00 */    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VAbv,  VPre,
+  /* AA30 */  VPre,  VAbv,  VBlw,  MPst,  MPre,  MBlw,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* AA40 */     B,     B,     B,  FAbv,     B,     B,     B,     B,     B,     B,     B,     B,  FAbv,  FPst,     O,     O,
+  /* AA50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Myanmar Extended-A */
+
+  /* AA60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA70 */     O,     B,     B,     B,     O,     O,     O,     O,     O,     O,     B, VMPst, VMAbv, VMPst,     B,     B,
+
+  /* Tai Viet */
+
+  /* AA80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AA90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AAA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* AAB0 */  VAbv,     B,  VAbv,  VAbv,  VBlw,     B,     B,  VAbv,  VAbv,     B,     B,     B,     B,     B,  VAbv, VMAbv,
+  /* AAC0 */     B, VMAbv,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* AAD0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Meetei Mayek Extensions */
+
+  /* AAE0 */    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPre,  VBlw,  VAbv,  VPre,  VPst,
+  /* AAF0 */     O,     O,     O,     O,     O, VMPst,     H,     O,
+
+#define use_offset_0xabc0u 3368
+
+
+  /* Meetei Mayek */
+
+  /* ABC0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,    IV,    IV,
+  /* ABD0 */     B,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* ABE0 */     B,     B,     B,  VPst,  VPst,  VAbv,  VPst,  VPst,  VBlw,  VPst,  VPst,     O, VMPst,  VBlw,     O,     O,
+  /* ABF0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0xfe00u 3432
+
+
+  /* Variation Selectors */
+
+  /* FE00 */    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,
+
+#define use_offset_0x10a00u 3448
+
+
+  /* Kharoshthi */
+
+  /* 10A00 */     B,  VBlw,  VBlw,  VBlw,     O,  VAbv,  VBlw,     O,     O,     O,     O,     O,  VBlw,  VBlw, VMBlw, VMAbv,
+  /* 10A10 */     B,     B,     B,     B,     O,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,
+  /* 10A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 10A30 */     B,     B,     B,     B,     O,     O,     O,     O, CMAbv, CMBlw, CMBlw,     O,     O,     O,     O,     H,
+  /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,
+
+#define use_offset_0x11000u 3520
+
+
+  /* Brahmi */
+
+  /* 11000 */ VMPst, VMAbv, VMPst,     R,     R,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 11010 */    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11020 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11030 */     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,
+  /* 11040 */  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11050 */     O,     O,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,
+  /* 11060 */     N,     N,     N,     N,     N,     N,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11070 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Kaithi */
+
+  /* 11080 */ VMAbv, VMAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,
+  /* 11090 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 110A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 110B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VPst,  VPst,     H, CMBlw,     O,     O,     O,     O,     O,
+
+#define use_offset_0x11100u 3712
+
+
+  /* Chakma */
+
+  /* 11100 */ VMAbv, VMAbv, VMAbv,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11110 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11120 */     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VAbv,  VAbv,
+  /* 11130 */  VAbv,  VBlw,  VBlw,     H,  VAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11140 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Mahajani */
+
+  /* 11150 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11160 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11170 */     B,     B,     B, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Sharada */
+
+  /* 11180 */ VMAbv, VMAbv, VMPst,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,
+  /* 11190 */    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 111A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 111B0 */     B,     B,     B,  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,
+  /* 111C0 */     H,     B,     R,     R,     O,     O,     O,     O,     O,     O, CMBlw,  VAbv,  VBlw,     O,     O,     O,
+  /* 111D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Sinhala Archaic Numbers */
+
+  /* 111E0 */     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 111F0 */     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Khojki */
+
+  /* 11200 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11210 */     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11220 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,  VPst,  VPst,  VBlw,
+  /* 11230 */  VAbv,  VAbv,  VAbv,  VAbv, VMAbv,     H, CMAbv, CMAbv,
+
+#define use_offset_0x11280u 4024
+
+
+  /* Multani */
+
+  /* 11280 */    IV,    IV,    IV,    IV,     B,     B,     B,     O,     B,     O,     B,     B,     B,     B,     O,     B,
+  /* 11290 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,
+  /* 112A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Khudawadi */
+
+  /* 112B0 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,
+  /* 112C0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 112D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv,
+  /* 112E0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv, CMBlw,  VBlw,     O,     O,     O,     O,     O,
+  /* 112F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+  /* Grantha */
+
+  /* 11300 */ VMAbv, VMAbv, VMPst, VMPst,     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     O,     O,    IV,
+  /* 11310 */    IV,     O,     O,    IV,    IV,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11320 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
+  /* 11330 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPst,
+  /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
+  /* 11350 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11360 */    IV,    IV,  VPst,  VPst,     O,     O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
+  /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
+
+#define use_offset_0x11480u 4272
+
+
+  /* Tirhuta */
+
+  /* 11480 */     O,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,
+  /* 11490 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 114A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 114B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VPre,  VAbv,  VPre,  VPre,  VPst,  VPre, VMAbv,
+  /* 114C0 */ VMAbv, VMPst,     H, CMBlw,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 114D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x11580u 4368
+
+
+  /* Siddham */
+
+  /* 11580 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,
+  /* 11590 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 115A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,
+  /* 115B0 */  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,  VPre,  VPre, VMAbv, VMAbv, VMPst,     H,
+  /* 115C0 */ CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 115D0 */     O,     O,     O,     O,     O,     O,     O,     O,    IV,    IV,    IV,    IV,  VBlw,  VBlw,     O,     O,
+  /* 115E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 115F0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Modi */
+
+  /* 11600 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,
+  /* 11610 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11620 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11630 */  VPst,  VPst,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPst,  VPst, VMAbv, VMPst,     H,
+  /* 11640 */  VAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11650 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 11660 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11670 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Takri */
+
+  /* 11680 */    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,    IV,     B,     B,     B,     B,     B,     B,
+  /* 11690 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 116A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv, VMPst,  VAbv,  VPre,  VPst,
+  /* 116B0 */  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,     H, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 116C0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+  /* 116D0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 116E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 116F0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Ahom */
+
+  /* 11700 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11710 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,  MBlw,  MPre,  MAbv,
+  /* 11720 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VBlw,  VAbv,  VAbv,  VAbv,     O,     O,     O,     O,
+  /* 11730 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+
+}; /* Table items: 4816; occupancy: 72% */
+
+USE_TABLE_ELEMENT_TYPE
+hb_use_get_categories (hb_codepoint_t u)
+{
+  switch (u >> 12)
+  {
+    case 0x0u:
+      if (hb_in_range (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
+      if (hb_in_range (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
+      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
+      if (unlikely (u == 0x034Fu)) return CGJ;
+      break;
+
+    case 0x1u:
+      if (hb_in_range (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
+      if (hb_in_range (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
+      if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
+      if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
+      if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
+      break;
+
+    case 0x2u:
+      if (hb_in_range (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
+      if (hb_in_range (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
+      if (unlikely (u == 0x25CCu)) return GB;
+      break;
+
+    case 0xAu:
+      if (hb_in_range (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
+      if (hb_in_range (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
+      break;
+
+    case 0xFu:
+      if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
+      break;
+
+    case 0x10u:
+      if (hb_in_range (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
+      break;
+
+    case 0x11u:
+      if (hb_in_range (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
+      if (hb_in_range (u, 0x11100u, 0x11237u)) return use_table[u - 0x11100u + use_offset_0x11100u];
+      if (hb_in_range (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
+      if (hb_in_range (u, 0x11480u, 0x114DFu)) return use_table[u - 0x11480u + use_offset_0x11480u];
+      if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
+      if (unlikely (u == 0x1107Fu)) return HN;
+      break;
+
+    default:
+      break;
+  }
+  return USE_O;
+}
+
+#undef B
+#undef CGJ
+#undef FM
+#undef GB
+#undef H
+#undef HN
+#undef IND
+#undef IV
+#undef N
+#undef O
+#undef R
+#undef Rsv
+#undef S
+#undef SUB
+#undef VS
+#undef WJ
+#undef ZWJ
+#undef ZWNJ
+#undef CMBlw
+#undef CMAbv
+#undef FBlw
+#undef FPst
+#undef FAbv
+#undef MPre
+#undef MBlw
+#undef MPst
+#undef MAbv
+#undef SMBlw
+#undef SMAbv
+#undef VPre
+#undef VBlw
+#undef VPst
+#undef VAbv
+#undef VMPre
+#undef VMBlw
+#undef VMPst
+#undef VMAbv
+
+/* == End of generated table == */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -0,0 +1,593 @@
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-complex-use-private.hh"
+#include "hb-ot-shape-complex-arabic-private.hh"
+
+/* buffer var allocations */
+#define use_category() complex_var_u8_0()
+
+
+/*
+ * Universal Shaping Engine.
+ * https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm
+ */
+
+static const hb_tag_t
+basic_features[] =
+{
+  /*
+   * Basic features.
+   * These features are applied all at once, before reordering.
+   */
+  HB_TAG('r','k','r','f'),
+  HB_TAG('a','b','v','f'),
+  HB_TAG('b','l','w','f'),
+  HB_TAG('h','a','l','f'),
+  HB_TAG('p','s','t','f'),
+  HB_TAG('v','a','t','u'),
+  HB_TAG('c','j','c','t'),
+};
+static const hb_tag_t
+arabic_features[] =
+{
+  HB_TAG('i','s','o','l'),
+  HB_TAG('i','n','i','t'),
+  HB_TAG('m','e','d','i'),
+  HB_TAG('f','i','n','a'),
+  /* The spec doesn't specify these but we apply anyway, since our Arabic shaper
+   * does.  These are only used in Syriac spec. */
+  HB_TAG('m','e','d','2'),
+  HB_TAG('f','i','n','2'),
+  HB_TAG('f','i','n','3'),
+};
+/* Same order as arabic_features.  Don't need Syriac stuff.*/
+enum joining_form_t {
+  ISOL,
+  INIT,
+  MEDI,
+  FINA,
+  _NONE
+};
+static const hb_tag_t
+other_features[] =
+{
+  /*
+   * Other features.
+   * These features are applied all at once, after reordering.
+   */
+  HB_TAG('a','b','v','s'),
+  HB_TAG('b','l','w','s'),
+  HB_TAG('h','a','l','n'),
+  HB_TAG('p','r','e','s'),
+  HB_TAG('p','s','t','s'),
+  /* Positioning features, though we don't care about the types. */
+  HB_TAG('d','i','s','t'),
+  HB_TAG('a','b','v','m'),
+  HB_TAG('b','l','w','m'),
+};
+
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
+static void
+clear_substitution_flags (const hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer);
+static void
+record_rphf (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer);
+static void
+record_pref (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer);
+static void
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer);
+
+static void
+collect_features_use (hb_ot_shape_planner_t *plan)
+{
+  hb_ot_map_builder_t *map = &plan->map;
+
+  /* Do this before any lookups have been applied. */
+  map->add_gsub_pause (setup_syllables);
+
+  /* "Default glyph pre-processing group" */
+  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
+  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
+  map->add_global_bool_feature (HB_TAG('n','u','k','t'));
+  map->add_global_bool_feature (HB_TAG('a','k','h','n'));
+
+  /* "Reordering group" */
+  map->add_gsub_pause (clear_substitution_flags);
+  map->add_feature (HB_TAG('r','p','h','f'), 1, F_MANUAL_ZWJ);
+  map->add_gsub_pause (record_rphf);
+  map->add_gsub_pause (clear_substitution_flags);
+  map->add_feature (HB_TAG('p','r','e','f'), 1, F_GLOBAL | F_MANUAL_ZWJ);
+  map->add_gsub_pause (record_pref);
+
+  /* "Orthographic unit shaping group" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
+    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+
+  map->add_gsub_pause (reorder);
+
+  /* "Topographical features" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
+    map->add_feature (arabic_features[i], 1, F_NONE);
+  map->add_gsub_pause (NULL);
+
+  /* "Standard typographic presentation" and "Positional feature application" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
+    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+}
+
+struct use_shape_plan_t
+{
+  ASSERT_POD ();
+
+  hb_mask_t rphf_mask;
+
+  arabic_shape_plan_t *arabic_plan;
+};
+
+static bool
+has_arabic_joining (hb_script_t script)
+{
+  /* List of scripts that have data in arabic-table. */
+  switch ((int) script)
+  {
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_ARABIC:
+
+    /* Unicode-3.0 additions */
+    case HB_SCRIPT_MONGOLIAN:
+    case HB_SCRIPT_SYRIAC:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_NKO:
+    case HB_SCRIPT_PHAGS_PA:
+
+    /* Unicode-6.0 additions */
+    case HB_SCRIPT_MANDAIC:
+
+    /* Unicode-7.0 additions */
+    case HB_SCRIPT_MANICHAEAN:
+    case HB_SCRIPT_PSALTER_PAHLAVI:
+
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+static void *
+data_create_use (const hb_ot_shape_plan_t *plan)
+{
+  use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t));
+  if (unlikely (!use_plan))
+    return NULL;
+
+  use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f'));
+
+  if (has_arabic_joining (plan->props.script))
+  {
+    use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan);
+    if (unlikely (!use_plan->arabic_plan))
+    {
+      free (use_plan);
+      return NULL;
+    }
+  }
+
+  return use_plan;
+}
+
+static void
+data_destroy_use (void *data)
+{
+  use_shape_plan_t *use_plan = (use_shape_plan_t *) data;
+
+  if (use_plan->arabic_plan)
+    data_destroy_arabic (use_plan->arabic_plan);
+
+  free (data);
+}
+
+enum syllable_type_t {
+  independent_cluster,
+  virama_terminated_cluster,
+  consonant_cluster,
+  vowel_cluster,
+  number_joiner_terminated_cluster,
+  numeral_cluster,
+  symbol_cluster,
+  broken_cluster,
+};
+
+#include "hb-ot-shape-complex-use-machine.hh"
+
+
+static inline void
+set_use_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  info.use_category() = hb_use_get_categories (u);
+}
+
+
+static void
+setup_masks_use (const hb_ot_shape_plan_t *plan,
+		 hb_buffer_t              *buffer,
+		 hb_font_t                *font HB_UNUSED)
+{
+  const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
+
+  /* Do this before allocating use_category(). */
+  if (use_plan->arabic_plan)
+  {
+    setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script);
+  }
+
+  HB_BUFFER_ALLOCATE_VAR (buffer, use_category);
+
+  /* We cannot setup masks here.  We save information about characters
+   * and setup masks later on in a pause-callback. */
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].use_category() = hb_use_get_categories (info[i].codepoint);
+}
+
+static void
+setup_rphf_mask (const hb_ot_shape_plan_t *plan,
+		 hb_buffer_t *buffer)
+{
+  const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
+
+  hb_mask_t mask = use_plan->rphf_mask;
+  if (!mask) return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+  {
+    unsigned int limit = info[start].use_category() == USE_R ? 1 : MIN (3u, end - start);
+    for (unsigned int i = start; i < start + limit; i++)
+      info[i].mask |= mask;
+  }
+}
+
+static void
+setup_topographical_masks (const hb_ot_shape_plan_t *plan,
+			   hb_buffer_t *buffer)
+{
+
+  ASSERT_STATIC (INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4);
+  hb_mask_t masks[4], all_masks = 0;
+  for (unsigned int i = 0; i < 4; i++)
+  {
+    masks[i] = plan->map.get_1_mask (arabic_features[i]);
+    if (masks[i] == plan->map.get_global_mask ())
+      masks[i] = 0;
+    all_masks |= masks[i];
+  }
+  if (!all_masks)
+    return;
+  hb_mask_t other_masks = ~all_masks;
+
+  unsigned int last_start = 0;
+  joining_form_t last_form = _NONE;
+  hb_glyph_info_t *info = buffer->info;
+  foreach_syllable (buffer, start, end)
+  {
+    syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F);
+    switch (syllable_type)
+    {
+      case independent_cluster:
+      case symbol_cluster:
+	/* These don't join.  Nothing to do. */
+	last_form = _NONE;
+	break;
+
+      case virama_terminated_cluster:
+      case consonant_cluster:
+      case vowel_cluster:
+      case number_joiner_terminated_cluster:
+      case numeral_cluster:
+      case broken_cluster:
+
+	bool join = last_form == FINA || last_form == ISOL;
+
+	if (join)
+	{
+	  /* Fixup previous syllable's form. */
+	  last_form = last_form == FINA ? MEDI : INIT;
+	  for (unsigned int i = last_start; i < start; i++)
+	    info[i].mask = (info[i].mask & other_masks) | masks[last_form];
+	}
+
+	/* Form for this syllable. */
+	last_form = join ? FINA : ISOL;
+	for (unsigned int i = start; i < end; i++)
+	  info[i].mask = (info[i].mask & other_masks) | masks[last_form];
+
+	break;
+    }
+
+    last_start = start;
+  }
+}
+
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  find_syllables (buffer);
+  setup_rphf_mask (plan, buffer);
+  setup_topographical_masks (plan, buffer);
+}
+
+static void
+clear_substitution_flags (const hb_ot_shape_plan_t *plan,
+			  hb_font_t *font HB_UNUSED,
+			  hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    _hb_glyph_info_clear_substituted_and_ligated_and_multiplied (&info[i]);
+}
+
+static void
+record_rphf (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer)
+{
+  const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
+
+  hb_mask_t mask = use_plan->rphf_mask;
+  if (!mask) return;
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+  {
+    /* Mark a substituted repha as USE_R. */
+    for (unsigned int i = start; i < end && (info[i].mask & mask); i++)
+      if (_hb_glyph_info_substituted (&info[i]))
+      {
+	info[i].use_category() = USE_R;
+	break;
+      }
+  }
+}
+
+static void
+record_pref (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+  {
+    /* Mark a substituted pref as VPre, as they behave the same way. */
+    for (unsigned int i = start; i < end; i++)
+      if (_hb_glyph_info_substituted (&info[i]))
+      {
+	info[i].use_category() = USE_VPre;
+	break;
+      }
+  }
+}
+
+static void
+reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end)
+{
+  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  /* Only a few syllable types need reordering. */
+  if (unlikely (!(FLAG_SAFE (syllable_type) &
+		  (FLAG (virama_terminated_cluster) |
+		   FLAG (consonant_cluster) |
+		   FLAG (vowel_cluster) |
+		   FLAG (broken_cluster) |
+		   0))))
+    return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+#define HALANT_FLAGS FLAG(USE_H)
+#define BASE_FLAGS (FLAG (USE_B) | FLAG (USE_GB) | FLAG (USE_IV))
+
+  /* Move things forward. */
+  if (info[start].use_category() == USE_R && end - start > 1)
+  {
+    /* Got a repha.  Reorder it to after first base, before first halant. */
+    for (unsigned int i = start + 1; i < end; i++)
+      if (FLAG_UNSAFE (info[i].use_category()) & (HALANT_FLAGS | BASE_FLAGS))
+      {
+	/* If we hit a halant, move before it; otherwise it's a base: move to it's
+	 * place, and shift things in between backward. */
+
+	if (info[i].use_category() == USE_H)
+	  i--;
+
+	hb_glyph_info_t t = info[start];
+	memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0]));
+	info[i] = t;
+	buffer->merge_clusters (start, i + 1);
+
+	break;
+      }
+  }
+
+  /* Move things back. */
+  unsigned int j = end;
+  for (unsigned int i = start; i < end; i++)
+  {
+    uint32_t flag = FLAG_UNSAFE (info[i].use_category());
+    if (flag & (HALANT_FLAGS | BASE_FLAGS))
+    {
+      /* If we hit a halant, move before it; otherwise it's a base: move to it's
+       * place, and shift things in between backward. */
+      if (info[i].use_category() == USE_H)
+	j = i + 1;
+      else
+	j = i;
+    }
+    else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) &&
+	     /* Only move the first component of a MultipleSubst. */
+	     0 == _hb_glyph_info_get_lig_comp (&info[i]) &&
+	     j < i)
+    {
+      hb_glyph_info_t t = info[i];
+      memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0]));
+      info[j] = t;
+      buffer->merge_clusters (j, i + 1);
+    }
+  }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  /* Note: This loop is extra overhead, but should not be measurable. */
+  bool has_broken_syllables = false;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    {
+      has_broken_syllables = true;
+      break;
+    }
+  if (likely (!has_broken_syllables))
+    return;
+
+
+  hb_codepoint_t dottedcircle_glyph;
+  if (!font->get_glyph (0x25CCu, 0, &dottedcircle_glyph))
+    return;
+
+  hb_glyph_info_t dottedcircle = {0};
+  if (!font->get_glyph (0x25CCu, 0, &dottedcircle.codepoint))
+    return;
+  dottedcircle.use_category() = hb_use_get_categories (0x25CC);
+
+  buffer->clear_output ();
+
+  buffer->idx = 0;
+
+  unsigned int last_syllable = 0;
+  while (buffer->idx < buffer->len)
+  {
+    unsigned int syllable = buffer->cur().syllable();
+    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    {
+      last_syllable = syllable;
+
+      hb_glyph_info_t info = dottedcircle;
+      info.cluster = buffer->cur().cluster;
+      info.mask = buffer->cur().mask;
+      info.syllable() = buffer->cur().syllable();
+      /* TODO Set glyph_props? */
+
+      /* Insert dottedcircle after possible Repha. */
+      while (buffer->idx < buffer->len &&
+	     last_syllable == buffer->cur().syllable() &&
+	     buffer->cur().use_category() == USE_R)
+        buffer->next_glyph ();
+
+      buffer->output_info (info);
+    }
+    else
+      buffer->next_glyph ();
+  }
+
+  buffer->swap_buffers ();
+}
+
+static void
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer)
+{
+  insert_dotted_circles (plan, font, buffer);
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_syllable (buffer, start, end)
+    reorder_syllable (buffer, start, end);
+
+  /* Zero syllables now... */
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
+}
+
+static bool
+compose_use (const hb_ot_shape_normalize_context_t *c,
+	     hb_codepoint_t  a,
+	     hb_codepoint_t  b,
+	     hb_codepoint_t *ab)
+{
+  /* Avoid recomposing split matras. */
+  if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
+    return false;
+
+  return c->unicode->compose (a, b, ab);
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
+{
+  "use",
+  collect_features_use,
+  NULL, /* override_features */
+  data_create_use,
+  data_destroy_use,
+  NULL, /* preprocess_text */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+  NULL, /* decompose */
+  compose_use,
+  setup_masks_use,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  false, /* fallback_position */
+};
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -54,20 +54,16 @@ static hb_tag_t horizontal_features[] = 
   HB_TAG('c','a','l','t'),
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
   HB_TAG('l','i','g','a'),
   HB_TAG('r','c','l','t'),
 };
 
-static hb_tag_t vertical_features[] = {
-  HB_TAG('v','e','r','t'),
-};
-
 
 
 static void
 hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
 			      const hb_segment_properties_t  *props,
 			      const hb_feature_t             *user_features,
 			      unsigned int                    num_user_features)
 {
@@ -100,20 +96,23 @@ hb_ot_shape_collect_features (hb_ot_shap
     map->add_global_bool_feature (common_features[i]);
 
   if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
     for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
       map->add_feature (horizontal_features[i], 1, F_GLOBAL |
 			(horizontal_features[i] == HB_TAG('k','e','r','n') ?
 			 F_HAS_FALLBACK : F_NONE));
   else
-    for (unsigned int i = 0; i < ARRAY_LENGTH (vertical_features); i++)
-      map->add_feature (vertical_features[i], 1, F_GLOBAL |
-			(vertical_features[i] == HB_TAG('v','k','r','n') ?
-			 F_HAS_FALLBACK : F_NONE));
+  {
+    /* We really want to find a 'vert' feature if there's any in the font, no
+     * matter which script/langsys it is listed (or not) under.
+     * See various bugs referenced from:
+     * https://github.com/behdad/harfbuzz/issues/63 */
+    map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH);
+  }
 
   if (planner->shaper->override_features)
     planner->shaper->override_features (planner);
 
   for (unsigned int i = 0; i < num_user_features; i++) {
     const hb_feature_t *feature = &user_features[i];
     map->add_feature (feature->tag, feature->value,
 		      (feature->start == 0 && feature->end == (unsigned int) -1) ?
@@ -259,36 +258,67 @@ hb_insert_dotted_circle (hb_buffer_t *bu
     buffer->next_glyph ();
 
   buffer->swap_buffers ();
 }
 
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
+  if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    return;
+
+  /* Loop duplicated in hb_ensure_native_direction(). */
+  unsigned int base = 0;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
-    if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))
-      buffer->merge_clusters (i - 1, i + 1);
+  {
+    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
+    {
+      buffer->merge_clusters (base, i);
+      base = i;
+    }
+  }
+  buffer->merge_clusters (base, count);
 }
 
 static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
 
   /* TODO vertical:
    * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
    * Ogham fonts are supposed to be implemented BTT or not.  Need to research that
    * first. */
   if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) ||
       (HB_DIRECTION_IS_VERTICAL   (direction) && direction != HB_DIRECTION_TTB))
   {
-    hb_buffer_reverse_clusters (buffer);
+    /* Same loop as hb_form_clusters().
+     * Since form_clusters() merged clusters already, we don't merge. */
+    unsigned int base = 0;
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    for (unsigned int i = 1; i < count; i++)
+    {
+      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
+      {
+	buffer->reverse_range (base, i);
+	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+	  buffer->merge_clusters (base, i);
+	base = i;
+      }
+    }
+    buffer->reverse_range (base, count);
+    if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+      buffer->merge_clusters (base, count);
+
+    buffer->reverse ();
+
     buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
   }
 }
 
 
 /* Substitute */
 
 static inline void
@@ -300,17 +330,17 @@ hb_ot_mirror_chars (hb_ot_shape_context_
   hb_buffer_t *buffer = c->buffer;
   hb_unicode_funcs_t *unicode = buffer->unicode;
   hb_mask_t rtlm_mask = c->plan->rtlm_mask;
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++) {
     hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint);
-    if (likely (codepoint == info[i].codepoint))
+    if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint)))
       info[i].mask |= rtlm_mask;
     else
       info[i].codepoint = codepoint;
   }
 }
 
 static inline void
 hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
@@ -375,16 +405,111 @@ hb_ot_shape_setup_masks (hb_ot_shape_con
     if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
       unsigned int shift;
       hb_mask_t mask = map->get_mask (feature->tag, &shift);
       buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
     }
   }
 }
 
+static void
+hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c)
+{
+  hb_buffer_t *buffer = c->buffer;
+
+  if (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
+    return;
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  unsigned int i = 0;
+  for (i = 0; i < count; i++)
+    if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
+      pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
+}
+
+static void
+hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
+{
+  hb_buffer_t *buffer = c->buffer;
+
+  if (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
+    return;
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  unsigned int i = 0;
+  for (i = 0; i < count; i++)
+  {
+    if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
+      break;
+  }
+
+  /* No default-ignorables found; return. */
+  if (i == count)
+    return;
+
+  hb_codepoint_t space;
+  if (c->font->get_glyph (' ', 0, &space))
+  {
+    /* Replace default-ignorables with a zero-advance space glyph. */
+    for (/*continue*/; i < count; i++)
+    {
+      if (_hb_glyph_info_is_default_ignorable (&info[i]))
+	info[i].codepoint = space;
+    }
+  }
+  else
+  {
+    /* Merge clusters and delete default-ignorables.
+     * NOTE! We can't use out-buffer as we have positioning data. */
+    unsigned int j = i;
+    for (; i < count; i++)
+    {
+      if (_hb_glyph_info_is_default_ignorable (&info[i]))
+      {
+	/* Merge clusters.
+	 * Same logic as buffer->delete_glyph(), but for in-place removal. */
+
+	unsigned int cluster = info[i].cluster;
+	if (i + 1 < count && cluster == info[i + 1].cluster)
+	  continue; /* Cluster survives; do nothing. */
+
+	if (j)
+	{
+	  /* Merge cluster backward. */
+	  if (cluster < info[j - 1].cluster)
+	  {
+	    unsigned int old_cluster = info[j - 1].cluster;
+	    for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
+	      info[k - 1].cluster = cluster;
+	  }
+	  continue;
+	}
+
+	if (i + 1 < count)
+	  buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
+
+	continue;
+      }
+
+      if (j != i)
+      {
+	info[j] = info[i];
+	pos[j] = pos[i];
+      }
+      j++;
+    }
+    buffer->len = j;
+  }
+}
+
+
 static inline void
 hb_ot_map_glyphs_fast (hb_buffer_t  *buffer)
 {
   /* Normalization process sets up glyph_index(), we just copy it. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
     info[i].codepoint = info[i].glyph_index();
@@ -620,16 +745,18 @@ static inline void
 hb_ot_position (hb_ot_shape_context_t *c)
 {
   hb_ot_layout_position_start (c->font, c->buffer);
 
   hb_ot_position_default (c);
 
   hb_bool_t fallback = !hb_ot_position_complex (c);
 
+  hb_ot_zero_width_default_ignorables (c);
+
   hb_ot_layout_position_finish (c->font, c->buffer);
 
   if (fallback && c->plan->shaper->fallback_position)
     _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
 
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
 
@@ -637,63 +764,16 @@ hb_ot_position (hb_ot_shape_context_t *c
 
   if (fallback)
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
 
-/* Post-process */
-
-static void
-hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
-{
-  if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
-    return;
-
-  hb_codepoint_t space;
-  enum {
-    SPACE_DONT_KNOW,
-    SPACE_AVAILABLE,
-    SPACE_UNAVAILABLE
-  } space_status = SPACE_DONT_KNOW;
-
-  unsigned int count = c->buffer->len;
-  hb_glyph_info_t *info = c->buffer->info;
-  hb_glyph_position_t *pos = c->buffer->pos;
-  unsigned int j = 0;
-  for (unsigned int i = 0; i < count; i++)
-  {
-    if (unlikely (!_hb_glyph_info_ligated (&info[i]) &&
-		  _hb_glyph_info_is_default_ignorable (&info[i])))
-    {
-      if (space_status == SPACE_DONT_KNOW)
-	space_status = c->font->get_glyph (' ', 0, &space) ? SPACE_AVAILABLE : SPACE_UNAVAILABLE;
-
-      if (space_status == SPACE_AVAILABLE)
-      {
-	info[i].codepoint = space;
-	pos[i].x_advance = 0;
-	pos[i].y_advance = 0;
-      }
-      else
-	continue; /* Delete it. XXX Merge clusters? */
-    }
-    if (j != i)
-    {
-      info[j] = info[i];
-      pos[j] = pos[i];
-    }
-    j++;
-  }
-  c->buffer->len = j;
-}
-
-
 /* Pull it all together! */
 
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
 
   /* Save the original direction, we use it later. */
@@ -731,16 +811,19 @@ hb_bool_t
 {
   hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features};
   hb_ot_shape_internal (&c);
 
   return true;
 }
 
 
+/**
+ * Since: 0.9.7
+ **/
 void
 hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
 				  hb_tag_t         table_tag,
 				  hb_set_t        *lookup_indexes /* OUT */)
 {
   /* XXX Does the first part always succeed? */
   HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes);
 }
@@ -761,16 +844,19 @@ add_char (hb_font_t          *font,
   {
     hb_codepoint_t m = unicode->mirroring (u);
     if (m != u && font->get_glyph (m, 0, &glyph))
       glyphs->add (glyph);
   }
 }
 
 
+/**
+ * Since: 0.9.2
+ **/
 void
 hb_ot_shape_glyphs_closure (hb_font_t          *font,
 			    hb_buffer_t        *buffer,
 			    const hb_feature_t *features,
 			    unsigned int        num_features,
 			    hb_set_t           *glyphs)
 {
   hb_ot_shape_plan_t plan;
--- a/gfx/harfbuzz/src/hb-ot-tag.cc
+++ b/gfx/harfbuzz/src/hb-ot-tag.cc
@@ -170,16 +170,21 @@ typedef struct {
  *
  * Updated on 2012-12-07 with more research into remaining codes.
  *
  * Updated on 2013-11-23 based on usage in SIL and Microsoft fonts,
  * the new proposal from Microsoft, and latest ISO 639-3 names.
  *
  * Some items still missing.  Those are commented out at the end.
  * Keep sorted for bsearch.
+ *
+ * Updated as of 2015-05-06: OT1.7 on MS website has some newer
+ * items that we don't have here, eg. Zazaki.  This is the new
+ * items in OpenType 1.7 (red items), most of which we have:
+ * http://www.microsoft.com/typography/otspec170/languagetags.htm
  */
 
 static const LangTag ot_languages[] = {
   {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
   {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
   {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
   {"ach",	HB_TAG('A','C','H',' ')},	/* Acoli */
   {"ada",	HB_TAG('D','N','G',' ')},	/* Dangme */
@@ -212,19 +217,19 @@ static const LangTag ot_languages[] = {
   {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [family] */
   {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolangauge] */
   {"ban",	HB_TAG('B','A','N',' ')},	/* Balinese */
   {"bar",	HB_TAG('B','A','R',' ')},	/* Bavarian */
   {"bbc",	HB_TAG('B','B','C',' ')},	/* Batak Toba */
   {"bci",	HB_TAG('B','A','U',' ')},	/* Baoulé */
   {"bcl",	HB_TAG('B','I','K',' ')},	/* Central Bikol */
   {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
-  {"be",	HB_TAG('B','E','L',' ')},  	/* Belarusian */
+  {"be",	HB_TAG('B','E','L',' ')},	/* Belarusian */
   {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
-  {"ber",	HB_TAG('B','E','R',' ')},  	/* Berber [family] */
+  {"ber",	HB_TAG('B','E','R',' ')},	/* Berber [family] */
   {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
   {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
   {"bfy",	HB_TAG('B','A','G',' ')},	/* Baghelkhandi */
   {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
   {"bgc",	HB_TAG('B','G','C',' ')},	/* Haryanvi */
   {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
   {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) */
@@ -341,19 +346,19 @@ static const LangTag ot_languages[] = {
   {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
   {"guc",	HB_TAG('G','U','C',' ')},	/* Wayuu */
   {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
 /*{"guk",	HB_TAG('G','U','K',' ')},*/	/* Gumuz (in SIL fonts) */
   {"guz",	HB_TAG('G','U','Z',' ')},	/* Ekegusii/Gusii */
   {"gv",	HB_TAG('M','N','X',' ')},	/* Manx */
   {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
   {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
-  {"haw",	HB_TAG('H','A','W',' ')},  	/* Hawaiian */
-  {"hay",	HB_TAG('H','A','Y',' ')},  	/* Haya */
-  {"haz",	HB_TAG('H','A','Z',' ')},  	/* Hazaragi */
+  {"haw",	HB_TAG('H','A','W',' ')},	/* Hawaiian */
+  {"hay",	HB_TAG('H','A','Y',' ')},	/* Haya */
+  {"haz",	HB_TAG('H','A','Z',' ')},	/* Hazaragi */
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
   {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
   {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
   {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
   {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
   {"hno",	HB_TAG('H','N','D',' ')},	/* [Northern] Hindko */
   {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
@@ -537,16 +542,17 @@ static const LangTag ot_languages[] = {
   {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Thai */
   {"noe",	HB_TAG('N','O','E',' ')},	/* Nimadi */
   {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
   {"nov",	HB_TAG('N','O','V',' ')},	/* Novial */
   {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
   {"nr",	HB_TAG('N','D','B',' ')},	/* [South] Ndebele */
   {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
   {"nso",	HB_TAG('S','O','T',' ')},	/* [Northern] Sotho */
+  {"nv",	HB_TAG('N','A','V',' ')},	/* Navajo */
   {"ny",	HB_TAG('C','H','I',' ')},	/* Chewa/Chichwa/Nyanja */
   {"nym",	HB_TAG('N','Y','M',' ')},	/* Nyamwezi */
   {"nyn",	HB_TAG('N','K','L',' ')},	/* Nyankole */
   {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
   {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] */
   {"ojs",	HB_TAG('O','C','R',' ')},	/* Oji-Cree */
   {"om",	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
   {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
@@ -859,16 +865,25 @@ hb_ot_tag_from_language (hb_language_t l
   if (s - lang_str == 3) {
     /* Assume it's ISO-639-3 and upper-case and use it. */
     return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u;
   }
 
   return HB_OT_TAG_DEFAULT_LANGUAGE;
 }
 
+/**
+ * hb_ot_tag_to_language:
+ *
+ * 
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 1.0
+ **/
 hb_language_t
 hb_ot_tag_to_language (hb_tag_t tag)
 {
   unsigned int i;
 
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     return NULL;
 
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -188,17 +188,18 @@ static inline unsigned int ARRAY_LENGTH 
 
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
 #define _ASSERT_STATIC1(_line, _cond)	HB_UNUSED typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
 #define _ASSERT_STATIC0(_line, _cond)	_ASSERT_STATIC1 (_line, (_cond))
 #define ASSERT_STATIC(_cond)		_ASSERT_STATIC0 (__LINE__, (_cond))
 
-#define ASSERT_STATIC_EXPR(_cond)((void) sizeof (char[(_cond) ? 1 : -1]))
+/* Note: C++ allows sizeof() of variable-lengh arrays.  So, if _cond is not
+ * constant, it still compiles (ouch!), but at least we'll get a -Wvla warning. */
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * sizeof (char[(_cond) ? 1 : -1]))
 
 #define _PASTE1(a,b) a##b
 #define PASTE(a,b) _PASTE1(a,b)
 
 /* Lets assert int types.  Saves trouble down the road. */
 
 ASSERT_STATIC (sizeof (int8_t) == 1);
@@ -242,18 +243,18 @@ ASSERT_STATIC (sizeof (hb_var_int_t) == 
 # define ASSERT_POD()		_ASSERT_POD0 (__LINE__)
 
 
 
 /* Misc */
 
 /* Void! */
 struct _hb_void_t {};
-typedef const _hb_void_t &hb_void_t;
-#define HB_VOID (* (const _hb_void_t *) NULL)
+typedef const _hb_void_t *hb_void_t;
+#define HB_VOID ((const _hb_void_t *) NULL)
 
 /* Return the number of 1 bits in mask. */
 static inline HB_CONST_FUNC unsigned int
 _hb_popcount32 (uint32_t mask)
 {
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
   return __builtin_popcount (mask);
 #else
@@ -840,19 +841,21 @@ template <typename T> static inline bool
 hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
 {
   return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
 }
 
 
 /* Useful for set-operations on small enums.
  * For example, for testing "x ∈ {x1, x2, x3}" use:
- * (FLAG(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
+ * (FLAG_SAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
  */
-#define FLAG(x) (1<<(x))
+#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((x) < 32) + (1U << (x)))
+#define FLAG_SAFE(x) (1U << (x))
+#define FLAG_UNSAFE(x) ((x) < 32 ? FLAG_SAFE(x) : 0)
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
 template <typename T, typename T2> static inline void
 hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
 {
   if (unlikely (!len))
     return;
--- a/gfx/harfbuzz/src/hb-set-private.hh
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -31,17 +31,25 @@
 #include "hb-object-private.hh"
 
 
 /*
  * The set digests here implement various "filters" that support
  * "approximate member query".  Conceptually these are like Bloom
  * Filter and Quotient Filter, however, much smaller, faster, and
  * designed to fit the requirements of our uses for glyph coverage
- * queries.  As a result, our filters have much higher.
+ * queries.
+ *
+ * Our filters are highly accurate if the lookup covers fairly local
+ * set of glyphs, but fully flooded and ineffective if coverage is
+ * all over the place.
+ *
+ * The frozen-set can be used instead of a digest, to trade more
+ * memory for 100% accuracy, but in practice, that doesn't look like
+ * an attractive trade-off.
  */
 
 template <typename mask_t, unsigned int shift>
 struct hb_set_digest_lowest_bits_t
 {
   ASSERT_POD ();
 
   static const unsigned int mask_bytes = sizeof (mask_t);
--- a/gfx/harfbuzz/src/hb-set.cc
+++ b/gfx/harfbuzz/src/hb-set.cc
@@ -30,17 +30,17 @@
 /* Public API */
 
 
 /**
  * hb_set_create: (Xconstructor)
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_set_t *
 hb_set_create (void)
 {
   hb_set_t *set;
 
   if (!(set = hb_object_create<hb_set_t> ()))
     return hb_set_get_empty ();
@@ -50,17 +50,17 @@ hb_set_create (void)
   return set;
 }
 
 /**
  * hb_set_get_empty:
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_set_t *
 hb_set_get_empty (void)
 {
   static const hb_set_t _hb_set_nil = {
     HB_OBJECT_HEADER_STATIC,
     true, /* in_error */
 
@@ -71,29 +71,29 @@ hb_set_get_empty (void)
 }
 
 /**
  * hb_set_reference: (skip)
  * @set: a set.
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_set_t *
 hb_set_reference (hb_set_t *set)
 {
   return hb_object_reference (set);
 }
 
 /**
  * hb_set_destroy: (skip)
  * @set: a set.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_destroy (hb_set_t *set)
 {
   if (!hb_object_destroy (set)) return;
 
   set->fini ();
 
@@ -105,17 +105,17 @@ hb_set_destroy (hb_set_t *set)
  * @set: a set.
  * @key:
  * @data:
  * @destroy (closure data):
  * @replace:
  *
  * Return value:
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_set_user_data (hb_set_t           *set,
 		      hb_user_data_key_t *key,
 		      void *              data,
 		      hb_destroy_func_t   destroy,
 		      hb_bool_t           replace)
 {
@@ -124,17 +124,17 @@ hb_set_set_user_data (hb_set_t          
 
 /**
  * hb_set_get_user_data: (skip)
  * @set: a set.
  * @key:
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_set_get_user_data (hb_set_t           *set,
 		      hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (set, key);
 }
 
@@ -142,131 +142,131 @@ hb_set_get_user_data (hb_set_t          
 /**
  * hb_set_allocation_successful:
  * @set: a set.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_allocation_successful (const hb_set_t  *set HB_UNUSED)
 {
   return !set->in_error;
 }
 
 /**
  * hb_set_clear:
  * @set: a set.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_clear (hb_set_t *set)
 {
   set->clear ();
 }
 
 /**
  * hb_set_is_empty:
  * @set: a set.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_set_is_empty (const hb_set_t *set)
 {
   return set->is_empty ();
 }
 
 /**
  * hb_set_has:
  * @set: a set.
  * @codepoint: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_has (const hb_set_t *set,
 	    hb_codepoint_t  codepoint)
 {
   return set->has (codepoint);
 }
 
 /**
  * hb_set_add:
  * @set: a set.
  * @codepoint: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_add (hb_set_t       *set,
 	    hb_codepoint_t  codepoint)
 {
   set->add (codepoint);
 }
 
 /**
  * hb_set_add_range:
  * @set: a set.
  * @first: 
  * @last: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_set_add_range (hb_set_t       *set,
 		  hb_codepoint_t  first,
 		  hb_codepoint_t  last)
 {
   set->add_range (first, last);
 }
 
 /**
  * hb_set_del:
  * @set: a set.
  * @codepoint: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_del (hb_set_t       *set,
 	    hb_codepoint_t  codepoint)
 {
   set->del (codepoint);
 }
 
 /**
  * hb_set_del_range:
  * @set: a set.
  * @first: 
  * @last: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_set_del_range (hb_set_t       *set,
 		  hb_codepoint_t  first,
 		  hb_codepoint_t  last)
 {
   set->del_range (first, last);
 }
@@ -275,177 +275,177 @@ hb_set_del_range (hb_set_t       *set,
  * hb_set_is_equal:
  * @set: a set.
  * @other: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_set_is_equal (const hb_set_t *set,
 		 const hb_set_t *other)
 {
   return set->is_equal (other);
 }
 
 /**
  * hb_set_set:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_set (hb_set_t       *set,
 	    const hb_set_t *other)
 {
   set->set (other);
 }
 
 /**
  * hb_set_union:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_union (hb_set_t       *set,
 	      const hb_set_t *other)
 {
   set->union_ (other);
 }
 
 /**
  * hb_set_intersect:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_intersect (hb_set_t       *set,
 		  const hb_set_t *other)
 {
   set->intersect (other);
 }
 
 /**
  * hb_set_subtract:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_subtract (hb_set_t       *set,
 		 const hb_set_t *other)
 {
   set->subtract (other);
 }
 
 /**
  * hb_set_symmetric_difference:
  * @set: a set.
  * @other: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_set_symmetric_difference (hb_set_t       *set,
 			     const hb_set_t *other)
 {
   set->symmetric_difference (other);
 }
 
 /**
  * hb_set_invert:
  * @set: a set.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.10
  **/
 void
 hb_set_invert (hb_set_t *set)
 {
   set->invert ();
 }
 
 /**
  * hb_set_get_population:
  * @set: a set.
  *
  * Returns the number of numbers in the set.
  *
  * Return value: set population.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 unsigned int
 hb_set_get_population (const hb_set_t *set)
 {
   return set->get_population ();
 }
 
 /**
  * hb_set_get_min:
  * @set: a set.
  *
  * Finds the minimum number in the set.
  *
  * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_codepoint_t
 hb_set_get_min (const hb_set_t *set)
 {
   return set->get_min ();
 }
 
 /**
  * hb_set_get_max:
  * @set: a set.
  *
  * Finds the maximum number in the set.
  *
  * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_codepoint_t
 hb_set_get_max (const hb_set_t *set)
 {
   return set->get_max ();
 }
 
 /**
  * hb_set_next:
  * @set: a set.
  * @codepoint: (inout):
  *
  * 
  *
  * Return value: whether there was a next value.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_next (const hb_set_t *set,
 	     hb_codepoint_t *codepoint)
 {
   return set->next (codepoint);
 }
 
@@ -455,17 +455,17 @@ hb_set_next (const hb_set_t *set,
  * @first: (out): output first codepoint in the range.
  * @last: (inout): input current last and output last codepoint in the range.
  *
  * Gets the next consecutive range of numbers in @set that
  * are greater than current value of @last.
  *
  * Return value: whether there was a next range.
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_set_next_range (const hb_set_t *set,
 		   hb_codepoint_t *first,
 		   hb_codepoint_t *last)
 {
   return set->next_range (first, last);
 }
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -101,17 +101,17 @@ hb_shape_plan_plan (hb_shape_plan_t    *
  * @user_features: (array length=num_user_features):
  * @num_user_features: 
  * @shaper_list: (array zero-terminated=1):
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_create (hb_face_t                     *face,
 		      const hb_segment_properties_t *props,
 		      const hb_feature_t            *user_features,
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list)
 {
@@ -153,17 +153,17 @@ hb_shape_plan_create (hb_face_t         
 
 /**
  * hb_shape_plan_get_empty:
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_get_empty (void)
 {
   static const hb_shape_plan_t _hb_shape_plan_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     true, /* default_shaper_list */
@@ -189,31 +189,31 @@ hb_shape_plan_get_empty (void)
 /**
  * hb_shape_plan_reference: (skip)
  * @shape_plan: a shape plan.
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
 {
   return hb_object_reference (shape_plan);
 }
 
 /**
  * hb_shape_plan_destroy: (skip)
  * @shape_plan: a shape plan.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
 {
   if (!hb_object_destroy (shape_plan)) return;
 
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
 #include "hb-shaper-list.hh"
@@ -231,17 +231,17 @@ hb_shape_plan_destroy (hb_shape_plan_t *
  * @data: 
  * @destroy: 
  * @replace: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
 			     hb_user_data_key_t *key,
 			     void *              data,
 			     hb_destroy_func_t   destroy,
 			     hb_bool_t           replace)
 {
@@ -252,17 +252,17 @@ hb_shape_plan_set_user_data (hb_shape_pl
  * hb_shape_plan_get_user_data: (skip)
  * @shape_plan: a shape plan.
  * @key: 
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void *
 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
 			     hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (shape_plan, key);
 }
 
@@ -274,17 +274,17 @@ hb_shape_plan_get_user_data (hb_shape_pl
  * @buffer: a buffer.
  * @features: (array length=num_features):
  * @num_features: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_bool_t
 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
 		       hb_font_t          *font,
 		       hb_buffer_t        *buffer,
 		       const hb_feature_t *features,
 		       unsigned int        num_features)
 {
@@ -390,17 +390,17 @@ hb_non_global_user_features_present (con
  * @user_features: (array length=num_user_features):
  * @num_user_features: 
  * @shaper_list: (array zero-terminated=1):
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 hb_shape_plan_t *
 hb_shape_plan_create_cached (hb_face_t                     *face,
 			     const hb_segment_properties_t *props,
 			     const hb_feature_t            *user_features,
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list)
 {
@@ -481,15 +481,15 @@ retry:
 /**
  * hb_shape_plan_get_shaper:
  * @shape_plan: a shape plan.
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 const char *
 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
 {
   return shape_plan->shaper_name;
 }
--- a/gfx/harfbuzz/src/hb-shape.cc
+++ b/gfx/harfbuzz/src/hb-shape.cc
@@ -28,16 +28,27 @@
 
 #include "hb-private.hh"
 
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-font-private.hh"
 
+/**
+ * SECTION:hb-shape
+ * @title: Shaping
+ * @short_description: Conversion of text strings into positioned glyphs
+ * @include: hb.h
+ *
+ * Shaping is the central operation of HarfBuzz. Shaping operates on buffers,
+ * which are sequences of Unicode characters that use the same font and have
+ * the same text direction, script and language. After shaping the buffer
+ * contains the output glyphs and their positions.
+ **/
 
 static bool
 parse_space (const char **pp, const char *end)
 {
   while (*pp < end && ISSPACE (**pp))
     (*pp)++;
   return true;
 }
@@ -193,25 +204,26 @@ parse_one_feature (const char **pp, cons
 	 parse_feature_indices (pp, end, feature) &&
 	 parse_feature_value_postfix (pp, end, feature) &&
 	 parse_space (pp, end) &&
 	 *pp == end;
 }
 
 /**
  * hb_feature_from_string:
- * @str: (array length=len) (element-type uint8_t):
- * @len: 
- * @feature: (out) (optional):
+ * @str: (array length=len) (element-type uint8_t): a string to parse
+ * @len: length of @str, or -1 if string is nul-terminated
+ * @feature: (out): the #hb_feature_t to initialize with the parsed values
  *
- * 
+ * Parses a string into a #hb_feature_t. If @len is -1 then @str is
+ * %NULL-terminated.
  *
- * Return value: 
+ * Return value: %TRUE if @str is successfully parsed, %FALSE otherwise
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 hb_bool_t
 hb_feature_from_string (const char *str, int len,
 			hb_feature_t *feature)
 {
   hb_feature_t feat;
 
   if (len < 0)
@@ -226,23 +238,25 @@ hb_feature_from_string (const char *str,
 
   if (feature)
     memset (feature, 0, sizeof (*feature));
   return false;
 }
 
 /**
  * hb_feature_to_string:
- * @feature: 
- * @buf: (array length=size):
- * @size: 
+ * @feature: an #hb_feature_t to convert
+ * @buf: (array length=size) (out): output string
+ * @size: the allocated size of @buf
  *
- * 
+ * Converts a #hb_feature_t into a %NULL-terminated string in the format
+ * understood by hb_feature_from_string(). The client in responsible for
+ * allocating big enough size for @buf, 128 bytes is more than enough.
  *
- * Since: 1.0
+ * Since: 0.9.5
  **/
 void
 hb_feature_to_string (hb_feature_t *feature,
 		      char *buf, unsigned int size)
 {
   if (unlikely (!size)) return;
 
   char s[128];
@@ -285,21 +299,22 @@ void free_static_shaper_list (void)
 {
   free (static_shaper_list);
 }
 #endif
 
 /**
  * hb_shape_list_shapers:
  *
- * 
+ * Retrieves the list of shapers supported by HarfBuzz.
  *
- * Return value: (transfer none):
+ * Return value: (transfer none) (array zero-terminated=1): an array of
+ *    constant strings
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char **
 hb_shape_list_shapers (void)
 {
 retry:
   const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list);
 
   if (unlikely (!shaper_list))
@@ -328,27 +343,31 @@ retry:
   }
 
   return shaper_list;
 }
 
 
 /**
  * hb_shape_full:
- * @font: a font.
- * @buffer: a buffer.
- * @features: (array length=num_features):
- * @num_features: 
- * @shaper_list: (array zero-terminated=1):
+ * @font: an #hb_font_t to use for shaping
+ * @buffer: an #hb_buffer_t to shape
+ * @features: (array length=num_features) (allow-none): an array of user
+ *    specified #hb_feature_t or %NULL
+ * @num_features: the length of @features array
+ * @shaper_list: (array zero-terminated=1) (allow-none): a %NULL-terminated
+ *    array of shapers to use or %NULL
  *
- * 
+ * See hb_shape() for details. If @shaper_list is not %NULL, the specified
+ * shapers will be used in the given order, otherwise the default shapers list
+ * will be used.
  *
- * Return value: 
+ * Return value: %FALSE if all shapers failed, %TRUE otherwise
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_shape_full (hb_font_t          *font,
 	       hb_buffer_t        *buffer,
 	       const hb_feature_t *features,
 	       unsigned int        num_features,
 	       const char * const *shaper_list)
 {
@@ -363,22 +382,27 @@ hb_shape_full (hb_font_t          *font,
 
   if (res)
     buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
   return res;
 }
 
 /**
  * hb_shape:
- * @font: a font.
- * @buffer: a buffer.
- * @features: (array length=num_features):
- * @num_features: 
+ * @font: an #hb_font_t to use for shaping
+ * @buffer: an #hb_buffer_t to shape
+ * @features: (array length=num_features) (allow-none): an array of user
+ *    specified #hb_feature_t or %NULL
+ * @num_features: the length of @features array
  *
- * 
+ * Shapes @buffer using @font turning its Unicode characters content to
+ * positioned glyphs. If @features is not %NULL, it will be used to control the
+ * features applied during shaping.
+ *
+ * Return value: %FALSE if all shapers failed, %TRUE otherwise
  *
  * Since: 1.0
  **/
 void
 hb_shape (hb_font_t           *font,
 	  hb_buffer_t         *buffer,
 	  const hb_feature_t  *features,
 	  unsigned int         num_features)
--- a/gfx/harfbuzz/src/hb-shape.h
+++ b/gfx/harfbuzz/src/hb-shape.h
@@ -42,23 +42,20 @@ HB_BEGIN_DECLS
 
 typedef struct hb_feature_t {
   hb_tag_t      tag;
   uint32_t      value;
   unsigned int  start;
   unsigned int  end;
 } hb_feature_t;
 
-/* len=-1 means str is NUL-terminated */
 hb_bool_t
 hb_feature_from_string (const char *str, int len,
 			hb_feature_t *feature);
 
-/* Something like 128 bytes is more than enough.
- * nul-terminates. */
 void
 hb_feature_to_string (hb_feature_t *feature,
 		      char *buf, unsigned int size);
 
 
 void
 hb_shape (hb_font_t           *font,
 	  hb_buffer_t         *buffer,
--- a/gfx/harfbuzz/src/hb-ucdn.cc
+++ b/gfx/harfbuzz/src/hb-ucdn.cc
@@ -143,16 +143,22 @@ static const hb_script_t ucdn_script_tra
     HB_SCRIPT_OLD_PERMIC,
     HB_SCRIPT_PAHAWH_HMONG,
     HB_SCRIPT_PALMYRENE,
     HB_SCRIPT_PAU_CIN_HAU,
     HB_SCRIPT_PSALTER_PAHLAVI,
     HB_SCRIPT_SIDDHAM,
     HB_SCRIPT_TIRHUTA,
     HB_SCRIPT_WARANG_CITI,
+    HB_SCRIPT_AHOM,
+    HB_SCRIPT_ANATOLIAN_HIEROGLYPHS,
+    HB_SCRIPT_HATRAN,
+    HB_SCRIPT_MULTANI,
+    HB_SCRIPT_OLD_HUNGARIAN,
+    HB_SCRIPT_SIGNWRITING,
 };
 
 static hb_unicode_combining_class_t
 hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
 			void *user_data HB_UNUSED)
 {
     return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode);
 }
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -303,15 +303,15 @@ extern HB_INTERNAL const hb_unicode_func
 #define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
 #define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */
 #define HB_MODIFIED_COMBINING_CLASS_CCC132 132 /* sign u */
 
 
 /* Misc */
 
 #define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \
-	(FLAG (gen_cat) & \
+	(FLAG_SAFE (gen_cat) & \
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
 
 
 #endif /* HB_UNICODE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-unicode.cc
+++ b/gfx/harfbuzz/src/hb-unicode.cc
@@ -141,24 +141,19 @@ hb_unicode_funcs_get_default (void)
 #define HB_UNICODE_FUNCS_NIL 1
   HB_UNICODE_FUNCS_IMPLEMENT(nil)
 #endif
 
 #undef HB_UNICODE_FUNCS_IMPLEMENT
 }
 
 #if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
-#ifdef _MSC_VER
-#pragma error("Could not find any Unicode functions implementation, you have to provide your own.")
-#pragma error("Consider building hb-ucdn.c.  If you absolutely want to build without any, check the code.")
-#else
 #error "Could not find any Unicode functions implementation, you have to provide your own"
 #error "Consider building hb-ucdn.c.  If you absolutely want to build without any, check the code."
 #endif
-#endif
 
 /**
  * hb_unicode_funcs_create: (Xconstructor)
  * @parent: (nullable):
  *
  * 
  *
  * Return value: (transfer full):
@@ -395,17 +390,17 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
  * @a: 
  * @b: 
  * @ab: (out):
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_unicode_compose (hb_unicode_funcs_t *ufuncs,
 		    hb_codepoint_t      a,
 		    hb_codepoint_t      b,
 		    hb_codepoint_t     *ab)
 {
   return ufuncs->compose (a, b, ab);
@@ -417,17 +412,17 @@ hb_unicode_compose (hb_unicode_funcs_t *
  * @ab: 
  * @a: (out):
  * @b: (out):
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
 		      hb_codepoint_t      ab,
 		      hb_codepoint_t     *a,
 		      hb_codepoint_t     *b)
 {
   return ufuncs->decompose (ab, a, b);
@@ -438,17 +433,17 @@ hb_unicode_decompose (hb_unicode_funcs_t
  * @ufuncs: Unicode functions.
  * @u: 
  * @decomposed: (out):
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
 				    hb_codepoint_t      u,
 				    hb_codepoint_t     *decomposed)
 {
   return ufuncs->decompose_compatibility (u, decomposed);
 }
--- a/gfx/harfbuzz/src/hb-unicode.h
+++ b/gfx/harfbuzz/src/hb-unicode.h
@@ -358,88 +358,113 @@ hb_unicode_funcs_set_script_func (hb_uni
  * hb_unicode_funcs_set_compose_func:
  * @ufuncs: a Unicode function structure
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_unicode_funcs_set_compose_func (hb_unicode_funcs_t *ufuncs,
 				   hb_unicode_compose_func_t func,
 				   void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_unicode_funcs_set_decompose_func:
  * @ufuncs: a Unicode function structure
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t *ufuncs,
 				     hb_unicode_decompose_func_t func,
 				     void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_unicode_funcs_set_decompose_compatibility_func:
  * @ufuncs: a Unicode function structure
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs,
 						   hb_unicode_decompose_compatibility_func_t func,
 						   void *user_data, hb_destroy_func_t destroy);
 
 /* accessors */
 
+/**
+ * Since: 0.9.2
+ **/
 hb_unicode_combining_class_t
 hb_unicode_combining_class (hb_unicode_funcs_t *ufuncs,
 			    hb_codepoint_t unicode);
 
+/**
+ * Since: 0.9.2
+ **/
 unsigned int
 hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs,
 			    hb_codepoint_t unicode);
 
+/**
+ * Since: 0.9.2
+ **/
 hb_unicode_general_category_t
 hb_unicode_general_category (hb_unicode_funcs_t *ufuncs,
 			     hb_codepoint_t unicode);
 
+/**
+ * Since: 0.9.2
+ **/
 hb_codepoint_t
 hb_unicode_mirroring (hb_unicode_funcs_t *ufuncs,
 		      hb_codepoint_t unicode);
 
+/**
+ * Since: 0.9.2
+ **/
 hb_script_t
 hb_unicode_script (hb_unicode_funcs_t *ufuncs,
 		   hb_codepoint_t unicode);
 
+/**
+ * Since: 0.9.2
+ **/
 hb_bool_t
 hb_unicode_compose (hb_unicode_funcs_t *ufuncs,
 		    hb_codepoint_t      a,
 		    hb_codepoint_t      b,
 		    hb_codepoint_t     *ab);
+
+/**
+ * Since: 0.9.2
+ **/
 hb_bool_t
 hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
 		      hb_codepoint_t      ab,
 		      hb_codepoint_t     *a,
 		      hb_codepoint_t     *b);
 
+/**
+ * Since: 0.9.2
+ **/
 unsigned int
 hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
 				    hb_codepoint_t      u,
 				    hb_codepoint_t     *decomposed);
 
 HB_END_DECLS
 
 #endif /* HB_UNICODE_H */
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -31,21 +31,21 @@
 #ifndef HB_VERSION_H
 #define HB_VERSION_H
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
-#define HB_VERSION_MAJOR 0
-#define HB_VERSION_MINOR 9
-#define HB_VERSION_MICRO 40
+#define HB_VERSION_MAJOR 1
+#define HB_VERSION_MINOR 0
+#define HB_VERSION_MICRO 1
 
-#define HB_VERSION_STRING "0.9.40"
+#define HB_VERSION_STRING "1.0.1"
 
 #define HB_VERSION_ATLEAST(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/hb-warning.cc
+++ b/gfx/harfbuzz/src/hb-warning.cc
@@ -24,26 +24,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
 
 
 #if defined(HB_ATOMIC_INT_NIL)
-#ifdef _MSC_VER
-#pragma error("Could not find any system to define atomic_int macros, library WILL NOT be thread-safe")
-#pragma error("Check hb-atomic-private.hh for possible resolutions.")
-#else
 #error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe"
 #error "Check hb-atomic-private.hh for possible resolutions."
 #endif
-#endif
 
 #if defined(HB_MUTEX_IMPL_NIL)
-#ifdef _MSC_VER
-#pragma error("Could not find any system to define mutex macros, library WILL NOT be thread-safe")
-#pragma error("Check hb-mutex-private.hh for possible resolutions.")
-#else
 #error "Could not find any system to define mutex macros, library WILL NOT be thread-safe"
 #error "Check hb-mutex-private.hh for possible resolutions."
 #endif
-#endif
--- a/gfx/harfbuzz/src/moz.build
+++ b/gfx/harfbuzz/src/moz.build
@@ -24,17 +24,17 @@ EXPORTS.harfbuzz += [
     '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-complex-use.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',
@@ -43,16 +43,17 @@ UNIFIED_SOURCES += [
     '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-complex-use-table.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',
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -186,16 +186,18 @@ public:
       , mRects(0)
     { }
 
     float mOffsetX;
     float mOffsetY;
     gfx::Matrix4x4 mMVMatrix;
     size_t mRects;
     gfx::Rect mLayerRects[4];
+    gfx::Rect mTextureRects[4];
+    std::list<GLuint> mTexIDs;
 };
 
 class ContentMonitor {
 public:
     using THArray = nsTArray<const TextureHost *>;
 
     // Notify the content of a TextureHost was changed.
     void SetChangedHost(const TextureHost* host) {
@@ -284,16 +286,28 @@ public:
     void NewDrawSession() {
         mSession = mozilla::MakeUnique<DrawSession>();
     }
 
     DrawSession& CurrentSession() {
         return *mSession;
     }
 
+    void SetPixelScale(double scale) {
+        mScale = scale;
+    }
+
+    double GetPixelScale() const {
+        return mScale;
+    }
+
+    LayerScopeManager()
+        : mScale(1.0)
+    {
+    }
 private:
     friend class CreateServerSocketRunnable;
     class CreateServerSocketRunnable : public nsRunnable
     {
     public:
         explicit CreateServerSocketRunnable(LayerScopeManager *aLayerScopeManager)
             : mLayerScopeManager(aLayerScopeManager)
         {
@@ -305,16 +319,17 @@ private:
         }
     private:
         LayerScopeManager* mLayerScopeManager;
     };
 
     mozilla::UniquePtr<LayerScopeWebSocketManager> mWebSocketManager;
     mozilla::UniquePtr<DrawSession> mSession;
     mozilla::UniquePtr<ContentMonitor> mContentMonitor;
+    double mScale;
 };
 
 LayerScopeManager gLayerScopeManager;
 
 /*
  * DebugGLData is the base class of
  * 1. DebugGLFrameStatusData (Frame start/end packet)
  * 2. DebugGLColorData (Color data packet)
@@ -362,16 +377,18 @@ public:
 
     virtual bool Write() override {
         Packet packet;
         packet.set_type(mDataType);
 
         FramePacket* fp = packet.mutable_frame();
         fp->set_value(static_cast<uint64_t>(mFrameStamp));
 
+        fp->set_scale(gLayerScopeManager.GetPixelScale());
+
         return WriteToStream(packet);
     }
 
 protected:
     int64_t mFrameStamp;
 };
 
 #ifdef MOZ_WIDGET_GONK
@@ -618,26 +635,30 @@ protected:
 
 class DebugGLDrawData final: public DebugGLData {
 public:
     DebugGLDrawData(float aOffsetX,
                     float aOffsetY,
                     const gfx::Matrix4x4& aMVMatrix,
                     size_t aRects,
                     const gfx::Rect* aLayerRects,
+                    const gfx::Rect* aTextureRects,
+                    const std::list<GLuint> aTexIDs,
                     void* aLayerRef)
         : DebugGLData(Packet::DRAW),
           mOffsetX(aOffsetX),
           mOffsetY(aOffsetY),
           mMVMatrix(aMVMatrix),
           mRects(aRects),
+          mTexIDs(aTexIDs),
           mLayerRef(reinterpret_cast<uint64_t>(aLayerRef))
     {
         for (size_t i = 0; i < mRects; i++){
             mLayerRects[i] = aLayerRects[i];
+            mTextureRects[i] = aTextureRects[i];
         }
     }
 
     virtual bool Write() override {
         Packet packet;
         packet.set_type(mDataType);
 
         DrawPacket* dp = packet.mutable_draw();
@@ -649,32 +670,46 @@ public:
         auto element = reinterpret_cast<Float *>(&mMVMatrix);
         for (int i = 0; i < 16; i++) {
           dp->add_mvmatrix(*element++);
         }
         dp->set_totalrects(mRects);
 
         MOZ_ASSERT(mRects > 0 && mRects < 4);
         for (size_t i = 0; i < mRects; i++) {
+            // Vertex
             layerscope::DrawPacket::Rect* pRect = dp->add_layerrect();
             pRect->set_x(mLayerRects[i].x);
             pRect->set_y(mLayerRects[i].y);
             pRect->set_w(mLayerRects[i].width);
             pRect->set_h(mLayerRects[i].height);
+
+            // UV
+            pRect = dp->add_texturerect();
+            pRect->set_x(mTextureRects[i].x);
+            pRect->set_y(mTextureRects[i].y);
+            pRect->set_w(mTextureRects[i].width);
+            pRect->set_h(mTextureRects[i].height);
+        }
+
+        for (GLuint texId: mTexIDs) {
+            dp->add_texids(texId);
         }
 
         return WriteToStream(packet);
     }
 
 protected:
     float mOffsetX;
     float mOffsetY;
     gfx::Matrix4x4 mMVMatrix;
     size_t mRects;
     gfx::Rect mLayerRects[4];
+    gfx::Rect mTextureRects[4];
+    std::list<GLuint> mTexIDs;
     uint64_t mLayerRef;
 };
 
 class DebugListener : public nsIServerSocketListener
 {
     virtual ~DebugListener() { }
 
 public:
@@ -950,16 +985,18 @@ SenderHelper::SendTextureSource(GLContex
         aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
                                                          size,
                                                          shaderConfig, aFlipY);
     gLayerScopeManager.GetSocketManager()->AppendDebugData(
         new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
                                aTexID, img));
 
     sTextureIdList.push_back(aTexID);
+    gLayerScopeManager.CurrentSession().mTexIDs.push_back(aTexID);
+
 }
 
 #ifdef MOZ_WIDGET_GONK
 bool
 SenderHelper::SendGraphicBuffer(void* aLayerRef,
                                 TextureSourceOGL* aSource,
                                 GLuint aTexID,
                                 const TexturedEffect* aEffect) {
@@ -978,16 +1015,18 @@ SenderHelper::SendGraphicBuffer(void* aL
     if (!package->TryPack(changed)) {
         return false;
     }
 
     // Transfer ownership to SocketManager.
     gLayerScopeManager.GetSocketManager()->AppendDebugData(package.release());
     sTextureIdList.push_back(aTexID);
 
+    gLayerScopeManager.CurrentSession().mTexIDs.push_back(aTexID);
+
     gLayerScopeManager.GetContentMonitor()->ClearChangedHost(aEffect->mState.mTexture);
     return true;
 }
 #endif
 
 void
 SenderHelper::SendTexturedEffect(GLContext* aGLContext,
                                  void* aLayerRef,
@@ -1580,73 +1619,82 @@ LayerScope::DrawBegin()
 {
     if (!CheckSendable()) {
         return;
     }
 
     gLayerScopeManager.NewDrawSession();
 }
 
-void LayerScope::SetRenderOffset(float aX, float aY)
+void
+LayerScope::SetRenderOffset(float aX, float aY)
 {
     if (!CheckSendable()) {
         return;
     }
 
     gLayerScopeManager.CurrentSession().mOffsetX = aX;
     gLayerScopeManager.CurrentSession().mOffsetY = aY;
 }
 
-void LayerScope::SetLayerTransform(const gfx::Matrix4x4& aMatrix)
+void
+LayerScope::SetLayerTransform(const gfx::Matrix4x4& aMatrix)
 {
     if (!CheckSendable()) {
         return;
     }
 
     gLayerScopeManager.CurrentSession().mMVMatrix = aMatrix;
 }
 
-void LayerScope::SetLayerRects(size_t aRects, const gfx::Rect* aLayerRects)
+void
+LayerScope::SetDrawRects(size_t aRects,
+                         const gfx::Rect* aLayerRects,
+                         const gfx::Rect* aTextureRects)
 {
     if (!CheckSendable()) {
         return;
     }
 
     MOZ_ASSERT(aRects > 0 && aRects <= 4);
     MOZ_ASSERT(aLayerRects);
 
     gLayerScopeManager.CurrentSession().mRects = aRects;
 
     for (size_t i = 0; i < aRects; i++){
         gLayerScopeManager.CurrentSession().mLayerRects[i] = aLayerRects[i];
+        gLayerScopeManager.CurrentSession().mTextureRects[i] = aTextureRects[i];
     }
 }
 
 void
 LayerScope::DrawEnd(gl::GLContext* aGLContext,
                     const EffectChain& aEffectChain,
                     int aWidth,
                     int aHeight)
 {
     // Protect this public function
     if (!CheckSendable()) {
         return;
     }
 
-    // 1. Send parameters of draw call, such as uniforms and attributes of
+    // 1. Send textures.
+    SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight);
+
+    // 2. Send parameters of draw call, such as uniforms and attributes of
     // vertex adnd fragment shader.
     DrawSession& draws = gLayerScopeManager.CurrentSession();
     gLayerScopeManager.GetSocketManager()->AppendDebugData(
         new DebugGLDrawData(draws.mOffsetX, draws.mOffsetY,
                             draws.mMVMatrix, draws.mRects,
                             draws.mLayerRects,
+                            draws.mTextureRects,
+                            draws.mTexIDs,
                             aEffectChain.mLayerRef));
 
-    // 2. Send textures.
-    SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight);
 }
 
 void
 LayerScope::SendLayer(LayerComposite* aLayer,
                       int aWidth,
                       int aHeight)
 {
     // Protect this public function
@@ -1698,16 +1746,21 @@ void
 LayerScope::SetHWComposed()
 {
     if (CheckSendable()) {
         gLayerScopeManager.GetSocketManager()->AppendDebugData(
             new DebugGLMetaData(Packet::META, true));
     }
 }
 
+void
+LayerScope::SetPixelScale(double devPixelsPerCSSPixel)
+{
+    gLayerScopeManager.SetPixelScale(devPixelsPerCSSPixel);
+}
 // ----------------------------------------------
 // LayerScopeAutoFrame implementation
 // ----------------------------------------------
 LayerScopeAutoFrame::LayerScopeAutoFrame(int64_t aFrameStamp)
 {
     // Do Begin Frame
     BeginFrame(aFrameStamp);
 }
@@ -1716,21 +1769,20 @@ LayerScopeAutoFrame::~LayerScopeAutoFram
 {
     // Do End Frame
     EndFrame();
 }
 
 void
 LayerScopeAutoFrame::BeginFrame(int64_t aFrameStamp)
 {
-    SenderHelper::ClearTextureIdList();
-
     if (!LayerScope::CheckSendable()) {
         return;
     }
+    SenderHelper::ClearTextureIdList();
 
     gLayerScopeManager.GetSocketManager()->AppendDebugData(
         new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp));
 }
 
 void
 LayerScopeAutoFrame::EndFrame()
 {
--- a/gfx/layers/LayerScope.h
+++ b/gfx/layers/LayerScope.h
@@ -21,30 +21,33 @@ namespace layerscope { class Packet; }
 struct EffectChain;
 class LayerComposite;
 
 class LayerScope {
 public:
     static void DrawBegin();
     static void SetRenderOffset(float aX, float aY);
     static void SetLayerTransform(const gfx::Matrix4x4& aMatrix);
-    static void SetLayerRects(size_t aRects, const gfx::Rect* aLayerRects);
+    static void SetDrawRects(size_t aRects,
+                             const gfx::Rect* aLayerRects,
+                             const gfx::Rect* aTextureRects);
     static void DrawEnd(gl::GLContext* aGLContext,
                         const EffectChain& aEffectChain,
                         int aWidth,
                         int aHeight);
 
     static void SendLayer(LayerComposite* aLayer,
                           int aWidth,
                           int aHeight);
     static void SendLayerDump(UniquePtr<layerscope::Packet> aPacket);
     static bool CheckSendable();
     static void CleanLayer();
     static void SetHWComposed();
 
+    static void SetPixelScale(double devPixelsPerCSSPixel);
     static void ContentChanged(TextureHost *host);
 private:
     static void Init();
 };
 
 // Perform BeginFrame and EndFrame automatically
 class LayerScopeAutoFrame {
 public:
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -31,31 +31,33 @@
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsStyleStruct.h"              // for nsTimingFunction, etc
 #include "protobuf/LayerScopePacket.pb.h"
+#include "mozilla/Compression.h"
 
 uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 namespace layers {
 
 FILE*
 FILEOrDefault(FILE* aFile)
 {
   return aFile ? aFile : stderr;
 }
 
 typedef FrameMetrics::ViewID ViewID;
 
 using namespace mozilla::gfx;
+using namespace mozilla::Compression;
 
 //--------------------------------------------------
 // LayerManager
 FrameMetrics::ViewID
 LayerManager::GetRootScrollableLayerId()
 {
   if (!mRoot) {
     return FrameMetrics::NULL_SCROLL_ID;
@@ -1587,16 +1589,45 @@ Layer::Dump(layerscope::LayersPacket* aP
   }
 
   if (Layer* next = GetNextSibling()) {
     next->Dump(aPacket, aParent);
   }
 }
 
 void
+Layer::SetDisplayListLog(const char* log)
+{
+  if (gfxUtils::DumpDisplayList()) {
+    mDisplayListLog = log;
+  }
+}
+
+void
+Layer::GetDisplayListLog(nsCString& log)
+{
+  log.SetLength(0);
+
+  if (gfxUtils::DumpDisplayList()) {
+    // This function returns a plain text string which consists of two things
+    //   1. DisplayList log.
+    //   2. Memory address of this layer.
+    // We know the target layer of each display item by information in #1.
+    // Here is an example of a Text display item line log in #1
+    //   Text p=0xa9850c00 f=0x0xaa405b00(.....
+    // f keeps the address of the target client layer of a display item.
+    // For LayerScope, display-item-to-client-layer mapping is not enough since
+    // LayerScope, which lives in the chrome process, knows only composite layers.
+    // As so, we need display-item-to-client-layer-to-layer-composite
+    // mapping. That's the reason we insert #2 into the log
+    log.AppendPrintf("0x%p\n%s",(void*) this, mDisplayListLog.get());
+  }
+}
+
+void
 Layer::Log(const char* aPrefix)
 {
   if (!IsLogEnabled())
     return;
 
   LogSelf(aPrefix);
 
   if (Layer* kid = GetFirstChild()) {
@@ -1802,20 +1833,32 @@ Layer::DumpPacket(layerscope::LayersPack
   layer->set_calpha(static_cast<bool>(GetContentFlags() & CONTENT_COMPONENT_ALPHA));
   // Vertical or horizontal bar
   if (GetScrollbarDirection() != NONE) {
     layer->set_direct(GetScrollbarDirection() == VERTICAL ?
                       LayersPacket::Layer::VERTICAL :
                       LayersPacket::Layer::HORIZONTAL);
     layer->set_barid(GetScrollbarTargetContainerId());
   }
+
   // Mask layer
   if (mMaskLayer) {
     layer->set_mask(reinterpret_cast<uint64_t>(mMaskLayer.get()));
   }
+
+  // DisplayList log.
+  if (mDisplayListLog.Length() > 0) {
+    layer->set_displaylistloglength(mDisplayListLog.Length());
+    auto compressedData =
+      MakeUnique<char[]>(LZ4::maxCompressedSize(mDisplayListLog.Length()));
+    int compressedSize = LZ4::compress((char*)mDisplayListLog.get(),
+                                       mDisplayListLog.Length(),
+                                       compressedData.get());
+    layer->set_displaylistlog(compressedData.get(), compressedSize);
+  }
 }
 
 void
 PaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   Layer::PrintInfo(aStream, aPrefix);
   if (!mValidRegion.IsEmpty()) {
     AppendToString(aStream, mValidRegion, " [valid=", "]");
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -253,21 +253,21 @@ public:
    * Function called to draw the contents of each PaintedLayer.
    * aRegionToDraw contains the region that needs to be drawn.
    * This would normally be a subregion of the visible region.
    * The callee must draw all of aRegionToDraw. Drawing outside
    * aRegionToDraw will be clipped out or ignored.
    * The callee must draw all of aRegionToDraw.
    * This region is relative to 0,0 in the PaintedLayer.
    *
-   * aDirtyRegion contains the region that is due to be painted at some point,
-   * even though only aRegionToDraw should be drawn during this call.
-   * They will often be equal, but aDirtyRegion may be larger, for example
-   * when painting progressively. aRegionToDraw must be entirely contained
-   * within aDirtyRegion.
+   * aDirtyRegion, if non-null, contains the total region that is due to be
+   * painted during the transaction, even though only aRegionToDraw should
+   * be drawn during this call. The sum of every aRegionToDraw over the
+   * course of the transaction must equal aDirtyRegion. aDirtyRegion can be
+   * null if the total dirty region is unknown.
    *
    * aRegionToInvalidate contains a region whose contents have been
    * changed by the layer manager and which must therefore be invalidated.
    * For example, this could be non-empty if a retained layer internally
    * switches from RGBA to RGB or back ... we might want to repaint it to
    * consistently use subpixel-AA or not.
    * This region is relative to 0,0 in the PaintedLayer.
    * aRegionToInvalidate may contain areas that are outside
@@ -279,17 +279,17 @@ public:
    * We guarantee that buffered contents in the visible
    * region are valid once drawing is complete.
    *
    * The origin of aContext is 0,0 in the PaintedLayer.
    */
   typedef void (* DrawPaintedLayerCallback)(PaintedLayer* aLayer,
                                            gfxContext* aContext,
                                            const nsIntRegion& aRegionToDraw,
-                                           const nsIntRegion& aDirtyRegion,
+                                           const nsIntRegion* aDirtyRegion,
                                            DrawRegionClip aClip,
                                            const nsIntRegion& aRegionToInvalidate,
                                            void* aCallbackData);
 
   /**
    * Finish the construction phase of the transaction, perform the
    * drawing phase, and end the transaction.
    * During the drawing phase, all PaintedLayers in the tree are
@@ -1578,16 +1578,26 @@ public:
   // an implementation that first calls the base implementation then
   // appends additional info to aTo.
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
   // Just like PrintInfo, but this function dump information into layerscope packet,
   // instead of a StringStream. It is also internally used to implement Dump();
   virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent);
 
+  /**
+   * Store display list log.
+   */
+  void SetDisplayListLog(const char *log);
+
+  /**
+   * Return display list log.
+   */
+  void GetDisplayListLog(nsCString& log);
+
   static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); }
 
   /**
    * Returns the current area of the layer (in layer-space coordinates)
    * marked as needed to be recomposited.
    */
   const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; }
   const void SetInvalidRegion(const nsIntRegion& aRect) { mInvalidRegion = aRect; }
@@ -1780,16 +1790,18 @@ protected:
   bool mIsScrollbarContainer;
   DebugOnly<uint32_t> mDebugColorIndex;
   // If this layer is used for OMTA, then this counter is used to ensure we
   // stay in sync with the animation manager
   uint64_t mAnimationGeneration;
 #ifdef MOZ_DUMP_PAINTING
   nsTArray<nsCString> mExtraDumpInfo;
 #endif
+  // Store display list log.
+  nsCString mDisplayListLog;
 };
 
 /**
  * A Layer which we can paint into. It is a conceptually
  * infinite surface, but each PaintedLayer has an associated "valid region"
  * of contents that it is currently storing, which is finite. PaintedLayer
  * implementations can store content between paints.
  *
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -730,31 +730,29 @@ RotatedContentBuffer::BorrowDrawTargetFo
   }
 
   DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
                                                          BUFFER_BOTH, aIter);
   if (!result) {
     return nullptr;
   }
 
-  if (result->GetBackendType() == BackendType::DIRECT2D ||
-      result->GetBackendType() == BackendType::DIRECT2D1_1) {
-    // Simplify the draw region to avoid hitting expensive drawing paths for
-    // complex regions. Must be applied to the entire draw region so that it
-    // remains a superset of the iterator's draw region.
-    aPaintState.mRegionToDraw.SimplifyOutwardByArea(100 * 100);
-  }
-
   nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
   if (aIter) {
     // The iterators draw region currently only contains the bounds of the region,
     // this makes it the precise region.
     aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
     drawPtr = &aIter->mDrawRegion;
   }
+  if (result->GetBackendType() == BackendType::DIRECT2D ||
+      result->GetBackendType() == BackendType::DIRECT2D1_1) {
+    // Simplify the draw region to avoid hitting expensive drawing paths
+    // for complex regions.
+    drawPtr->SimplifyOutwardByArea(100 * 100);
+  }
 
   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     if (!mDTBuffer || !mDTBufferOnWhite) {
       // This can happen in release builds if allocating one of the two buffers
       // failed. This in turn can happen if unreasonably large textures are
       // requested.
       return nullptr;
     }
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -86,17 +86,17 @@ BasicPaintedLayer::PaintThebes(gfxContex
                                             &needsClipToVisibleRegion);
         if (effectiveOperator != CompositionOp::OP_OVER) {
           needsClipToVisibleRegion = true;
         }
       } else {
         groupContext = aContext;
       }
       SetAntialiasingFlags(this, groupContext->GetDrawTarget());
-      aCallback(this, groupContext, toDraw, toDraw,
+      aCallback(this, groupContext, toDraw, &toDraw,
                 DrawRegionClip::NONE, nsIntRegion(), aCallbackData);
       if (needsGroup) {
         aContext->PopGroupToSource();
         if (needsClipToVisibleRegion) {
           gfxUtils::ClipToRegion(aContext, toDraw);
         }
         AutoSetOperator setOptimizedOperator(aContext, ThebesOp(effectiveOperator));
         PaintWithMask(aContext, opacity, aMaskLayer);
--- a/gfx/layers/basic/BasicPaintedLayer.h
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -107,17 +107,17 @@ protected:
               DrawRegionClip aClip,
               LayerManager::DrawPaintedLayerCallback aCallback,
               void* aCallbackData)
   {
     if (!aCallback) {
       BasicManager()->SetTransactionIncomplete();
       return;
     }
-    aCallback(this, aContext, aExtendedRegionToDraw, aExtendedRegionToDraw,
+    aCallback(this, aContext, aExtendedRegionToDraw, &aExtendedRegionToDraw,
               aClip, aRegionToInvalidate, aCallbackData);
     // Everything that's visible has been validated. Do this instead of just
     // OR-ing with aRegionToDraw, since that can lead to a very complex region
     // here (OR doesn't automatically simplify to the simplest possible
     // representation of a region.)
     nsIntRegion tmp;
     tmp.Or(mVisibleRegion, aExtendedRegionToDraw);
     mValidRegion.Or(mValidRegion, tmp);
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -82,17 +82,17 @@ ClientPaintedLayer::PaintThebes()
   while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
     SetAntialiasingFlags(this, target);
 
     nsRefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target);
 
     ClientManager()->GetPaintedLayerCallback()(this,
                                               ctx,
                                               iter.mDrawRegion,
-                                              state.mRegionToDraw,
+                                              nullptr,
                                               state.mClip,
                                               state.mRegionToInvalidate,
                                               ClientManager()->GetPaintedLayerCallbackData());
 
     ctx = nullptr;
     mContentClient->ReturnDrawTargetToBuffer(target);
     didUpdate = true;
   }
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -176,17 +176,17 @@ ClientSingleTiledLayerBuffer::PaintThebe
     dt = Factory::CreateDualDrawTarget(dt, dtOnWhite);
     dtOnWhite = nullptr;
   }
 
   {
     nsRefPtr<gfxContext> ctx = new gfxContext(dt);
     ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
 
-    aCallback(mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
+    aCallback(mPaintedLayer, ctx, paintRegion, &paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
   }
 
   // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
   // now out of sync.
   mTile.mInvalidFront.OrWith(tileDirtyRegion);
 
   // The new buffer is now validated, remove the dirty region from it.
   mTile.mInvalidBack.SubOut(tileDirtyRegion);
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -959,17 +959,17 @@ ClientMultiTiledLayerBuffer::PaintThebes
     if (PR_IntervalNow() - start > 3) {
       printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start);
     }
     start = PR_IntervalNow();
 #endif
     PROFILER_LABEL("ClientMultiTiledLayerBuffer", "PaintThebesSingleBufferDraw",
       js::ProfileEntry::Category::GRAPHICS);
 
-    mCallback(mPaintedLayer, ctxt, aPaintRegion, aDirtyRegion,
+    mCallback(mPaintedLayer, ctxt, aPaintRegion, &aDirtyRegion,
               DrawRegionClip::NONE, nsIntRegion(), mCallbackData);
   }
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   if (PR_IntervalNow() - start > 30) {
     const IntRect bounds = aPaintRegion.GetBounds();
     printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
     if (aPaintRegion.IsComplex()) {
@@ -1166,17 +1166,17 @@ void ClientMultiTiledLayerBuffer::Update
     tileset.mTileCount = mMoz2DTiles.size();
     RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
     drawTarget->SetTransform(Matrix());
 
     RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
     ctx->SetMatrix(
       ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
 
-    mCallback(mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
+    mCallback(mPaintedLayer, ctx, aPaintRegion, &aDirtyRegion,
               DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
     mMoz2DTiles.clear();
     // Reset:
     mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
                              std::numeric_limits<int32_t>::max());
   }
 
   bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -70,16 +70,18 @@
 #include "ProfilerMarkers.h"
 #endif
 #include "mozilla/VsyncDispatcher.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "GeckoTouchDispatcher.h"
 #endif
 
+#include "LayerScope.h"
+
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace std;
 
 using base::ProcessId;
@@ -676,16 +678,18 @@ CompositorParent::CompositorParent(nsIWi
   if (UseVsyncComposition()) {
     gfxDebugOnce() << "Enabling vsync compositor";
     mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
   } else {
     mCompositorScheduler = new CompositorSoftwareTimerScheduler(this);
   }
 
   gfxPlatform::GetPlatform()->ComputeTileSize();
+
+  LayerScope::SetPixelScale(mWidget->GetDefaultScale().scale);
 }
 
 bool
 CompositorParent::IsInCompositorThread()
 {
   return CompositorThread() && CompositorThread()->thread_id() == PlatformThread::CurrentId();
 }
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -342,16 +342,17 @@ LayerTransactionParent::RecvUpdate(Infal
       if (PLayerParent* maskLayer = common.maskLayerParent()) {
         layer->SetMaskLayer(cast(maskLayer)->AsLayer());
       } else {
         layer->SetMaskLayer(nullptr);
       }
       layer->SetAnimations(common.animations());
       layer->SetInvalidRegion(common.invalidRegion());
       layer->SetFrameMetrics(common.metrics());
+      layer->SetDisplayListLog(common.displayListLog().get());
 
       nsTArray<nsRefPtr<Layer>> maskLayers;
       for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) {
         Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer();
         maskLayers.AppendElement(maskLayer);
       }
       layer->SetAncestorMaskLayers(maskLayers);
 
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -228,16 +228,17 @@ struct CommonLayerAttributes {
   bool forceIsolatedGroup;
   nullable PLayer maskLayer;
   PLayer[] ancestorMaskLayers;
   // Animated colors will only honored for ColorLayers.
   Animation[] animations;
   nsIntRegion invalidRegion;
   FrameMetrics[] metrics;
   string contentDescription;
+  nsCString displayListLog;
 };
 
 struct PaintedLayerAttributes {
   nsIntRegion validRegion;
 };
 struct ContainerLayerAttributes {
   float preXScale;
   float preYScale;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -608,16 +608,20 @@ ShadowLayerForwarder::EndTransaction(Inf
     common.maskLayerParent() = nullptr;
     common.animations() = mutant->GetAnimations();
     common.invalidRegion() = mutant->GetInvalidRegion();
     common.metrics() = mutant->GetAllFrameMetrics();
     for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) {
       auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer());
       common.ancestorMaskLayersChild().AppendElement(layer);
     }
+    nsCString log;
+    mutant->GetDisplayListLog(log);
+    common.displayListLog() = log;
+
     attrs.specific() = null_t();
     mutant->FillSpecificAttributes(attrs.specific());
 
     MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
 
     mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
   }
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1622,17 +1622,17 @@ CompositorOGL::BindAndDrawQuads(ShaderPr
   aProg->SetLayerRects(aLayerRects);
   if (aProg->GetTextureCount() > 0) {
     aProg->SetTextureRects(aTextureRects);
   }
 
   // We are using GL_TRIANGLES here because the Mac Intel drivers fail to properly
   // process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
   mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
-  LayerScope::SetLayerRects(aQuads, aLayerRects);
+  LayerScope::SetDrawRects(aQuads, aLayerRects, aTextureRects);
 }
 
 GLBlitTextureImageHelper*
 CompositorOGL::BlitTextureImageHelper()
 {
     if (!mBlitTextureImageHelper) {
         mBlitTextureImageHelper = MakeUnique<GLBlitTextureImageHelper>(this);
     }
--- a/gfx/layers/protobuf/LayerScopePacket.pb.cc
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.cc
@@ -94,16 +94,17 @@ struct StaticDescriptorInitializer_Layer
   }
 } static_descriptor_initializer_LayerScopePacket_2eproto_;
 #endif
 
 // ===================================================================
 
 #ifndef _MSC_VER
 const int FramePacket::kValueFieldNumber;
+const int FramePacket::kScaleFieldNumber;
 #endif  // !_MSC_VER
 
 FramePacket::FramePacket()
   : ::google::protobuf::MessageLite() {
   SharedCtor();
   // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.FramePacket)
 }
 
@@ -115,16 +116,17 @@ FramePacket::FramePacket(const FramePack
   SharedCtor();
   MergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.FramePacket)
 }
 
 void FramePacket::SharedCtor() {
   _cached_size_ = 0;
   value_ = GOOGLE_ULONGLONG(0);
+  scale_ = 0;
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
 FramePacket::~FramePacket() {
   // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.FramePacket)
   SharedDtor();
 }
 
@@ -153,17 +155,31 @@ const FramePacket& FramePacket::default_
 
 FramePacket* FramePacket::default_instance_ = NULL;
 
 FramePacket* FramePacket::New() const {
   return new FramePacket;
 }
 
 void FramePacket::Clear() {
-  value_ = GOOGLE_ULONGLONG(0);
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>(      \
+  &reinterpret_cast<FramePacket*>(16)->f) - \
+   reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do {                              \
+    size_t f = OFFSET_OF_FIELD_(first);                    \
+    size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last);  \
+    ::memset(&first, 0, n);                                \
+  } while (0)
+
+  ZR_(value_, scale_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
   mutable_unknown_fields()->clear();
 }
 
 bool FramePacket::MergePartialFromCodedStream(
     ::google::protobuf::io::CodedInputStream* input) {
 #define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
   ::google::protobuf::uint32 tag;
@@ -182,16 +198,31 @@ bool FramePacket::MergePartialFromCodedS
         if (tag == 8) {
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
                  input, &value_)));
           set_has_value();
         } else {
           goto handle_unusual;
         }
+        if (input->ExpectTag(21)) goto parse_scale;
+        break;
+      }
+
+      // optional float scale = 2;
+      case 2: {
+        if (tag == 21) {
+         parse_scale:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+                 input, &scale_)));
+          set_has_scale();
+        } else {
+          goto handle_unusual;
+        }
         if (input->ExpectAtEnd()) goto success;
         break;
       }
 
       default: {
       handle_unusual:
         if (tag == 0 ||
             ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
@@ -216,32 +247,42 @@ failure:
 void FramePacket::SerializeWithCachedSizes(
     ::google::protobuf::io::CodedOutputStream* output) const {
   // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.FramePacket)
   // optional uint64 value = 1;
   if (has_value()) {
     ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->value(), output);
   }
 
+  // optional float scale = 2;
+  if (has_scale()) {
+    ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->scale(), output);
+  }
+
   output->WriteRaw(unknown_fields().data(),
                    unknown_fields().size());
   // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.FramePacket)
 }
 
 int FramePacket::ByteSize() const {
   int total_size = 0;
 
   if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
     // optional uint64 value = 1;
     if (has_value()) {
       total_size += 1 +
         ::google::protobuf::internal::WireFormatLite::UInt64Size(
           this->value());
     }
 
+    // optional float scale = 2;
+    if (has_scale()) {
+      total_size += 1 + 4;
+    }
+
   }
   total_size += unknown_fields().size();
 
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
   _cached_size_ = total_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -252,16 +293,19 @@ void FramePacket::CheckTypeAndMergeFrom(
 }
 
 void FramePacket::MergeFrom(const FramePacket& from) {
   GOOGLE_CHECK_NE(&from, this);
   if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
     if (from.has_value()) {
       set_value(from.value());
     }
+    if (from.has_scale()) {
+      set_scale(from.scale());
+    }
   }
   mutable_unknown_fields()->append(from.unknown_fields());
 }
 
 void FramePacket::CopyFrom(const FramePacket& from) {
   if (&from == this) return;
   Clear();
   MergeFrom(from);
@@ -270,16 +314,17 @@ void FramePacket::CopyFrom(const FramePa
 bool FramePacket::IsInitialized() const {
 
   return true;
 }
 
 void FramePacket::Swap(FramePacket* other) {
   if (other != this) {
     std::swap(value_, other->value_);
+    std::swap(scale_, other->scale_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     _unknown_fields_.swap(other->_unknown_fields_);
     std::swap(_cached_size_, other->_cached_size_);
   }
 }
 
 ::std::string FramePacket::GetTypeName() const {
   return "mozilla.layers.layerscope.FramePacket";
@@ -2454,16 +2499,18 @@ const int LayersPacket_Layer::kDispatchR
 const int LayersPacket_Layer::kNoActionRegionFieldNumber;
 const int LayersPacket_Layer::kHPanRegionFieldNumber;
 const int LayersPacket_Layer::kVPanRegionFieldNumber;
 const int LayersPacket_Layer::kValidFieldNumber;
 const int LayersPacket_Layer::kColorFieldNumber;
 const int LayersPacket_Layer::kFilterFieldNumber;
 const int LayersPacket_Layer::kRefIDFieldNumber;
 const int LayersPacket_Layer::kSizeFieldNumber;
+const int LayersPacket_Layer::kDisplayListLogLengthFieldNumber;
+const int LayersPacket_Layer::kDisplayListLogFieldNumber;
 #endif  // !_MSC_VER
 
 LayersPacket_Layer::LayersPacket_Layer()
   : ::google::protobuf::MessageLite() {
   SharedCtor();
   // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer)
 }
 
@@ -2539,16 +2586,17 @@ void LayersPacket_Layer::InitAsDefaultIn
 LayersPacket_Layer::LayersPacket_Layer(const LayersPacket_Layer& from)
   : ::google::protobuf::MessageLite() {
   SharedCtor();
   MergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer)
 }
 
 void LayersPacket_Layer::SharedCtor() {
+  ::google::protobuf::internal::GetEmptyString();
   _cached_size_ = 0;
   type_ = 0;
   ptr_ = GOOGLE_ULONGLONG(0);
   parentptr_ = GOOGLE_ULONGLONG(0);
   clip_ = NULL;
   transform_ = NULL;
   vregion_ = NULL;
   shadow_ = NULL;
@@ -2563,25 +2611,30 @@ void LayersPacket_Layer::SharedCtor() {
   noactionregion_ = NULL;
   hpanregion_ = NULL;
   vpanregion_ = NULL;
   valid_ = NULL;
   color_ = 0u;
   filter_ = 0;
   refid_ = GOOGLE_ULONGLONG(0);
   size_ = NULL;
+  displaylistloglength_ = 0u;
+  displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
 LayersPacket_Layer::~LayersPacket_Layer() {
   // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer)
   SharedDtor();
 }
 
 void LayersPacket_Layer::SharedDtor() {
+  if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+    delete displaylistlog_;
+  }
   #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
   if (this != &default_instance()) {
   #else
   if (this != default_instance_) {
   #endif
     delete clip_;
     delete transform_;
     delete vregion_;
@@ -2652,30 +2705,36 @@ void LayersPacket_Layer::Clear() {
     }
     if (has_dispatchregion()) {
       if (dispatchregion_ != NULL) dispatchregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
     }
     if (has_noactionregion()) {
       if (noactionregion_ != NULL) noactionregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
     }
   }
-  if (_has_bits_[16 / 32] & 8323072) {
+  if (_has_bits_[16 / 32] & 16711680) {
     ZR_(color_, refid_);
     if (has_hpanregion()) {
       if (hpanregion_ != NULL) hpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
     }
     if (has_vpanregion()) {
       if (vpanregion_ != NULL) vpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
     }
     if (has_valid()) {
       if (valid_ != NULL) valid_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
     }
     if (has_size()) {
       if (size_ != NULL) size_->::mozilla::layers::layerscope::LayersPacket_Layer_Size::Clear();
     }
+    displaylistloglength_ = 0u;
+  }
+  if (has_displaylistlog()) {
+    if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+      displaylistlog_->clear();
+    }
   }
 
 #undef OFFSET_OF_FIELD_
 #undef ZR_
 
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
   mutable_unknown_fields()->clear();
 }
@@ -3025,16 +3084,44 @@ bool LayersPacket_Layer::MergePartialFro
       case 104: {
         if (tag == 834) {
          parse_size:
           DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
                input, mutable_size()));
         } else {
           goto handle_unusual;
         }
+        if (input->ExpectTag(840)) goto parse_displayListLogLength;
+        break;
+      }
+
+      // optional uint32 displayListLogLength = 105;
+      case 105: {
+        if (tag == 840) {
+         parse_displayListLogLength:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+                 input, &displaylistloglength_)));
+          set_has_displaylistloglength();
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(850)) goto parse_displayListLog;
+        break;
+      }
+
+      // optional bytes displayListLog = 106;
+      case 106: {
+        if (tag == 850) {
+         parse_displayListLog:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+                input, this->mutable_displaylistlog()));
+        } else {
+          goto handle_unusual;
+        }
         if (input->ExpectAtEnd()) goto success;
         break;
       }
 
       default: {
       handle_unusual:
         if (tag == 0 ||
             ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
@@ -3183,16 +3270,27 @@ void LayersPacket_Layer::SerializeWithCa
   }
 
   // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
   if (has_size()) {
     ::google::protobuf::internal::WireFormatLite::WriteMessage(
       104, this->size(), output);
   }
 
+  // optional uint32 displayListLogLength = 105;
+  if (has_displaylistloglength()) {
+    ::google::protobuf::internal::WireFormatLite::WriteUInt32(105, this->displaylistloglength(), output);
+  }
+
+  // optional bytes displayListLog = 106;
+  if (has_displaylistlog()) {
+    ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased(
+      106, this->displaylistlog(), output);
+  }
+
   output->WriteRaw(unknown_fields().data(),
                    unknown_fields().size());
   // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer)
 }
 
 int LayersPacket_Layer::ByteSize() const {
   int total_size = 0;
 
@@ -3348,16 +3446,32 @@ int LayersPacket_Layer::ByteSize() const
 
     // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
     if (has_size()) {
       total_size += 2 +
         ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
           this->size());
     }
 
+    // optional uint32 displayListLogLength = 105;
+    if (has_displaylistloglength()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::UInt32Size(
+          this->displaylistloglength());
+    }
+
+  }
+  if (_has_bits_[24 / 32] & (0xffu << (24 % 32))) {
+    // optional bytes displayListLog = 106;
+    if (has_displaylistlog()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::BytesSize(
+          this->displaylistlog());
+    }
+
   }
   total_size += unknown_fields().size();
 
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
   _cached_size_ = total_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -3438,16 +3552,24 @@ void LayersPacket_Layer::MergeFrom(const
       set_filter(from.filter());
     }
     if (from.has_refid()) {
       set_refid(from.refid());
     }
     if (from.has_size()) {
       mutable_size()->::mozilla::layers::layerscope::LayersPacket_Layer_Size::MergeFrom(from.size());
     }
+    if (from.has_displaylistloglength()) {
+      set_displaylistloglength(from.displaylistloglength());
+    }
+  }
+  if (from._has_bits_[24 / 32] & (0xffu << (24 % 32))) {
+    if (from.has_displaylistlog()) {
+      set_displaylistlog(from.displaylistlog());
+    }
   }
   mutable_unknown_fields()->append(from.unknown_fields());
 }
 
 void LayersPacket_Layer::CopyFrom(const LayersPacket_Layer& from) {
   if (&from == this) return;
   Clear();
   MergeFrom(from);
@@ -3479,16 +3601,18 @@ void LayersPacket_Layer::Swap(LayersPack
     std::swap(noactionregion_, other->noactionregion_);
     std::swap(hpanregion_, other->hpanregion_);
     std::swap(vpanregion_, other->vpanregion_);
     std::swap(valid_, other->valid_);
     std::swap(color_, other->color_);
     std::swap(filter_, other->filter_);
     std::swap(refid_, other->refid_);
     std::swap(size_, other->size_);
+    std::swap(displaylistloglength_, other->displaylistloglength_);
+    std::swap(displaylistlog_, other->displaylistlog_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     _unknown_fields_.swap(other->_unknown_fields_);
     std::swap(_cached_size_, other->_cached_size_);
   }
 }
 
 ::std::string LayersPacket_Layer::GetTypeName() const {
   return "mozilla.layers.layerscope.LayersPacket.Layer";
@@ -4172,16 +4296,18 @@ void DrawPacket_Rect::Swap(DrawPacket_Re
 
 #ifndef _MSC_VER
 const int DrawPacket::kOffsetXFieldNumber;
 const int DrawPacket::kOffsetYFieldNumber;
 const int DrawPacket::kMvMatrixFieldNumber;
 const int DrawPacket::kTotalRectsFieldNumber;
 const int DrawPacket::kLayerRectFieldNumber;
 const int DrawPacket::kLayerrefFieldNumber;
+const int DrawPacket::kTexIDsFieldNumber;
+const int DrawPacket::kTextureRectFieldNumber;
 #endif  // !_MSC_VER
 
 DrawPacket::DrawPacket()
   : ::google::protobuf::MessageLite() {
   SharedCtor();
   // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.DrawPacket)
 }
 
@@ -4244,24 +4370,29 @@ void DrawPacket::Clear() {
    reinterpret_cast<char*>(16))
 
 #define Z