merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 15 Oct 2015 11:52:40 +0200
changeset 267857 e193b4da0a8c1025aa76a403c64663ff1cd41709
parent 267856 3c18fce625f9dc8973dc492e1ce733334c8e0eca (current diff)
parent 267807 1c9516a8f8f5cc996e8d26b88769ed4c72923e32 (diff)
child 267858 ceb07c2b56f92e7674a5f54ecdb3e04d9262f2a6
child 267891 6c3246a2a4acaf735c2231a7cf15de5a5d6539d2
child 267904 f9e8bd374203682da519f6e3a63fa44157013033
push id66610
push usercbook@mozilla.com
push dateThu, 15 Oct 2015 09:57:55 +0000
treeherdermozilla-inbound@ceb07c2b56f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
e193b4da0a8c / 44.0a1 / 20151015030233 / files
nightly linux64
e193b4da0a8c / 44.0a1 / 20151015030233 / files
nightly mac
e193b4da0a8c / 44.0a1 / 20151015030233 / files
nightly win32
e193b4da0a8c / 44.0a1 / 20151015030233 / files
nightly win64
e193b4da0a8c / 44.0a1 / 20151015030233 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -257,16 +257,17 @@ DocAccessibleParent::Destroy()
   MOZ_ASSERT(!mShutdown);
   mShutdown = true;
 
   uint32_t childDocCount = mChildDocs.Length();
   for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
     mChildDocs[i]->Destroy();
 
   for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
+    MOZ_ASSERT(iter.Get()->mProxy != this);
     ProxyDestroyed(iter.Get()->mProxy);
     iter.Remove();
   }
   ProxyDestroyed(this);
   if (mParentDoc)
     mParentDoc->RemoveChildDoc(this);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -56,24 +56,27 @@ a11y::ProxyDestroyed(ProxyAccessible* aP
   AccessibleWrap* wrapper =
     reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
   MOZ_ASSERT(wrapper);
   if (!wrapper)
     return;
 
   auto doc =
     static_cast<DocProxyAccessibleWrap*>(WrapperFor(aProxy->Document()));
+  MOZ_ASSERT(doc);
+  if (doc) {
 #ifdef _WIN64
-  uint32_t id = wrapper->GetExistingID();
-  if (id != AccessibleWrap::kNoID) {
-    doc->RemoveID(id);
+    uint32_t id = wrapper->GetExistingID();
+    if (id != AccessibleWrap::kNoID) {
+      doc->RemoveID(id);
+    }
+#else
+    doc->RemoveID(-reinterpret_cast<int32_t>(wrapper));
+#endif
   }
-#else
-  doc->RemoveID(-reinterpret_cast<int32_t>(wrapper));
-#endif
 
   wrapper->Shutdown();
   aProxy->SetWrapper(0);
   wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType)
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -314,17 +314,17 @@ pref("media.preload.auto", 2);    // pre
 pref("media.cache_size", 4096);    // 4MB media cache
 // Try to save battery by not resuming reading from a connection until we fall
 // below 10s of buffered data.
 pref("media.cache_resume_threshold", 10);
 pref("media.cache_readahead_limit", 30);
 
 #ifdef MOZ_FMP4
 // Enable/Disable Gonk Decoder Module
-pref("media.fragmented-mp4.gonk.enabled", true);
+pref("media.gonk.enabled", true);
 #endif
 
 //Encrypted media extensions.
 pref("media.eme.enabled", true);
 pref("media.eme.apiVisible", true);
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1830,18 +1830,18 @@ pref("ui.key.menuAccessKeyFocuses", true
 #endif
 
 // Encrypted media extensions.
 pref("media.eme.enabled", true);
 pref("media.eme.apiVisible", true);
 
 // If decoding-via-gmp is turned on for <video>, default to using
 // Adobe's GMP for decoding.
-pref("media.fragmented-mp4.gmp.aac", 2);
-pref("media.fragmented-mp4.gmp.h264", 2);
+pref("media.gmp.decoder.aac", 2);
+pref("media.gmp.decoder.h264", 2);
 
 // Whether we should run a test-pattern through EME GMPs before assuming they'll
 // decode H.264.
 pref("media.gmp.trial-create.enabled", true);
 
 #ifdef MOZ_ADOBE_EME
 pref("browser.eme.ui.enabled", true);
 pref("media.gmp-eme-adobe.enabled", true);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -157,16 +157,17 @@
     </panel>
 
     <!-- for select dropdowns. The menupopup is what shows the list of options,
          and the popuponly menulist makes things like the menuactive attributes
          work correctly on the menupopup. ContentSelectDropdown expects the
          popuponly menulist to be its immediate parent. -->
     <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
       <menupopup rolluponmousewheel="true"
+                 activateontab="true"
 #ifdef XP_WIN
                  consumeoutsideclicks="false" ignorekeys="handled"
 #endif
         />
     </menulist>
 
     <!-- for invalid form error message -->
     <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
--- a/browser/base/content/test/general/browser_audioTabIcon.js
+++ b/browser/base/content/test/general/browser_audioTabIcon.js
@@ -93,20 +93,19 @@ function* test_mute_tab(tab, icon, expec
   EventUtils.synthesizeMouseAtCenter(icon, {button: 0});
   leave_icon(icon);
 
   is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab");
 
   return mutedPromise;
 }
 
-function get_tab_attributes(tab) {
+function get_tab_state(tab) {
   const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
-  let {attributes} = JSON.parse(ss.getTabState(tab));
-  return attributes;
+  return JSON.parse(ss.getTabState(tab));
 }
 
 function* test_muting_using_menu(tab, expectMuted) {
   // Show the popup menu
   let contextMenu = document.getElementById("tabContextMenu");
   let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
   EventUtils.synthesizeMouseAtCenter(tab, {type: "contextmenu", button: 2});
   yield popupShownPromise;
@@ -142,27 +141,27 @@ function* test_playing_icon_on_tab(tab, 
   let icon = document.getAnonymousElementByAttribute(tab, "anonid",
                                                      isPinned ? "overlay-icon" : "soundplaying-icon");
   let isActiveTab = tab === gBrowser.selectedTab;
 
   yield play(tab);
 
   yield test_tooltip(icon, "Mute tab", isActiveTab);
 
-  ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted");
+  ok(!("muted" in get_tab_state(tab)), "No muted attribute should be persisted");
 
   yield test_mute_tab(tab, icon, true);
 
-  ok("muted" in get_tab_attributes(tab), "Muted attribute should be persisted");
+  ok("muted" in get_tab_state(tab), "Muted attribute should be persisted");
 
   yield test_tooltip(icon, "Unmute tab", isActiveTab);
 
   yield test_mute_tab(tab, icon, false);
 
-  ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted");
+  ok(!("muted" in get_tab_state(tab)), "No muted attribute should be persisted");
 
   yield test_tooltip(icon, "Mute tab", isActiveTab);
 
   yield test_mute_tab(tab, icon, true);
 
   yield pause(tab);
 
   ok(tab.hasAttribute("muted") &&
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2674,16 +2674,20 @@ var SessionStoreInternal = {
 
       if (winData.tabs[t].hidden) {
         tabbrowser.hideTab(tabs[t]);
       }
       else {
         tabbrowser.showTab(tabs[t]);
         numVisibleTabs++;
       }
+
+      if (!!winData.tabs[t].muted != tabs[t].linkedBrowser.audioMuted) {
+        tabs[t].toggleMuteAudio();
+      }
     }
 
     if (!overwriteTabs && firstWindow) {
       // Move the originally open tabs to the end
       let endPosition = tabbrowser.tabs.length - 1;
       for (let i = 0; i < initialTabs.length; i++) {
         tabbrowser.moveTabTo(initialTabs[i], endPosition);
       }
@@ -2933,16 +2937,20 @@ var SessionStoreInternal = {
     }
 
     if (tabData.hidden) {
       tabbrowser.hideTab(tab);
     } else {
       tabbrowser.showTab(tab);
     }
 
+    if (!!tabData.muted != browser.audioMuted) {
+      tab.toggleMuteAudio();
+    }
+
     if (tabData.lastAccessed) {
       tab.lastAccessed = tabData.lastAccessed;
     }
 
     if ("attributes" in tabData) {
       // Ensure that we persist tab attributes restored from previous sessions.
       Object.keys(tabData.attributes).forEach(a => TabAttributes.persist(a));
     }
--- a/browser/components/sessionstore/TabAttributes.jsm
+++ b/browser/components/sessionstore/TabAttributes.jsm
@@ -19,23 +19,25 @@ this.TabAttributes = Object.freeze({
   },
 
   set: function (tab, data = {}) {
     TabAttributesInternal.set(tab, data);
   }
 });
 
 var TabAttributesInternal = {
-  _attrs: new Set(["muted"]),
+  _attrs: new Set(),
 
   // We never want to directly read or write those attributes.
   // 'image' should not be accessed directly but handled by using the
   //         gBrowser.getIcon()/setIcon() methods.
+  // 'muted' should not be accessed directly but handled by using the
+  //         tab.linkedBrowser.audioMuted/toggleMuteAudio methods.
   // 'pending' is used internal by sessionstore and managed accordingly.
-  _skipAttrs: new Set(["image", "pending"]),
+  _skipAttrs: new Set(["image", "muted", "pending"]),
 
   persist: function (name) {
     if (this._attrs.has(name) || this._skipAttrs.has(name)) {
       return false;
     }
 
     this._attrs.add(name);
     return true;
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -154,16 +154,20 @@ var TabStateInternal = {
     let tabData = {entries: [], lastAccessed: tab.lastAccessed };
     let browser = tab.linkedBrowser;
 
     if (tab.pinned)
       tabData.pinned = true;
     else
       delete tabData.pinned;
     tabData.hidden = tab.hidden;
+    if (browser.audioMuted)
+      tabData.muted = true;
+    else
+      delete tabData.muted;
 
     // Save tab attributes.
     tabData.attributes = TabAttributes.get(tab);
 
     if (tab.__SS_extdata)
       tabData.extData = tab.__SS_extdata;
     else if (tabData.extData)
       delete tabData.extData;
--- a/browser/components/sessionstore/test/browser_attributes.js
+++ b/browser/components/sessionstore/test/browser_attributes.js
@@ -1,34 +1,40 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * This test makes sure that we correctly preserve tab attributes when storing
  * and restoring tabs. It also ensures that we skip special attributes like
- * 'image' and 'pending' that need to be handled differently or internally.
+ * 'image', 'muted' and 'pending' that need to be handled differently or internally.
  */
 
 const PREF = "browser.sessionstore.restore_on_demand";
 
 add_task(function* test() {
   Services.prefs.setBoolPref(PREF, true)
   registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
 
   // Add a new tab with a nice icon.
   let tab = gBrowser.addTab("about:robots");
   yield promiseBrowserLoaded(tab.linkedBrowser);
 
   // Check that the tab has an 'image' attribute.
   ok(tab.hasAttribute("image"), "tab.image exists");
 
-  // Make sure we do not persist 'image' attributes.
+  tab.toggleMuteAudio();
+  // Check that the tab has a 'muted' attribute.
+  ok(tab.hasAttribute("muted"), "tab.muted exists");
+
+  // Make sure we do not persist 'image' or 'muted' attributes.
   ss.persistTabAttribute("image");
+  ss.persistTabAttribute("muted");
   let {attributes} = JSON.parse(ss.getTabState(tab));
   ok(!("image" in attributes), "'image' attribute not saved");
+  ok(!("muted" in attributes), "'muted' attribute not saved");
   ok(!("custom" in attributes), "'custom' attribute not saved");
 
   // Test persisting a custom attribute.
   tab.setAttribute("custom", "foobar");
   ss.persistTabAttribute("custom");
 
   ({attributes} = JSON.parse(ss.getTabState(tab)));
   is(attributes.custom, "foobar", "'custom' attribute is correct");
--- a/browser/config/mozconfigs/macosx-universal/l10n-mozconfig
+++ b/browser/config/mozconfigs/macosx-universal/l10n-mozconfig
@@ -1,12 +1,12 @@
 . "$topsrcdir/browser/config/mozconfigs/common"
 . "$topsrcdir/build/macosx/mozconfig.common"
 
-ac_add_options --with-l10n-base=../../../l10n
+ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
 
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
--- a/browser/extensions/pdfjs/test/browser.ini
+++ b/browser/extensions/pdfjs/test/browser.ini
@@ -1,9 +1,12 @@
 [DEFAULT]
-skip-if = e10s # Bug 1159385
 support-files = file_pdfjs_test.pdf
 
 [browser_pdfjs_main.js]
+skip-if = e10s # Bug 1159385
 [browser_pdfjs_navigation.js]
+skip-if = e10s # Bug 1159385
 [browser_pdfjs_savedialog.js]
 [browser_pdfjs_views.js]
+skip-if = e10s # Bug 1159385
 [browser_pdfjs_zoom.js]
+skip-if = e10s # Bug 1159385
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -219,16 +219,43 @@ bool isIgnoredPathForImplicitConversion(
   }
   return false;
 }
 
 bool isInterestingDeclForImplicitConversion(const Decl *decl) {
   return !isInIgnoredNamespaceForImplicitConversion(decl) &&
          !isIgnoredPathForImplicitConversion(decl);
 }
+
+bool isIgnoredExprForMustUse(const Expr *E) {
+  if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+    switch (OpCall->getOperator()) {
+    case OO_Equal:
+    case OO_PlusEqual:
+    case OO_MinusEqual:
+    case OO_StarEqual:
+    case OO_SlashEqual:
+    case OO_PercentEqual:
+    case OO_CaretEqual:
+    case OO_AmpEqual:
+    case OO_PipeEqual:
+    case OO_LessLessEqual:
+    case OO_GreaterGreaterEqual:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
+    return Op->isAssignmentOp();
+  }
+
+  return false;
+}
 }
 
 class CustomTypeAnnotation {
   enum ReasonKind {
     RK_None,
     RK_Direct,
     RK_ArrayElement,
     RK_BaseClass,
@@ -315,17 +342,17 @@ public:
 
     return false;
   }
 
   void HandleUnusedExprResult(const Stmt *stmt) {
     const Expr *E = dyn_cast_or_null<Expr>(stmt);
     if (E) {
       QualType T = E->getType();
-      if (MustUse.hasEffectiveAnnotation(T)) {
+      if (MustUse.hasEffectiveAnnotation(T) && !isIgnoredExprForMustUse(E)) {
         unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
             DiagnosticIDs::Error, "Unused value of must-use type %0");
 
         Diag.Report(E->getLocStart(), errorID) << T;
         MustUse.dumpAnnotationReason(Diag, T, E->getLocStart());
       }
     }
   }
--- a/build/clang-plugin/tests/TestMustUse.cpp
+++ b/build/clang-plugin/tests/TestMustUse.cpp
@@ -15,142 +15,163 @@ void use(MustUse*);
 void use(MustUse&);
 void use(MustUse&&);
 void use(MayUse*);
 void use(MayUse&);
 void use(MayUse&&);
 void use(bool);
 
 void foo() {
+  MustUse u;
+
   producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   producesMustUsePointer();
   producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   producesMayUse();
   producesMayUsePointer();
   producesMayUseRef();
+  u = producesMustUse();
   {
     producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMustUsePointer();
     producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMayUse();
     producesMayUsePointer();
     producesMayUseRef();
+    u = producesMustUse();
   }
   if (true) {
     producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMustUsePointer();
     producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMayUse();
     producesMayUsePointer();
     producesMayUseRef();
+    u = producesMustUse();
   } else {
     producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMustUsePointer();
     producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMayUse();
     producesMayUsePointer();
     producesMayUseRef();
+    u = producesMustUse();
   }
 
   if(true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   else producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   if(true) producesMustUsePointer();
   else producesMustUsePointer();
   if(true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   else producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   if(true) producesMayUse();
   else producesMayUse();
   if(true) producesMayUsePointer();
   else producesMayUsePointer();
   if(true) producesMayUseRef();
   else producesMayUseRef();
+  if(true) u = producesMustUse();
+  else u = producesMustUse();
 
   while (true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   while (true) producesMustUsePointer();
   while (true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   while (true) producesMayUse();
   while (true) producesMayUsePointer();
   while (true) producesMayUseRef();
+  while (true) u = producesMustUse();
 
   do producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   while (true);
   do producesMustUsePointer();
   while (true);
   do producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   while (true);
   do producesMayUse();
   while (true);
   do producesMayUsePointer();
   while (true);
   do producesMayUseRef();
   while (true);
+  do u = producesMustUse();
+  while (true);
 
   for (;;) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   for (;;) producesMustUsePointer();
   for (;;) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   for (;;) producesMayUse();
   for (;;) producesMayUsePointer();
   for (;;) producesMayUseRef();
+  for (;;) u = producesMustUse();
 
   for (producesMustUse();;); // expected-error {{Unused value of must-use type 'MustUse'}}
   for (producesMustUsePointer();;);
   for (producesMustUseRef();;); // expected-error {{Unused value of must-use type 'MustUse'}}
   for (producesMayUse();;);
   for (producesMayUsePointer();;);
   for (producesMayUseRef();;);
+  for (u = producesMustUse();;);
 
   for (;;producesMustUse()); // expected-error {{Unused value of must-use type 'MustUse'}}
   for (;;producesMustUsePointer());
   for (;;producesMustUseRef()); // expected-error {{Unused value of must-use type 'MustUse'}}
   for (;;producesMayUse());
   for (;;producesMayUsePointer());
   for (;;producesMayUseRef());
+  for (;;u = producesMustUse());
 
   use((producesMustUse(), false)); // expected-error {{Unused value of must-use type 'MustUse'}}
   use((producesMustUsePointer(), false));
   use((producesMustUseRef(), false)); // expected-error {{Unused value of must-use type 'MustUse'}}
   use((producesMayUse(), false));
   use((producesMayUsePointer(), false));
   use((producesMayUseRef(), false));
+  use((u = producesMustUse(), false));
 
   switch (1) {
   case 1:
     producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMustUsePointer();
     producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMayUse();
     producesMayUsePointer();
     producesMayUseRef();
+    u = producesMustUse();
   case 2:
     producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
   case 3:
     producesMustUsePointer();
   case 4:
     producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
   case 5:
     producesMayUse();
   case 6:
     producesMayUsePointer();
   case 7:
     producesMayUseRef();
+  case 8:
+    u = producesMustUse();
   default:
     producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMustUsePointer();
     producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
     producesMayUse();
     producesMayUsePointer();
     producesMayUseRef();
+    u = producesMustUse();
   }
 
   use(producesMustUse());
   use(producesMustUsePointer());
   use(producesMustUseRef());
   use(producesMayUse());
   use(producesMayUsePointer());
   use(producesMayUseRef());
+  use(u = producesMustUse());
 
   MustUse a = producesMustUse();
   MustUse *b = producesMustUsePointer();
   MustUse &c = producesMustUseRef();
   MayUse d = producesMayUse();
   MayUse *e = producesMayUsePointer();
   MayUse &f = producesMayUseRef();
+  MustUse g = u = producesMustUse();
 }
--- a/build/sanitizers/lsan_suppressions.txt
+++ b/build/sanitizers/lsan_suppressions.txt
@@ -78,22 +78,16 @@ leak:nsPSPrinterList::GetPrinterList
 leak:_PR_Getfd
 
 # Bug 1028483 - The XML parser sometimes leaks an object. bc3
 leak:processInternalEntity
 
 # Bug 1187421 - With e10s, NSS does not always free the error stack. m1.
 leak:nss_ClearErrorStack
 
-# Bug 1090570 - IPC Transport lifetime is not always managed correctly.
-leak:mozilla::ipc::OpenDescriptor
-leak:IPC::Channel::ChannelImpl::OutputQueuePush
-leak:IPC::Channel::Channel
-leak:base::MessagePumpLibevent::WatchFileDescriptor
-
 # Bug 1122045 - Leaks in MessageLoop::MessageLoop()
 leak:MessageLoop::MessageLoop
 # This may not actually be related to MessageLoop.
 leak:base::WaitableEvent::TimedWait
 leak:MessageLoop::PostTask_Helper
 
 # Bug 1189430 - DNS leaks in mochitest-chrome.
 leak:nsDNSService::AsyncResolveExtended
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -62,16 +62,17 @@ GK_ATOM(above, "above")
 GK_ATOM(acceltext, "acceltext")
 GK_ATOM(accept, "accept")
 GK_ATOM(acceptcharset, "accept-charset")
 GK_ATOM(accesskey, "accesskey")
 GK_ATOM(acronym, "acronym")
 GK_ATOM(action, "action")
 GK_ATOM(active, "active")
 GK_ATOM(activetitlebarcolor, "activetitlebarcolor")
+GK_ATOM(activateontab, "activateontab")
 GK_ATOM(actuate, "actuate")
 GK_ATOM(address, "address")
 GK_ATOM(after, "after")
 GK_ATOM(after_end, "after_end")
 GK_ATOM(after_start, "after_start")
 GK_ATOM(align, "align")
 GK_ATOM(alink, "alink")
 GK_ATOM(all, "all")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8795,17 +8795,17 @@ nsGlobalWindow::CanClose()
     if (NS_SUCCEEDED(rv) && !canClose)
       return false;
   }
 
   return true;
 }
 
 void
-nsGlobalWindow::CloseOuter()
+nsGlobalWindow::CloseOuter(bool aTrustedCaller)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   if (!mDocShell || IsInModalState() ||
       (IsFrame() && !mDocShell->GetIsBrowserOrApp())) {
     // window.close() is called on a frame in a frameset, on a window
     // that's already closed, or on a window for which there's
     // currently a modal dialog open. Ignore such calls.
@@ -8827,17 +8827,17 @@ nsGlobalWindow::CloseOuter()
   }
 
   // Don't allow scripts from content to close non-app or non-neterror
   // windows that were not opened by script.
   nsAutoString url;
   mDoc->GetURL(url);
   if (!mDocShell->GetIsApp() &&
       !StringBeginsWith(url, NS_LITERAL_STRING("about:neterror")) &&
-      !mHadOriginalOpener && !nsContentUtils::IsCallerChrome()) {
+      !mHadOriginalOpener && !aTrustedCaller) {
     bool allowClose = mAllowScriptsToClose ||
       Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
     if (!allowClose) {
       // We're blocking the close operation
       // report localized error msg in JS console
       nsContentUtils::ReportToConsole(
           nsIScriptError::warningFlag,
           NS_LITERAL_CSTRING("DOM Window"), mDoc,  // Better name for the category?
@@ -8871,28 +8871,25 @@ nsGlobalWindow::CloseOuter()
   }
 
   FinalClose();
 }
 
 void
 nsGlobalWindow::Close(ErrorResult& aError)
 {
-  FORWARD_TO_OUTER_OR_THROW(CloseOuter, (), aError, );
+  FORWARD_TO_OUTER_OR_THROW(CloseOuter, (nsContentUtils::IsCallerChrome()), aError, );
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::Close()
 {
-  FORWARD_TO_INNER(Close, (), NS_ERROR_UNEXPECTED);
-
-  ErrorResult rv;
-  Close(rv);
-
-  return rv.StealNSResult();
+  FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
+  CloseOuter(/* aTrustedCaller = */ true);
+  return NS_OK;
 }
 
 void
 nsGlobalWindow::ForceClose()
 {
   MOZ_ASSERT(IsOuterWindow());
 
   if (IsFrame() || !mDocShell) {
@@ -10001,21 +9998,16 @@ nsGlobalWindow::AddEventListener(const n
                                  bool aUseCapture, bool aWantsUntrusted,
                                  uint8_t aOptionalArgc)
 {
   NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
                "Won't check if this is chrome, you want to set "
                "aWantsUntrusted to false or make the aWantsUntrusted "
                "explicit by making optional_argc non-zero.");
 
-  if (IsOuterWindow() && mInnerWindow &&
-      !nsContentUtils::CanCallerAccess(mInnerWindow)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
   if (!aWantsUntrusted &&
       (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
     aWantsUntrusted = true;
   }
 
   EventListenerManager* manager = GetOrCreateListenerManager();
   NS_ENSURE_STATE(manager);
   manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -860,17 +860,17 @@ public:
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatusOuter(nsAString& aStatus);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
   void SetStatusOuter(const nsAString& aStatus);
   void SetStatus(const nsAString& aStatus, mozilla::ErrorResult& aError);
-  void CloseOuter();
+  void CloseOuter(bool aTrustedCaller);
   void Close(mozilla::ErrorResult& aError);
   bool GetClosedOuter();
   bool GetClosed(mozilla::ErrorResult& aError);
   void StopOuter(mozilla::ErrorResult& aError);
   void Stop(mozilla::ErrorResult& aError);
   void FocusOuter(mozilla::ErrorResult& aError);
   void Focus(mozilla::ErrorResult& aError);
   void BlurOuter();
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -274,19 +274,16 @@ nsLocation::SetURI(nsIURI* aURI, bool aR
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocation::GetHash(nsAString& aHash)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aHash.SetLength(0);
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetURI(getter_AddRefs(uri));
   if (NS_FAILED(rv) || !uri) {
     return rv;
   }
 
@@ -357,19 +354,16 @@ nsLocation::SetHash(const nsAString& aHa
   }
 
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::GetHost(nsAString& aHost)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aHost.Truncate();
 
   nsCOMPtr<nsIURI> uri;
   nsresult result;
 
   result = GetURI(getter_AddRefs(uri), true);
 
   if (uri) {
@@ -383,19 +377,16 @@ nsLocation::GetHost(nsAString& aHost)
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocation::SetHost(const nsAString& aHost)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
   rv = uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -403,36 +394,30 @@ nsLocation::SetHost(const nsAString& aHo
   }
 
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::GetHostname(nsAString& aHostname)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aHostname.Truncate();
 
   nsCOMPtr<nsIURI> uri;
   GetURI(getter_AddRefs(uri), true);
   if (uri) {
     nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocation::SetHostname(const nsAString& aHostname)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
   rv = uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -440,19 +425,16 @@ nsLocation::SetHostname(const nsAString&
   }
 
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::GetHref(nsAString& aHref)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aHref.Truncate();
 
   nsCOMPtr<nsIURI> uri;
   nsresult result;
 
   result = GetURI(getter_AddRefs(uri));
 
   if (uri) {
@@ -558,19 +540,16 @@ nsLocation::SetHrefWithBase(const nsAStr
   }
 
   return result;
 }
 
 NS_IMETHODIMP
 nsLocation::GetOrigin(nsAString& aOrigin)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aOrigin.Truncate();
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetURI(getter_AddRefs(uri), true);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(uri, NS_OK);
 
   nsAutoString origin;
@@ -579,19 +558,16 @@ nsLocation::GetOrigin(nsAString& aOrigin
 
   aOrigin = origin;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocation::GetPathname(nsAString& aPathname)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aPathname.Truncate();
 
   nsCOMPtr<nsIURI> uri;
   nsresult result = NS_OK;
 
   result = GetURI(getter_AddRefs(uri));
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
@@ -606,19 +582,16 @@ nsLocation::GetPathname(nsAString& aPath
   }
 
   return result;
 }
 
 NS_IMETHODIMP
 nsLocation::SetPathname(const nsAString& aPathname)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
   rv = uri->SetPath(NS_ConvertUTF16toUTF8(aPathname));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -626,19 +599,16 @@ nsLocation::SetPathname(const nsAString&
   }
 
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::GetPort(nsAString& aPort)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aPort.SetLength(0);
 
   nsCOMPtr<nsIURI> uri;
   nsresult result = NS_OK;
 
   result = GetURI(getter_AddRefs(uri), true);
 
   if (uri) {
@@ -656,19 +626,16 @@ nsLocation::GetPort(nsAString& aPort)
   }
 
   return result;
 }
 
 NS_IMETHODIMP
 nsLocation::SetPort(const nsAString& aPort)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
   // perhaps use nsReadingIterators at some point?
   NS_ConvertUTF16toUTF8 portStr(aPort);
@@ -690,19 +657,16 @@ nsLocation::SetPort(const nsAString& aPo
   }
 
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::GetProtocol(nsAString& aProtocol)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aProtocol.SetLength(0);
 
   nsCOMPtr<nsIURI> uri;
   nsresult result = NS_OK;
 
   result = GetURI(getter_AddRefs(uri));
 
   if (uri) {
@@ -717,19 +681,16 @@ nsLocation::GetProtocol(nsAString& aProt
   }
 
   return result;
 }
 
 NS_IMETHODIMP
 nsLocation::SetProtocol(const nsAString& aProtocol)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
   rv = uri->SetScheme(NS_ConvertUTF16toUTF8(aProtocol));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -737,40 +698,34 @@ nsLocation::SetProtocol(const nsAString&
   }
 
   return SetURI(uri);
 }
 
 void
 nsLocation::GetUsername(nsAString& aUsername, ErrorResult& aError)
 {
-  if (!CallerSubsumes()) {
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
+  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
 
   aUsername.Truncate();
   nsCOMPtr<nsIURI> uri;
   nsresult result = GetURI(getter_AddRefs(uri));
   if (uri) {
     nsAutoCString username;
     result = uri->GetUsername(username);
     if (NS_SUCCEEDED(result)) {
       CopyUTF8toUTF16(username, aUsername);
     }
   }
 }
 
 void
 nsLocation::SetUsername(const nsAString& aUsername, ErrorResult& aError)
 {
-  if (!CallerSubsumes()) {
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
+  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
@@ -785,40 +740,34 @@ nsLocation::SetUsername(const nsAString&
   }
 
   rv = SetURI(uri);
 }
 
 void
 nsLocation::GetPassword(nsAString& aPassword, ErrorResult& aError)
 {
-  if (!CallerSubsumes()) {
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
+  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
 
   aPassword.Truncate();
   nsCOMPtr<nsIURI> uri;
   nsresult result = GetURI(getter_AddRefs(uri));
   if (uri) {
     nsAutoCString password;
     result = uri->GetPassword(password);
     if (NS_SUCCEEDED(result)) {
       CopyUTF8toUTF16(password, aPassword);
     }
   }
 }
 
 void
 nsLocation::SetPassword(const nsAString& aPassword, ErrorResult& aError)
 {
-  if (!CallerSubsumes()) {
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
+  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
@@ -833,19 +782,16 @@ nsLocation::SetPassword(const nsAString&
   }
 
   rv = SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::GetSearch(nsAString& aSearch)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   aSearch.SetLength(0);
 
   nsCOMPtr<nsIURI> uri;
   nsresult result = NS_OK;
 
   result = GetURI(getter_AddRefs(uri));
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
@@ -873,19 +819,16 @@ nsLocation::SetSearch(const nsAString& a
   }
 
   return NS_OK;
 }
 
 nsresult
 nsLocation::SetSearchInternal(const nsAString& aSearch)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !url)) {
     return rv;
   }
 
@@ -895,19 +838,16 @@ nsLocation::SetSearchInternal(const nsAS
   }
 
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 nsLocation::Reload(bool aForceget)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsresult rv;
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
   nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
   nsCOMPtr<nsPIDOMWindow> window = docShell ? docShell->GetWindow() : nullptr;
 
   if (window && window->IsHandlingResizeEvent()) {
     // location.reload() was called on a window that is handling a
     // resize event. Sites do this since Netscape 4.x needed it, but
@@ -967,19 +907,16 @@ nsLocation::Replace(const nsAString& aUr
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SetHrefWithBase(aUrl, oldUri, true);
 }
 
 NS_IMETHODIMP
 nsLocation::Assign(const nsAString& aUrl)
 {
-  if (!CallerSubsumes())
-    return NS_ERROR_DOM_SECURITY_ERR;
-
   nsAutoString oldHref;
   nsresult result = NS_OK;
 
   result = GetHref(oldHref);
 
   if (NS_SUCCEEDED(result)) {
     nsCOMPtr<nsIURI> oldUri;
 
@@ -991,17 +928,16 @@ nsLocation::Assign(const nsAString& aUrl
   }
 
   return result;
 }
 
 NS_IMETHODIMP
 nsLocation::ToString(nsAString& aReturn)
 {
-  // NB: GetHref checks CallerSubsumes().
   return GetHref(aReturn);
 }
 
 NS_IMETHODIMP
 nsLocation::ValueOf(nsIDOMLocation** aReturn)
 {
   nsCOMPtr<nsIDOMLocation> loc(this);
   loc.forget(aReturn);
--- a/dom/base/nsLocation.h
+++ b/dom/base/nsLocation.h
@@ -37,104 +37,132 @@ public:
                                                          nsIDOMLocation)
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
+  #define THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME() { \
+    if (!CallerSubsumes()) { \
+      aError.Throw(NS_ERROR_DOM_SECURITY_ERR); \
+      return; \
+    } \
+  }
+
   // WebIDL API:
   void Assign(const nsAString& aUrl, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Assign(aUrl);
   }
+
   void Replace(const nsAString& aUrl, ErrorResult& aError)
   {
     aError = Replace(aUrl);
   }
+
   void Reload(bool aForceget, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Reload(aForceget);
   }
   void GetHref(nsAString& aHref, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHref(aHref);
   }
   void SetHref(const nsAString& aHref, ErrorResult& aError)
   {
     aError = SetHref(aHref);
   }
   void GetOrigin(nsAString& aOrigin, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetOrigin(aOrigin);
   }
   void GetProtocol(nsAString& aProtocol, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetProtocol(aProtocol);
   }
   void SetProtocol(const nsAString& aProtocol, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetProtocol(aProtocol);
   }
   void GetUsername(nsAString& aUsername, ErrorResult& aError);
   void SetUsername(const nsAString& aUsername, ErrorResult& aError);
   void GetPassword(nsAString& aPassword, ErrorResult& aError);
   void SetPassword(const nsAString& aPassword, ErrorResult& aError);
   void GetHost(nsAString& aHost, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHost(aHost);
   }
   void SetHost(const nsAString& aHost, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHost(aHost);
   }
   void GetHostname(nsAString& aHostname, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHostname(aHostname);
   }
   void SetHostname(const nsAString& aHostname, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHostname(aHostname);
   }
   void GetPort(nsAString& aPort, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPort(aPort);
   }
   void SetPort(const nsAString& aPort, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPort(aPort);
   }
   void GetPathname(nsAString& aPathname, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPathname(aPathname);
   }
   void SetPathname(const nsAString& aPathname, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPathname(aPathname);
   }
   void GetSearch(nsAString& aSeach, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetSearch(aSeach);
   }
   void SetSearch(const nsAString& aSeach, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetSearch(aSeach);
   }
 
   void GetHash(nsAString& aHash, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHash(aHash);
   }
   void SetHash(const nsAString& aHash, ErrorResult& aError)
   {
+    THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHash(aHash);
   }
   void Stringify(nsAString& aRetval, ErrorResult& aError)
   {
+    // GetHref checks CallerSubsumes.
     GetHref(aRetval, aError);
   }
   nsPIDOMWindow* GetParentObject() const
   {
     return mInnerWindow;
   }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -2699,17 +2699,18 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
       net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
 
     if (postDataStream) {
       // If no content type header was set by the client, we set it to
       // application/xml.
       nsAutoCString contentType;
       if (NS_FAILED(httpChannel->
                       GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
-                                       contentType))) {
+                                       contentType)) ||
+          contentType.IsEmpty()) {
         contentType = defaultContentType;
 
         if (!charset.IsEmpty()) {
           // If we are providing the default content type, then we also need to
           // provide a charset declaration.
           contentType.Append(NS_LITERAL_CSTRING(";charset="));
           contentType.Append(charset);
         }
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -175,14 +175,12 @@ WebGLContext::InitWebGL2()
     }
 
     mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs);
     mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings);
 
     mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
     mBoundTransformFeedback = mDefaultTransformFeedback;
 
-    mBypassShaderValidation = true;
-
     return true;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -94,17 +94,17 @@ ChooseValidatorCompileOptions(const ShBu
 ////////////////////////////////////////
 
 webgl::ShaderValidator*
 WebGLContext::CreateShaderValidator(GLenum shaderType) const
 {
     if (mBypassShaderValidation)
         return nullptr;
 
-    ShShaderSpec spec = SH_WEBGL_SPEC;
+    ShShaderSpec spec = IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC;
     ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT
                                                  : SH_GLSL_OUTPUT;
 
     ShBuiltInResources resources;
     memset(&resources, 0, sizeof(resources));
     ShInitBuiltInResources(&resources);
 
     resources.HashFunction = webgl::IdentifierHashFunc;
--- a/dom/canvas/test/webgl-mochitest/webgl-util.js
+++ b/dom/canvas/test/webgl-mochitest/webgl-util.js
@@ -57,16 +57,17 @@ WebGLUtil = (function() {
 
     return gl;
   }
 
   function withWebGL2(canvasId, callback, onFinished) {
     var prefArrArr = [
       ['webgl.force-enabled', true],
       ['webgl.disable-angle', true],
+      ['webgl.bypass-shader-validation', true],
       ['webgl.enable-prototype-webgl2', true],
     ];
     var prefEnv = {'set': prefArrArr};
     SpecialPowers.pushPrefEnv(prefEnv, function() {
       var canvas = document.getElementById(canvasId);
 
       var gl = null;
       try {
--- a/dom/crypto/test/test_WebCrypto_ECDH.html
+++ b/dom/crypto/test/test_WebCrypto_ECDH.html
@@ -368,17 +368,17 @@ TestArray.addTest(
 
     Promise.all([
       crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveKey"])
         .then(setPriv),
       crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveKey"])
         .then(setPub)
     ]).then(doDerive)
       .then(doSignAndVerify)
-      .then(complete(that), error(that));
+      .then(complete(that, x => x), error(that));
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "SPKI import/export of public ECDH keys (P-256)",
   function () {
     var that = this;
--- a/dom/crypto/test/test_WebCrypto_ECDSA.html
+++ b/dom/crypto/test/test_WebCrypto_ECDSA.html
@@ -50,25 +50,25 @@ TestArray.addTest(
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "ECDSA JWK import and verify a known-good signature",
   function() {
     var that = this;
-    var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+    var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
 
     function doVerify(x) {
       return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
     }
 
     crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
       .then(doVerify)
-      .then(complete(that), error(that))
+      .then(complete(that, x => x), error(that))
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "ECDSA key generation with public key export",
   function() {
     var that = this;
@@ -77,17 +77,17 @@ TestArray.addTest(
 
     crypto.subtle.generateKey(alg, false, ["sign", "verify"])
       .then(pair => Promise.all([
           crypto.subtle.sign(alg, pair.privateKey, msg),
           crypto.subtle.exportKey("spki", pair.publicKey)
             .then(spki => crypto.subtle.importKey("spki", spki, alg, false, ["verify"]))
       ]))
       .then(sigAndKey => crypto.subtle.verify(alg, sigAndKey[1], sigAndKey[0], msg))
-      .then(complete(that), error(that))
+      .then(complete(that, x => x), error(that))
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "ECDSA JWK import and reject a known-bad signature",
   function() {
     var that = this;
@@ -119,17 +119,17 @@ TestArray.addTest(
     }
     function doVerify(sig) {
       return crypto.subtle.verify(alg, pubKey, sig, tv.ecdsa_verify.data);
     }
 
     crypto.subtle.generateKey(alg, true, ["sign", "verify"])
       .then(doSign)
       .then(doVerify)
-      .then(complete(that), error(that))
+      .then(complete(that, x => x), error(that))
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Verify that ECDSA import fails with a known-bad public key",
   function() {
     var that = this;
@@ -169,17 +169,17 @@ TestArray.addTest(
     var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
 
     function doVerify(x) {
       return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
     }
 
     crypto.subtle.importKey("raw", tv.ecdsa_verify.raw, alg, true, ["verify"])
       .then(doVerify)
-      .then(complete(that), error(that))
+      .then(complete(that, x => x), error(that))
   }
 );
 
 /*]]>*/</script>
 </head>
 
 <body>
 
--- a/dom/crypto/test/test_WebCrypto_PBKDF2.html
+++ b/dom/crypto/test/test_WebCrypto_PBKDF2.html
@@ -114,17 +114,17 @@ TestArray.addTest(
         });
     }
 
     function fail(x) { console.log("failing"); error(that)(x); }
 
     crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
       .then( doDerive, fail )
       .then( doSignAndVerify, fail )
-      .then( complete(that), fail );
+      .then( complete(that, x => x), fail );
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Import raw PBKDF2 key and derive a new key using HMAC-SHA-1 with custom length",
   function() {
     var that = this;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -895,16 +895,42 @@ private:
     nsRefPtr<MemoryReportRequestChild> mActor;
     const nsCString mProcess;
 };
 NS_IMPL_ISUPPORTS(
   MemoryReportCallback
 , nsIMemoryReporterCallback
 )
 
+class MemoryReportFinishedCallback final : public nsIFinishReportingCallback
+{
+public:
+    NS_DECL_ISUPPORTS
+
+    explicit MemoryReportFinishedCallback(MemoryReportRequestChild* aActor)
+    : mActor(aActor)
+    {
+    }
+
+    NS_IMETHOD Callback(nsISupports* aUnused) override
+    {
+        bool sent = PMemoryReportRequestChild::Send__delete__(mActor);
+        return sent ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+private:
+    ~MemoryReportFinishedCallback() {}
+
+    nsRefPtr<MemoryReportRequestChild> mActor;
+};
+NS_IMPL_ISUPPORTS(
+  MemoryReportFinishedCallback
+, nsIFinishReportingCallback
+)
+
 bool
 ContentChild::RecvPMemoryReportRequestConstructor(
     PMemoryReportRequestChild* aChild,
     const uint32_t& aGeneration,
     const bool& aAnonymize,
     const bool& aMinimizeMemoryUsage,
     const MaybeFileDesc& aDMDFile)
 {
@@ -931,21 +957,22 @@ NS_IMETHODIMP MemoryReportRequestChild::
     nsCString process;
     child->GetProcessName(process);
     child->AppendProcessId(process);
 
     // Run the reporters.  The callback will turn each measurement into a
     // MemoryReport.
     nsRefPtr<MemoryReportCallback> cb =
         new MemoryReportCallback(this, process);
-    mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
-                                          FileDescriptorToFILE(mDMDFile, "wb"));
-
-    bool sent = Send__delete__(this);
-    return sent ? NS_OK : NS_ERROR_FAILURE;
+    nsRefPtr<MemoryReportFinishedCallback> finished =
+        new MemoryReportFinishedCallback(this);
+
+    return mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
+                                                 FileDescriptorToFILE(mDMDFile, "wb"),
+                                                 finished, nullptr);
 }
 
 bool
 ContentChild::RecvDataStoreNotify(const uint32_t& aAppId,
                                   const nsString& aName,
                                   const nsString& aManifestURL)
 {
   nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -19,16 +19,17 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
 
 #include <set>
 
+#include "mozilla/a11y/PDocAccessible.h"
 #include "AppProcessChecker.h"
 #include "AudioChannelService.h"
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
 #include "GMPServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
@@ -5471,8 +5472,24 @@ NS_IMPL_ISUPPORTS(ParentIdleListener, ns
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
     mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
                                                        nsDependentCString(aTopic),
                                                        nsDependentString(aData));
     return NS_OK;
 }
+
+bool
+ContentParent::HandleWindowsMessages(const Message& aMsg) const
+{
+  MOZ_ASSERT(aMsg.is_sync());
+
+  // a11y messages can be triggered by windows messages, which means if we
+  // allow handling windows messages while we wait for the response to a sync
+  // a11y message we can reenter the ipc message sending code.
+  if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() &&
+      a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) {
+    return false;
+  }
+
+  return true;
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -422,16 +422,18 @@ public:
 
     virtual PContentPermissionRequestParent*
     AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                          const IPC::Principal& aPrincipal,
                                          const TabId& aTabId) override;
     virtual bool
     DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
+    virtual bool HandleWindowsMessages(const Message& aMsg) const override;
+
     bool HasGamepadListener() const { return mHasGamepadListener; }
 
     void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; }
     void ForkNewProcess(bool aBlocking);
 
 protected:
     void OnChannelConnected(int32_t pid) override;
     virtual void ActorDestroy(ActorDestroyReason why) override;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -102,16 +102,18 @@ MediaLoadInvalidURI=Invalid URI. Load of
 # LOCALIZATION NOTE: %1$S is the media resource's format/codec type (basically equivalent to the file type, e.g. MP4,AVI,WMV,MOV etc), %2$S is the URL of the media resource which failed to load.
 MediaLoadUnsupportedTypeAttribute=Specified "type" attribute of "%1$S" is not supported. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %1$S is the "media" attribute value of the <source> element. It is a media query. %2$S is the URL of the media resource which failed to load.
 MediaLoadSourceMediaNotMatched=Specified "media" attribute of "%1$S" does not match the environment. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %1$S is the MIME type HTTP header being sent by the web server, %2$S is the URL of the media resource which failed to load.
 MediaLoadUnsupportedMimeType=HTTP "Content-Type" of "%1$S" is not supported. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %S is the URL of the media resource which failed to load because of error in decoding.
 MediaLoadDecodeError=Media resource %S could not be decoded.
+# LOCALIZATION NOTE: %S is the ID of the MediaStreamTrack passed to MediaStream.addTrack(). Do not translate "MediaStreamTrack" and "AudioChannel".
+MediaStreamAddTrackDifferentAudioChannel=MediaStreamTrack %S could not be added since it belongs to a different AudioChannel.
 # LOCALIZATION NOTE: Do not translate "MediaStream", "stop()" and "MediaStreamTrack"
 MediaStreamStopDeprecatedWarning=MediaStream.stop() is deprecated and will soon be removed. Use MediaStreamTrack.stop() instead.
 # LOCALIZATION NOTE: Do not translate "DOMException", "code" and "name"
 DOMExceptionCodeWarning=Use of DOMException's code attribute is deprecated. Use name instead.
 # LOCALIZATION NOTE: Do not translate "__exposedProps__"
 NoExposedPropsWarning=Exposing chrome JS objects to content without __exposedProps__ is insecure and deprecated. See https://developer.mozilla.org/en/XPConnect_wrappers for more information.
 # LOCALIZATION NOTE: Do not translate "Mutation Event" and "MutationObserver"
 MutationEventWarning=Use of Mutation Events is deprecated. Use MutationObserver instead.
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -416,16 +416,82 @@ DOMMediaStream::Destroy()
 }
 
 JSObject*
 DOMMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::MediaStreamBinding::Wrap(aCx, this, aGivenProto);
 }
 
+/* static */ already_AddRefed<DOMMediaStream>
+DOMMediaStream::Constructor(const GlobalObject& aGlobal,
+                            ErrorResult& aRv)
+{
+  Sequence<OwningNonNull<MediaStreamTrack>> emptyTrackSeq;
+  return Constructor(aGlobal, emptyTrackSeq, aRv);
+}
+
+/* static */ already_AddRefed<DOMMediaStream>
+DOMMediaStream::Constructor(const GlobalObject& aGlobal,
+                            const DOMMediaStream& aStream,
+                            ErrorResult& aRv)
+{
+  nsTArray<nsRefPtr<MediaStreamTrack>> tracks;
+  aStream.GetTracks(tracks);
+
+  Sequence<OwningNonNull<MediaStreamTrack>> nonNullTrackSeq;
+  if (!nonNullTrackSeq.SetLength(tracks.Length(), fallible)) {
+    MOZ_ASSERT(false);
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
+
+  for (size_t i = 0; i < tracks.Length(); ++i) {
+    nonNullTrackSeq[i] = tracks[i];
+  }
+
+  return Constructor(aGlobal, nonNullTrackSeq, aRv);
+}
+
+/* static */ already_AddRefed<DOMMediaStream>
+DOMMediaStream::Constructor(const GlobalObject& aGlobal,
+                            const Sequence<OwningNonNull<MediaStreamTrack>>& aTracks,
+                            ErrorResult& aRv)
+{
+  nsCOMPtr<nsIDOMWindow> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!ownerWindow) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr<DOMMediaStream> newStream = new DOMMediaStream();
+  newStream->mWindow = ownerWindow;
+
+  for (MediaStreamTrack& track : aTracks) {
+    if (!newStream->GetPlaybackStream()) {
+      MOZ_RELEASE_ASSERT(track.GetStream());
+      MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream());
+      MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream()->Graph());
+      MediaStreamGraph* graph = track.GetStream()->GetPlaybackStream()->Graph();
+      newStream->InitPlaybackStreamCommon(graph);
+    }
+    newStream->AddTrack(track);
+  }
+
+  if (!newStream->GetPlaybackStream()) {
+    MOZ_ASSERT(aTracks.IsEmpty());
+    MediaStreamGraph* graph =
+      MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER,
+                                    AudioChannel::Normal);
+    newStream->InitPlaybackStreamCommon(graph);
+  }
+
+  return newStream.forget();
+}
+
 double
 DOMMediaStream::CurrentTime()
 {
   if (!mPlaybackStream) {
     return 0.0;
   }
   return mPlaybackStream->
     StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime);
@@ -433,57 +499,80 @@ DOMMediaStream::CurrentTime()
 
 void
 DOMMediaStream::GetId(nsAString& aID) const
 {
   aID = mID;
 }
 
 void
-DOMMediaStream::GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks)
+DOMMediaStream::GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks) const
 {
   for (const nsRefPtr<TrackPort>& info : mTracks) {
     AudioStreamTrack* t = info->GetTrack()->AsAudioStreamTrack();
     if (t) {
       aTracks.AppendElement(t);
     }
   }
 }
 
 void
-DOMMediaStream::GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks)
+DOMMediaStream::GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks) const
 {
   for (const nsRefPtr<TrackPort>& info : mTracks) {
     VideoStreamTrack* t = info->GetTrack()->AsVideoStreamTrack();
     if (t) {
       aTracks.AppendElement(t);
     }
   }
 }
 
 void
-DOMMediaStream::GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks)
+DOMMediaStream::GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks) const
 {
   for (const nsRefPtr<TrackPort>& info : mTracks) {
     aTracks.AppendElement(info->GetTrack());
   }
 }
 
 void
 DOMMediaStream::AddTrack(MediaStreamTrack& aTrack)
 {
+  MOZ_RELEASE_ASSERT(mPlaybackStream);
+
   RefPtr<ProcessedMediaStream> dest = mPlaybackStream->AsProcessedStream();
   MOZ_ASSERT(dest);
   if (!dest) {
     return;
   }
 
   LOG(LogLevel::Info, ("DOMMediaStream %p Adding track %p (from stream %p with ID %d)",
                        this, &aTrack, aTrack.GetStream(), aTrack.GetTrackID()));
 
+  if (mPlaybackStream->Graph() !=
+      aTrack.GetStream()->mPlaybackStream->Graph()) {
+    NS_ASSERTION(false, "Cannot combine tracks from different MediaStreamGraphs");
+    LOG(LogLevel::Error, ("DOMMediaStream %p Own MSG %p != aTrack's MSG %p",
+                         this, mPlaybackStream->Graph(),
+                         aTrack.GetStream()->mPlaybackStream->Graph()));
+
+    nsAutoString trackId;
+    aTrack.GetId(trackId);
+    const char16_t* params[] = { trackId.get() };
+    nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(GetParentObject());
+    nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
+    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                    NS_LITERAL_CSTRING("Media"),
+                                    document,
+                                    nsContentUtils::eDOM_PROPERTIES,
+                                    "MediaStreamAddTrackDifferentAudioChannel",
+                                    params, ArrayLength(params));
+    return;
+  }
+
   if (HasTrack(aTrack)) {
     LOG(LogLevel::Debug, ("DOMMediaStream %p already contains track %p", this, &aTrack));
     return;
   }
 
   RefPtr<DOMMediaStream> addedDOMStream = aTrack.GetStream();
   MOZ_RELEASE_ASSERT(addedDOMStream);
 
@@ -544,63 +633,86 @@ DOMMediaStream::IsFinished()
   return !mPlaybackStream || mPlaybackStream->IsFinished();
 }
 
 void
 DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow,
                                  MediaStreamGraph* aGraph)
 {
   mWindow = aWindow;
-  InitStreamCommon(aGraph->CreateSourceStream(nullptr), aGraph);
+  InitInputStreamCommon(aGraph->CreateSourceStream(nullptr), aGraph);
+  InitOwnedStreamCommon(aGraph);
+  InitPlaybackStreamCommon(aGraph);
 }
 
 void
 DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow,
                                      MediaStreamGraph* aGraph)
 {
   mWindow = aWindow;
-  InitStreamCommon(aGraph->CreateTrackUnionStream(nullptr), aGraph);
+  InitInputStreamCommon(aGraph->CreateTrackUnionStream(nullptr), aGraph);
+  InitOwnedStreamCommon(aGraph);
+  InitPlaybackStreamCommon(aGraph);
 }
 
 void
 DOMMediaStream::InitAudioCaptureStream(nsIDOMWindow* aWindow,
                                        MediaStreamGraph* aGraph)
 {
   mWindow = aWindow;
 
   const TrackID AUDIO_TRACK = 1;
 
-  InitStreamCommon(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK), aGraph);
+  InitInputStreamCommon(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK), aGraph);
+  InitOwnedStreamCommon(aGraph);
+  InitPlaybackStreamCommon(aGraph);
   CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO);
 }
 
 void
-DOMMediaStream::InitStreamCommon(MediaStream* aStream,
-                                 MediaStreamGraph* aGraph)
+DOMMediaStream::InitInputStreamCommon(MediaStream* aStream,
+                                      MediaStreamGraph* aGraph)
 {
+  MOZ_ASSERT(!mOwnedStream, "Input stream must be initialized before owned stream");
+
   mInputStream = aStream;
+}
+
+void
+DOMMediaStream::InitOwnedStreamCommon(MediaStreamGraph* aGraph)
+{
+  MOZ_ASSERT(!mPlaybackStream, "Owned stream must be initialized before playback stream");
 
   // We pass null as the wrapper since it is only used to signal finished
   // streams. This is only needed for the playback stream.
   mOwnedStream = aGraph->CreateTrackUnionStream(nullptr);
   mOwnedStream->SetAutofinish(true);
-  mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
-
-  mPlaybackStream = aGraph->CreateTrackUnionStream(this);
-  mPlaybackStream->SetAutofinish(true);
-  mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
-
-  LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, mOwnedStream=%p, mPlaybackStream=%p",
-                        this, mInputStream, mOwnedStream, mPlaybackStream));
+  if (mInputStream) {
+    mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
+  }
 
   // Setup track listeners
   mOwnedListener = new OwnedStreamListener(this);
   mOwnedStream->AddListener(mOwnedListener);
+}
+
+void
+DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph)
+{
+  mPlaybackStream = aGraph->CreateTrackUnionStream(this);
+  mPlaybackStream->SetAutofinish(true);
+  if (mOwnedStream) {
+    mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
+  }
+
   mPlaybackListener = new PlaybackStreamListener(this);
   mPlaybackStream->AddListener(mPlaybackListener);
+
+  LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, mOwnedStream=%p, mPlaybackStream=%p",
+                        this, mInputStream, mOwnedStream, mPlaybackStream));
 }
 
 already_AddRefed<DOMMediaStream>
 DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
                                    MediaStreamGraph* aGraph)
 {
   nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
   stream->InitSourceStream(aWindow, aGraph);
@@ -700,16 +812,19 @@ bool
 DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver)
 {
   return mPrincipalChangeObservers.RemoveElement(aObserver);
 }
 
 MediaStreamTrack*
 DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
 {
+  MOZ_RELEASE_ASSERT(mInputStream);
+  MOZ_RELEASE_ASSERT(mOwnedStream);
+
   MOZ_ASSERT(FindOwnedDOMTrack(GetOwnedStream(), aTrackID) == nullptr);
 
   MediaStreamTrack* track;
   switch (aType) {
   case MediaSegment::AUDIO:
     track = new AudioStreamTrack(this, aTrackID);
     break;
   case MediaSegment::VIDEO:
@@ -731,39 +846,44 @@ DOMMediaStream::CreateOwnDOMTrack(TrackI
 
   NotifyTrackAdded(track);
   return track;
 }
 
 MediaStreamTrack*
 DOMMediaStream::FindOwnedDOMTrack(MediaStream* aOwningStream, TrackID aTrackID) const
 {
+  MOZ_RELEASE_ASSERT(mOwnedStream);
+
   if (aOwningStream != mOwnedStream) {
     return nullptr;
   }
 
   for (const nsRefPtr<TrackPort>& info : mOwnedTracks) {
     if (info->GetTrack()->GetTrackID() == aTrackID) {
       return info->GetTrack();
     }
   }
   return nullptr;
 }
 
 MediaStreamTrack*
 DOMMediaStream::FindPlaybackDOMTrack(MediaStream* aInputStream, TrackID aInputTrackID) const
 {
+  MOZ_RELEASE_ASSERT(mPlaybackStream);
+
   for (const nsRefPtr<TrackPort>& info : mTracks) {
     if (info->GetInputPort() == mPlaybackPort &&
         aInputStream == mOwnedStream &&
         aInputTrackID == info->GetTrack()->GetTrackID()) {
       // This track is in our owned and playback streams.
       return info->GetTrack();
     }
-    if (info->GetInputPort()->GetSource() == aInputStream &&
+    if (info->GetInputPort() &&
+        info->GetInputPort()->GetSource() == aInputStream &&
         info->GetSourceTrackId() == aInputTrackID) {
       // This track is owned externally but in our playback stream.
       MOZ_ASSERT(aInputTrackID != TRACK_NONE);
       MOZ_ASSERT(aInputTrackID != TRACK_INVALID);
       MOZ_ASSERT(aInputTrackID != TRACK_ANY);
       return info->GetTrack();
     }
   }
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -218,23 +218,38 @@ public:
 
   nsIDOMWindow* GetParentObject() const
   {
     return mWindow;
   }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
+
+  static already_AddRefed<DOMMediaStream>
+  Constructor(const dom::GlobalObject& aGlobal,
+              ErrorResult& aRv);
+
+  static already_AddRefed<DOMMediaStream>
+  Constructor(const dom::GlobalObject& aGlobal,
+              const DOMMediaStream& aStream,
+              ErrorResult& aRv);
+
+  static already_AddRefed<DOMMediaStream>
+  Constructor(const dom::GlobalObject& aGlobal,
+              const dom::Sequence<OwningNonNull<MediaStreamTrack>>& aTracks,
+              ErrorResult& aRv);
+
   double CurrentTime();
 
   void GetId(nsAString& aID) const;
 
-  void GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks);
-  void GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks);
-  void GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks);
+  void GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks) const;
+  void GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks) const;
+  void GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks) const;
   void AddTrack(MediaStreamTrack& aTrack);
   void RemoveTrack(MediaStreamTrack& aTrack);
 
   // NON-WebIDL
 
   /**
    * Returns true if this DOMMediaStream has aTrack in its mPlaybackStream.
    */
@@ -352,30 +367,30 @@ public:
    */
   void NotifyStreamFinished();
 
   // Webrtc allows the remote side to name a stream whatever it wants, and we
   // need to surface this to content.
   void AssignId(const nsAString& aID) { mID = aID; }
 
   /**
-   * Create an nsDOMMediaStream whose underlying stream is a SourceMediaStream.
+   * Create a DOMMediaStream whose underlying input stream is a SourceMediaStream.
    */
   static already_AddRefed<DOMMediaStream> CreateSourceStream(nsIDOMWindow* aWindow,
                                                              MediaStreamGraph* aGraph);
 
   /**
-   * Create an nsDOMMediaStream whose underlying stream is a TrackUnionStream.
+   * Create a DOMMediaStream whose underlying input stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMMediaStream> CreateTrackUnionStream(nsIDOMWindow* aWindow,
                                                                  MediaStreamGraph* aGraph);
 
   /**
-   * Create an nsDOMMediaStream whose underlying stream is an
-   * AudioCaptureStream
+   * Create an DOMMediaStream whose underlying input stream is an
+   * AudioCaptureStream.
    */
   static already_AddRefed<DOMMediaStream> CreateAudioCaptureStream(
     nsIDOMWindow* aWindow, MediaStreamGraph* aGraph);
 
   void SetLogicalStreamStartTime(StreamTime aTime)
   {
     mLogicalStreamStartTime = aTime;
   }
@@ -419,17 +434,33 @@ public:
 
 protected:
   virtual ~DOMMediaStream();
 
   void Destroy();
   void InitSourceStream(nsIDOMWindow* aWindow, MediaStreamGraph* aGraph);
   void InitTrackUnionStream(nsIDOMWindow* aWindow, MediaStreamGraph* aGraph);
   void InitAudioCaptureStream(nsIDOMWindow* aWindow, MediaStreamGraph* aGraph);
-  void InitStreamCommon(MediaStream* aStream, MediaStreamGraph* aGraph);
+
+  // Sets up aStream as mInputStream. A producer may append data to a
+  // SourceMediaStream input stream, or connect another stream to a
+  // TrackUnionStream input stream.
+  void InitInputStreamCommon(MediaStream* aStream, MediaStreamGraph* aGraph);
+
+  // Sets up a new TrackUnionStream as mOwnedStream and connects it to
+  // mInputStream with a TRACK_ANY MediaInputPort if available.
+  // If this DOMMediaStream should have an input stream (producing data),
+  // it has to be initiated before the owned stream.
+  void InitOwnedStreamCommon(MediaStreamGraph* aGraph);
+
+  // Sets up a new TrackUnionStream as mPlaybackStream and connects it to
+  // mOwnedStream with a TRACK_ANY MediaInputPort if available.
+  // If this DOMMediaStream should have an owned stream (producer or clone),
+  // it has to be initiated before the playback stream.
+  void InitPlaybackStreamCommon(MediaStreamGraph* aGraph);
 
   void CheckTracksAvailable();
 
   // Called when MediaStreamGraph has finished an iteration where tracks were
   // created.
   void NotifyTracksCreated();
 
   // Dispatches NotifyTrackAdded() to all registered track listeners.
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -343,81 +343,81 @@ MediaDecoder::SetInfinite(bool aInfinite
 
 bool
 MediaDecoder::IsInfinite()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mInfiniteStream;
 }
 
-MediaDecoder::MediaDecoder() :
-  mWatchManager(this, AbstractThread::MainThread()),
-  mDormantSupported(false),
-  mLogicalPosition(0.0),
-  mDuration(std::numeric_limits<double>::quiet_NaN()),
+MediaDecoder::MediaDecoder()
+  : mWatchManager(this, AbstractThread::MainThread())
+  , mDormantSupported(false)
+  , mLogicalPosition(0.0)
+  , mDuration(std::numeric_limits<double>::quiet_NaN())
 #ifdef MOZ_EME
-  mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__)),
+  , mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__))
 #endif
-  mIgnoreProgressData(false),
-  mInfiniteStream(false),
-  mOwner(nullptr),
-  mPlaybackStatistics(new MediaChannelStatistics()),
-  mPinnedForSeek(false),
-  mShuttingDown(false),
-  mPausedForPlaybackRateNull(false),
-  mMinimizePreroll(false),
-  mMediaTracksConstructed(false),
-  mFiredMetadataLoaded(false),
-  mIsDormant(false),
-  mWasEndedWhenEnteredDormant(false),
-  mIsHeuristicDormantSupported(
-    Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)),
-  mHeuristicDormantTimeout(
-    Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
-                        DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS)),
-  mIsHeuristicDormant(false),
-  mStateMachineIsShutdown(AbstractThread::MainThread(), true,
-                          "MediaDecoder::mStateMachineIsShutdown (Mirror)"),
-  mBuffered(AbstractThread::MainThread(), TimeIntervals(),
-            "MediaDecoder::mBuffered (Mirror)"),
-  mNextFrameStatus(AbstractThread::MainThread(),
-                   MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
-                   "MediaDecoder::mNextFrameStatus (Mirror)"),
-  mCurrentPosition(AbstractThread::MainThread(), 0,
-                   "MediaDecoder::mCurrentPosition (Mirror)"),
-  mStateMachineDuration(AbstractThread::MainThread(), NullableTimeUnit(),
-                        "MediaDecoder::mStateMachineDuration (Mirror)"),
-  mPlaybackPosition(AbstractThread::MainThread(), 0,
-                    "MediaDecoder::mPlaybackPosition (Mirror)"),
-  mVolume(AbstractThread::MainThread(), 0.0,
-          "MediaDecoder::mVolume (Canonical)"),
-  mPlaybackRate(AbstractThread::MainThread(), 1.0,
-                "MediaDecoder::mPlaybackRate (Canonical)"),
-  mPreservesPitch(AbstractThread::MainThread(), true,
-                  "MediaDecoder::mPreservesPitch (Canonical)"),
-  mEstimatedDuration(AbstractThread::MainThread(), NullableTimeUnit(),
-                     "MediaDecoder::mEstimatedDuration (Canonical)"),
-  mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
-                    "MediaDecoder::mExplicitDuration (Canonical)"),
-  mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
-             "MediaDecoder::mPlayState (Canonical)"),
-  mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
-             "MediaDecoder::mNextState (Canonical)"),
-  mLogicallySeeking(AbstractThread::MainThread(), false,
-                    "MediaDecoder::mLogicallySeeking (Canonical)"),
-  mSameOriginMedia(AbstractThread::MainThread(), false,
-                   "MediaDecoder::mSameOriginMedia (Canonical)"),
-  mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0,
-                          "MediaDecoder::mPlaybackBytesPerSecond (Canonical)"),
-  mPlaybackRateReliable(AbstractThread::MainThread(), true,
-                        "MediaDecoder::mPlaybackRateReliable (Canonical)"),
-  mDecoderPosition(AbstractThread::MainThread(), 0,
-                   "MediaDecoder::mDecoderPosition (Canonical)"),
-  mMediaSeekable(AbstractThread::MainThread(), true,
-                 "MediaDecoder::mMediaSeekable (Canonical)")
+  , mIgnoreProgressData(false)
+  , mInfiniteStream(false)
+  , mOwner(nullptr)
+  , mPlaybackStatistics(new MediaChannelStatistics())
+  , mPinnedForSeek(false)
+  , mShuttingDown(false)
+  , mPausedForPlaybackRateNull(false)
+  , mMinimizePreroll(false)
+  , mMediaTracksConstructed(false)
+  , mFiredMetadataLoaded(false)
+  , mIsDormant(false)
+  , mWasEndedWhenEnteredDormant(false)
+  , mIsHeuristicDormantSupported(
+      Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
+  , mHeuristicDormantTimeout(
+      Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
+                          DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS))
+  , mIsHeuristicDormant(false)
+  , mStateMachineIsShutdown(AbstractThread::MainThread(), true,
+                            "MediaDecoder::mStateMachineIsShutdown (Mirror)")
+  , mBuffered(AbstractThread::MainThread(), TimeIntervals(),
+              "MediaDecoder::mBuffered (Mirror)")
+  , mNextFrameStatus(AbstractThread::MainThread(),
+                     MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
+                     "MediaDecoder::mNextFrameStatus (Mirror)")
+  , mCurrentPosition(AbstractThread::MainThread(), 0,
+                     "MediaDecoder::mCurrentPosition (Mirror)")
+  , mStateMachineDuration(AbstractThread::MainThread(), NullableTimeUnit(),
+                          "MediaDecoder::mStateMachineDuration (Mirror)")
+  , mPlaybackPosition(AbstractThread::MainThread(), 0,
+                      "MediaDecoder::mPlaybackPosition (Mirror)")
+  , mVolume(AbstractThread::MainThread(), 0.0,
+            "MediaDecoder::mVolume (Canonical)")
+  , mPlaybackRate(AbstractThread::MainThread(), 1.0,
+                  "MediaDecoder::mPlaybackRate (Canonical)")
+  , mPreservesPitch(AbstractThread::MainThread(), true,
+                    "MediaDecoder::mPreservesPitch (Canonical)")
+  , mEstimatedDuration(AbstractThread::MainThread(), NullableTimeUnit(),
+                       "MediaDecoder::mEstimatedDuration (Canonical)")
+  , mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
+                      "MediaDecoder::mExplicitDuration (Canonical)")
+  , mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
+               "MediaDecoder::mPlayState (Canonical)")
+  , mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
+               "MediaDecoder::mNextState (Canonical)")
+  , mLogicallySeeking(AbstractThread::MainThread(), false,
+                      "MediaDecoder::mLogicallySeeking (Canonical)")
+  , mSameOriginMedia(AbstractThread::MainThread(), false,
+                     "MediaDecoder::mSameOriginMedia (Canonical)")
+  , mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0,
+                            "MediaDecoder::mPlaybackBytesPerSecond (Canonical)")
+  , mPlaybackRateReliable(AbstractThread::MainThread(), true,
+                          "MediaDecoder::mPlaybackRateReliable (Canonical)")
+  , mDecoderPosition(AbstractThread::MainThread(), 0,
+                     "MediaDecoder::mDecoderPosition (Canonical)")
+  , mMediaSeekable(AbstractThread::MainThread(), true,
+                   "MediaDecoder::mMediaSeekable (Canonical)")
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 
   //
@@ -1302,16 +1302,23 @@ size_t
 MediaDecoder::SizeOfAudioQueue() {
   MOZ_ASSERT(NS_IsMainThread());
   if (mDecoderStateMachine) {
     return mDecoderStateMachine->SizeOfAudioQueue();
   }
   return 0;
 }
 
+void MediaDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (GetResource()) {
+    aSizes->mByteSize += GetResource()->SizeOfIncludingThis(aSizes->mMallocSizeOf);
+  }
+}
+
 void
 MediaDecoder::NotifyDataArrived(uint32_t aLength,
                                 int64_t aOffset,
                                 bool aThrottleUpdates) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mDecoderStateMachine) {
     mDecoderStateMachine->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
@@ -1498,26 +1505,50 @@ MediaDecoder::IsAppleMP3Enabled()
 }
 #endif
 
 NS_IMETHODIMP
 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
                                    nsISupports* aData, bool aAnonymize)
 {
   int64_t video = 0, audio = 0;
-  size_t resources = 0;
+
+  // NB: When resourceSizes' ref count goes to 0 the promise will report the
+  //     resources memory and finish the asynchronous memory report.
+  nsRefPtr<MediaDecoder::ResourceSizes> resourceSizes =
+      new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf);
+
+  nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
+  nsCOMPtr<nsISupports> data = aData;
+
+  resourceSizes->Promise()->Then(
+      AbstractThread::MainThread(), __func__,
+      [handleReport, data] (size_t size) {
+        handleReport->Callback(
+            EmptyCString(), NS_LITERAL_CSTRING("explicit/media/resources"),
+            KIND_HEAP, UNITS_BYTES, size,
+            NS_LITERAL_CSTRING("Memory used by media resources including "
+                               "streaming buffers, caches, etc."),
+            data);
+
+        nsCOMPtr<nsIMemoryReporterManager> imgr =
+          do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+        if (imgr) {
+          imgr->EndReport();
+        }
+      },
+      [] (size_t) { /* unused reject function */ });
+
   DecodersArray& decoders = Decoders();
   for (size_t i = 0; i < decoders.Length(); ++i) {
     MediaDecoder* decoder = decoders[i];
     video += decoder->SizeOfVideoQueue();
     audio += decoder->SizeOfAudioQueue();
-
-    if (decoder->GetResource()) {
-      resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf);
-    }
+    decoder->AddSizeOfResources(resourceSizes);
   }
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
       nsresult rv;                                                            \
       rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
                                    KIND_HEAP, UNITS_BYTES, _amount,           \
                                    NS_LITERAL_CSTRING(_desc), aData);         \
@@ -1525,20 +1556,16 @@ MediaMemoryTracker::CollectReports(nsIHa
   } while (0)
 
   REPORT("explicit/media/decoded/video", video,
          "Memory used by decoded video frames.");
 
   REPORT("explicit/media/decoded/audio", audio,
          "Memory used by decoded audio chunks.");
 
-  REPORT("explicit/media/resources", resources,
-         "Memory used by media resources including streaming buffers, caches, "
-         "etc.");
-
 #undef REPORT
 
   return NS_OK;
 }
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
@@ -1615,17 +1642,17 @@ MediaDecoder::RemoveMediaTracks()
 
 MediaMemoryTracker::MediaMemoryTracker()
 {
 }
 
 void
 MediaMemoryTracker::InitMemoryReporter()
 {
-  RegisterWeakMemoryReporter(this);
+  RegisterWeakAsyncMemoryReporter(this);
 }
 
 MediaMemoryTracker::~MediaMemoryTracker()
 {
   UnregisterWeakMemoryReporter(this);
 }
 
 } // namespace mozilla
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -183,16 +183,17 @@ destroying the MediaDecoder object.
 */
 #if !defined(MediaDecoder_h_)
 #define MediaDecoder_h_
 
 #ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
 #endif
 
+#include "mozilla/Atomics.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/StateMirroring.h"
 #include "mozilla/StateWatching.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 
 #include "necko-config.h"
@@ -525,16 +526,49 @@ public:
   // are buffered and playable.
   virtual media::TimeIntervals GetBuffered();
 
   // Returns the size, in bytes, of the heap memory used by the currently
   // queued decoded video and audio data.
   size_t SizeOfVideoQueue();
   size_t SizeOfAudioQueue();
 
+  // Helper struct for accumulating resource sizes that need to be measured
+  // asynchronously. Once all references are dropped the callback will be
+  // invoked.
+  struct ResourceSizes
+  {
+    typedef MozPromise<size_t, size_t, true> SizeOfPromise;
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ResourceSizes)
+    explicit ResourceSizes(MallocSizeOf aMallocSizeOf)
+      : mMallocSizeOf(aMallocSizeOf)
+      , mByteSize(0)
+      , mCallback()
+    {
+    }
+
+    mozilla::MallocSizeOf mMallocSizeOf;
+    mozilla::Atomic<size_t> mByteSize;
+
+    nsRefPtr<SizeOfPromise> Promise()
+    {
+      return mCallback.Ensure(__func__);
+    }
+
+private:
+    ~ResourceSizes()
+    {
+      mCallback.ResolveIfExists(mByteSize, __func__);
+    }
+
+    MozPromiseHolder<SizeOfPromise> mCallback;
+  };
+
+  virtual void AddSizeOfResources(ResourceSizes* aSizes);
+
   VideoFrameContainer* GetVideoFrameContainer() final override
   {
     return mVideoFrameContainer;
   }
   layers::ImageContainer* GetImageContainer() override;
 
   // Fire timeupdate events if needed according to the time constraints
   // outlined in the specification.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1,8 +1,9 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef XP_WIN
 // Include Windows headers required for enabling high precision timers.
 #include "windows.h"
@@ -868,24 +869,26 @@ MediaDecoderStateMachine::OnVideoDecoded
       Push(video, MediaData::VIDEO_DATA);
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       if (mIsVideoPrerolling && DonePrerollingVideo()) {
         StopPrerollingVideo();
       }
 
-      // Schedule the state machine to send stream data as soon as possible or
-      // the VideoQueue() is empty before the Push().
-      // VideoQueue() is empty implies the state machine thread doesn't have
-      // precise time information about video frames. Once the first video
-      // frame pushed in the queue, schedule the state machine as soon as
-      // possible to render the video frame or delay the state machine thread
-      // accurately.
-      if (VideoQueue().GetSize() == 1) {
+      // Schedule the state machine to send stream data as soon as possible if
+      // the VideoQueue() is empty or contains one frame before the Push().
+      //
+      // The state machine threads requires a frame in VideoQueue() that is `in
+      // the future` to gather precise timing information. The head of
+      // VideoQueue() is always `in the past`.
+      //
+      // Schedule the state machine as soon as possible to render the video
+      // frame or delay the state machine thread accurately.
+      if (VideoQueue().GetSize() <= 2) {
         ScheduleStateMachine();
       }
 
       // For non async readers, if the requested video sample was slow to
       // arrive, increase the amount of audio we buffer to ensure that we
       // don't run out of audio. This is unnecessary for async readers,
       // since they decode audio and video on different threads so they
       // are unlikely to run out of decoded audio.
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -171,17 +171,17 @@ MP4Decoder::CanHandleMediaType(const nsA
 
   return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), codecs);
 }
 
 /* static */
 bool
 MP4Decoder::IsEnabled()
 {
-  return Preferences::GetBool("media.fragmented-mp4.enabled");
+  return Preferences::GetBool("media.mp4.enabled");
 }
 
 static const uint8_t sTestH264ExtraData[] = {
   0x01, 0x64, 0x00, 0x0a, 0xff, 0xe1, 0x00, 0x17, 0x67, 0x64,
   0x00, 0x0a, 0xac, 0xd9, 0x44, 0x26, 0x84, 0x00, 0x00, 0x03,
   0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xc8, 0x3c, 0x48, 0x96,
   0x58, 0x01, 0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0
 };
--- a/dom/media/gtest/TestMP4Reader.cpp
+++ b/dom/media/gtest/TestMP4Reader.cpp
@@ -25,17 +25,17 @@ public:
   nsRefPtr<MP4Reader> reader;
 
   explicit TestBinding(const char* aFileName = "gizmo.mp4")
     : decoder(new MP4Decoder())
     , resource(new MockMediaResource(aFileName))
     , reader(new MP4Reader(decoder))
   {
     EXPECT_EQ(NS_OK, Preferences::SetBool(
-                       "media.fragmented-mp4.use-blank-decoder", true));
+                       "media.use-blank-decoder", true));
 
     EXPECT_EQ(NS_OK, resource->Open(nullptr));
     decoder->SetResource(resource);
 
     reader->Init(nullptr);
     // This needs to be done before invoking GetBuffered. This is normally
     // done by MediaDecoderStateMachine.
     reader->DispatchSetStartTime(0);
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -172,16 +172,25 @@ void
 MediaSourceDecoder::Ended(bool aEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
   static_cast<MediaSourceResource*>(GetResource())->SetEnded(aEnded);
   mEnded = true;
 }
 
 void
+MediaSourceDecoder::AddSizeOfResources(ResourceSizes* aSizes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (GetDemuxer()) {
+    GetDemuxer()->AddSizeOfResources(aSizes);
+  }
+}
+
+void
 MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Only use the decoded duration if one wasn't already
   // set.
   if (!mMediaSource || !IsNaN(ExplicitDuration())) {
     return;
   }
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -70,16 +70,18 @@ public:
   {
     return mDemuxer;
   }
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
+  void AddSizeOfResources(ResourceSizes* aSizes) override;
+
 private:
   void DoSetMediaSourceDuration(double aDuration);
 
   // The owning MediaSource holds a strong reference to this decoder, and
   // calls Attach/DetachMediaSource on this decoder to set and clear
   // mMediaSource.
   dom::MediaSource* mMediaSource;
   nsRefPtr<MediaSourceDemuxer> mDemuxer;
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -48,16 +48,34 @@ MediaSourceDemuxer::AttemptInit()
     return InitPromise::CreateAndResolve(NS_OK, __func__);
   }
 
   nsRefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
 
   return p;
 }
 
+void
+MediaSourceDemuxer::AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // NB: The track buffers must only be accessed on the TaskQueue.
+  nsRefPtr<MediaSourceDemuxer> self = this;
+  nsRefPtr<MediaSourceDecoder::ResourceSizes> sizes = aSizes;
+  nsCOMPtr<nsIRunnable> task =
+    NS_NewRunnableFunction([self, sizes] () {
+      for (TrackBuffersManager* manager : self->mSourceBuffers) {
+        manager->AddSizeOfResources(sizes);
+      }
+    });
+
+  GetTaskQueue()->Dispatch(task.forget());
+}
+
 void MediaSourceDemuxer::NotifyDataArrived()
 {
   nsRefPtr<MediaSourceDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
       if (self->mInitPromise.IsEmpty()) {
         return;
       }
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -48,16 +48,18 @@ public:
   void AttachSourceBuffer(TrackBuffersManager* aSourceBuffer);
   void DetachSourceBuffer(TrackBuffersManager* aSourceBuffer);
   TaskQueue* GetTaskQueue() { return mTaskQueue; }
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
+  void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
+
 private:
   ~MediaSourceDemuxer();
   friend class MediaSourceTrackDemuxer;
   // Scan source buffers and update information.
   bool ScanSourceBuffersForContent();
   nsRefPtr<InitPromise> AttemptInit();
   TrackBuffersManager* GetManager(TrackInfo::TrackType aType);
   TrackInfo* GetTrackInfo(TrackInfo::TrackType);
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -2118,12 +2118,30 @@ TrackBuffersManager::GetNextRandomAccess
     const nsRefPtr<MediaRawData>& sample = track[i];
     if (sample->mKeyframe) {
       return TimeUnit::FromMicroseconds(sample->mTime);
     }
   }
   return media::TimeUnit::FromInfinity();
 }
 
+void
+TrackBuffersManager::TrackData::AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes)
+{
+  for (TrackBuffer& buffer : mBuffers) {
+    for (MediaRawData* data : buffer) {
+      aSizes->mByteSize += data->SizeOfIncludingThis(aSizes->mMallocSizeOf);
+    }
+  }
+}
+
+void
+TrackBuffersManager::AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  mVideoTracks.AddSizeOfResources(aSizes);
+  mAudioTracks.AddSizeOfResources(aSizes);
+}
+
 } // namespace mozilla
 #undef MSE_DEBUG
 #undef MSE_DEBUGV
 #undef SAMPLE_DEBUG
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -97,16 +97,18 @@ public:
                                            const media::TimeUnit& aFuzz,
                                            bool& aError);
   media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack);
 
 #if defined(DEBUG)
   void Dump(const char* aPath) override;
 #endif
 
+  void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
+
 private:
   // for MediaSourceDemuxer::GetMozDebugReaderData
   friend class MediaSourceDemuxer;
   virtual ~TrackBuffersManager();
   // All following functions run on the taskqueue.
   nsRefPtr<AppendPromise> InitSegmentParserLoop();
   void ScheduleSegmentParserLoop();
   void SegmentParserLoop();
@@ -276,16 +278,18 @@ private:
       mLastDecodeTimestamp.reset();
       mLastFrameDuration.reset();
       mHighestEndTimestamp.reset();
       mNeedRandomAccessPoint = true;
 
       mLongestFrameDuration.reset();
       mNextInsertionIndex.reset();
     }
+
+    void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
   };
 
   void CheckSequenceDiscontinuity(const media::TimeUnit& aPresentationTime);
   void ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData);
   bool CheckNextInsertionIndex(TrackData& aTrackData,
                                const media::TimeUnit& aSampleTime);
   void InsertFrames(TrackBuffer& aSamples,
                     const media::TimeIntervals& aIntervals,
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -51,16 +51,17 @@ skip-if = ((os == "win" && os_version ==
 [test_FrameSelection.html]
 [test_HaveMetadataUnbufferedSeek.html]
 [test_HaveMetadataUnbufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
 [test_LoadedMetadataFired.html]
 [test_LoadedMetadataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource.html]
+[test_MediaSource_memory_reporting.html]
 [test_MediaSource_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource_disabled.html]
 [test_MultipleInitSegments.html]
 [test_MultipleInitSegments_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableAfterEndOfStream.html]
 [test_SeekableAfterEndOfStream_mp4.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_MediaSource_memory_reporting.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: memory reporting</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+runWithMSE(function (ms, v) {
+  // Test that memory reporting works once we've played a video.
+  once(v, "stalled", () => {
+    // Grab a memory report.
+    var mgr = SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]
+          .getService(SpecialPowers.Ci.nsIMemoryReporterManager);
+
+    var amount = 0;
+    var resourcesPathSeen = false;
+    var handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
+      if (aPath == "explicit/media/resources") {
+        resourcePathSeen = true;
+        amount += aAmount;
+      }
+    }
+
+    var finished = function () {
+      ok(true, "Yay didn't crash!");
+      ok(resourcePathSeen, "Got media resources amount");
+      ok(amount > 0, "Non-zero amount reported for media resources");
+      SimpleTest.finish();
+    }
+
+    mgr.getReports(handleReport, null, finished, null, /* anonymized = */ false);
+  });
+
+  // Load a webm video and play it.
+  ms.addEventListener("sourceopen", () => {
+    var sb = ms.addSourceBuffer("video/webm");
+    fetchAndLoad(sb, 'seek', [''], '.webm').then(() => v.play());
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/omx/OMXCodecWrapper.cpp
+++ b/dom/media/omx/OMXCodecWrapper.cpp
@@ -12,17 +12,20 @@
 #include <media/ICrypto.h>
 #include <media/IOMX.h>
 #include <OMX_Component.h>
 #include <stagefright/MediaDefs.h>
 #include <stagefright/MediaErrors.h>
 
 #include "AudioChannelFormat.h"
 #include "GrallocImages.h"
+#include "LayersLogging.h"
+#include "libyuv.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/layers/GrallocTextureClient.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 #define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
 // AMR NB kbps
@@ -375,16 +378,77 @@ ConvertGrallocImageToNV12(GrallocImage* 
       break;
     default:
       NS_ERROR("Unsupported input gralloc image type. Should never be here.");
   }
 
   graphicBuffer->unlock();
 }
 
+static nsresult
+ConvertSourceSurfaceToNV12(const nsRefPtr<SourceSurface>& aSurface, uint8_t* aDestination)
+{
+  uint32_t width = aSurface->GetSize().width;
+  uint32_t height = aSurface->GetSize().height;
+
+  uint8_t* y = aDestination;
+  int yStride = width;
+
+  uint8_t* uv = y + (yStride * height);
+  int uvStride = width / 2;
+
+  SurfaceFormat format = aSurface->GetFormat();
+
+  if (!aSurface) {
+    CODEC_ERROR("Getting surface %s from image failed", Stringify(format).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+  if (!data) {
+    CODEC_ERROR("Getting data surface from %s image with %s (%s) surface failed",
+                Stringify(format).c_str(), Stringify(aSurface->GetType()).c_str(),
+                Stringify(aSurface->GetFormat()).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
+  if (!map.IsMapped()) {
+    CODEC_ERROR("Reading DataSourceSurface from %s image with %s (%s) surface failed",
+                Stringify(format).c_str(), Stringify(aSurface->GetType()).c_str(),
+                Stringify(aSurface->GetFormat()).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  int rv;
+  switch (aSurface->GetFormat()) {
+    case SurfaceFormat::B8G8R8A8:
+    case SurfaceFormat::B8G8R8X8:
+      rv = libyuv::ARGBToNV12(static_cast<uint8*>(map.GetData()),
+                              map.GetStride(),
+                              y, yStride,
+                              uv, uvStride,
+                              width, height);
+      break;
+    default:
+      CODEC_ERROR("Unsupported SourceSurface format %s",
+                  Stringify(aSurface->GetFormat()).c_str());
+      NS_ASSERTION(false, "Unsupported SourceSurface format");
+      return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  if (rv != 0) {
+    CODEC_ERROR("%s to I420 conversion failed",
+                Stringify(aSurface->GetFormat()).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
 nsresult
 OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
                         int64_t aTimestamp, int aInputFlags, bool* aSendEOS)
 {
   MOZ_ASSERT(mStarted, "Configure() should be called before Encode().");
 
   NS_ENSURE_TRUE(aWidth == mWidth && aHeight == mHeight && aTimestamp >= 0,
                  NS_ERROR_INVALID_ARG);
@@ -405,19 +469,20 @@ OMXVideoEncoder::Encode(const Image* aIm
     } else if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
       // Reject unsupported gralloc-ed buffers.
       int halFormat = static_cast<GrallocImage*>(img)->GetGraphicBuffer()->getPixelFormat();
       NS_ENSURE_TRUE(halFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
                      halFormat == HAL_PIXEL_FORMAT_YV12 ||
                      halFormat == GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
                      NS_ERROR_INVALID_ARG);
     } else {
-      // TODO: support RGB to YUV color conversion.
-      NS_ERROR("Unsupported input image type.");
-      return NS_ERROR_INVALID_ARG;
+      nsRefPtr<SourceSurface> surface = img->GetAsSourceSurface();
+      NS_ENSURE_TRUE(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+                     surface->GetFormat() == SurfaceFormat::B8G8R8X8,
+                     NS_ERROR_INVALID_ARG);
     }
   }
 
   status_t result;
 
   // Dequeue an input buffer.
   uint32_t index;
   result = mCodec->dequeueInputBuffer(&index, INPUT_BUFFER_TIMEOUT_US);
@@ -445,16 +510,23 @@ OMXVideoEncoder::Encode(const Image* aIm
     // Fill UV plane.
     memset(dst + yLen, 0x80, uvLen);
   } else {
     if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
       ConvertGrallocImageToNV12(static_cast<GrallocImage*>(img), dst);
     } else if (format == ImageFormat::PLANAR_YCBCR) {
       ConvertPlanarYCbCrToNV12(static_cast<PlanarYCbCrImage*>(img)->GetData(),
                                dst);
+    } else {
+      nsRefPtr<SourceSurface> surface = img->GetAsSourceSurface();
+      nsresult rv = ConvertSourceSurfaceToNV12(surface, dst);
+
+      if (rv != NS_OK) {
+        return NS_ERROR_FAILURE;
+      }
     }
   }
 
   // Queue this input buffer.
   result = mCodec->queueInputBuffer(index, 0, dstSize, aTimestamp, aInputFlags);
 
   if (aSendEOS && (aInputFlags & BUFFER_EOS) && result == OK) {
     *aSendEOS = true;
--- a/dom/media/omx/moz.build
+++ b/dom/media/omx/moz.build
@@ -36,16 +36,17 @@ if CONFIG['MOZ_AUDIO_OFFLOAD']:
 if CONFIG['MOZ_OMX_ENCODER']:
     EXPORTS += [
         'OMXCodecWrapper.h',
     ]
     SOURCES += [
         'OMXCodecDescriptorUtil.cpp',
         'OMXCodecWrapper.cpp',
     ]
+    LOCAL_INCLUDES += ['/media/libyuv/include']
 
 if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
     EXPORTS += [
         'RtspExtractor.h',
         'RtspOmxDecoder.h',
         'RtspOmxReader.h',
     ]
     SOURCES += [
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -40,21 +40,30 @@
 #endif
 
 namespace mozilla {
 
 extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
 extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
 
 bool PDMFactory::sUseBlankDecoder = false;
+#ifdef MOZ_GONK_MEDIACODEC
 bool PDMFactory::sGonkDecoderEnabled = false;
+#endif
+#ifdef MOZ_WIDGET_ANDROID
 bool PDMFactory::sAndroidMCDecoderEnabled = false;
 bool PDMFactory::sAndroidMCDecoderPreferred = false;
+#endif
 bool PDMFactory::sGMPDecoderEnabled = false;
+#ifdef MOZ_FFMPEG
 bool PDMFactory::sFFmpegDecoderEnabled = false;
+#endif
+#ifdef XP_WIN
+bool PDMFactory::sWMFDecoderEnabled = false;
+#endif
 
 bool PDMFactory::sEnableFuzzingWrapper = false;
 uint32_t PDMFactory::sVideoOutputMinimumInterval_ms = 0;
 bool PDMFactory::sDontDelayInputExhausted = false;
 
 /* static */
 void
 PDMFactory::Init()
@@ -62,32 +71,38 @@ PDMFactory::Init()
   MOZ_ASSERT(NS_IsMainThread());
   static bool alreadyInitialized = false;
   if (alreadyInitialized) {
     return;
   }
   alreadyInitialized = true;
 
   Preferences::AddBoolVarCache(&sUseBlankDecoder,
-                               "media.fragmented-mp4.use-blank-decoder");
+                               "media.use-blank-decoder");
 #ifdef MOZ_GONK_MEDIACODEC
   Preferences::AddBoolVarCache(&sGonkDecoderEnabled,
-                               "media.fragmented-mp4.gonk.enabled", false);
+                               "media.gonk.enabled", false);
 #endif
 #ifdef MOZ_WIDGET_ANDROID
   Preferences::AddBoolVarCache(&sAndroidMCDecoderEnabled,
-                               "media.fragmented-mp4.android-media-codec.enabled", false);
+                               "media.android-media-codec.enabled", false);
   Preferences::AddBoolVarCache(&sAndroidMCDecoderPreferred,
-                               "media.fragmented-mp4.android-media-codec.preferred", false);
+                               "media.android-media-codec.preferred", false);
 #endif
 
   Preferences::AddBoolVarCache(&sGMPDecoderEnabled,
-                               "media.fragmented-mp4.gmp.enabled", false);
+                               "media.gmp.decoder.enabled", false);
+#ifdef MOZ_FFMPEG
   Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
-                               "media.fragmented-mp4.ffmpeg.enabled", false);
+                               "media.ffmpeg.enabled", false);
+#endif
+#ifdef XP_WIN
+  Preferences::AddBoolVarCache(&sWMFDecoderEnabled,
+                               "media.wmf.enabled", false);
+#endif
 
   Preferences::AddBoolVarCache(&sEnableFuzzingWrapper,
                                "media.decoder.fuzzing.enabled", false);
   Preferences::AddUintVarCache(&sVideoOutputMinimumInterval_ms,
                                "media.decoder.fuzzing.video-output-minimum-interval-ms", 0);
   Preferences::AddBoolVarCache(&sDontDelayInputExhausted,
                                "media.decoder.fuzzing.dont-delay-inputexhausted", false);
 
@@ -199,18 +214,20 @@ PDMFactory::CreatePDMs()
   }
 #ifdef MOZ_WIDGET_ANDROID
   if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled) {
     m = new AndroidDecoderModule();
     StartupPDM(m);
   }
 #endif
 #ifdef XP_WIN
-  m = new WMFDecoderModule();
-  StartupPDM(m);
+  if (sWMFDecoderEnabled) {
+    m = new WMFDecoderModule();
+    StartupPDM(m);
+  }
 #endif
 #ifdef MOZ_FFMPEG
   if (sFFmpegDecoderEnabled) {
     m = FFmpegRuntimeLinker::CreateDecoderModule();
     StartupPDM(m);
   }
 #endif
 #ifdef MOZ_APPLEMEDIA
--- a/dom/media/platforms/PDMFactory.h
+++ b/dom/media/platforms/PDMFactory.h
@@ -49,23 +49,32 @@ public:
 private:
   virtual ~PDMFactory();
   void CreatePDMs();
   // Startup the provided PDM and add it to our list if successful.
   bool StartupPDM(PlatformDecoderModule* aPDM);
   // Returns the first PDM in our list supporting the mimetype.
   already_AddRefed<PlatformDecoderModule> GetDecoder(const nsACString& aMimeType);
 
-  // Caches pref media.fragmented-mp4.use-blank-decoder
+  // PDM pref caches...
   static bool sUseBlankDecoder;
+#ifdef MOZ_GONK_MEDIACODEC
   static bool sGonkDecoderEnabled;
+#endif
+#ifdef MOZ_WIDGET_ANDROID
   static bool sAndroidMCDecoderPreferred;
   static bool sAndroidMCDecoderEnabled;
+#endif
   static bool sGMPDecoderEnabled;
+#ifdef MOZ_FFMPEG
   static bool sFFmpegDecoderEnabled;
+#endif
+#ifdef XP_WIN
+  static bool sWMFDecoderEnabled;
+#endif
   static bool sEnableFuzzingWrapper;
   static uint32_t sVideoOutputMinimumInterval_ms;
   static bool sDontDelayInputExhausted;
 
   nsTArray<nsRefPtr<PlatformDecoderModule>> mCurrentPDMs;
   nsRefPtr<PlatformDecoderModule> mEMEPDM;
 };
 
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -37,17 +37,17 @@ class CDMProxy;
 // PlatformDecoderModule to provide access to its decoders in order to get
 // decompressed H.264/AAC from the MediaFormatReader.
 //
 // Decoding is asynchronous, and should be performed on the task queue
 // provided if the underlying platform isn't already exposing an async API.
 //
 // A cross-platform decoder module that discards input and produces "blank"
 // output samples exists for testing, and is created when the pref
-// "media.fragmented-mp4.use-blank-decoder" is true.
+// "media.use-blank-decoder" is true.
 
 class PlatformDecoderModule {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule)
 
   // Perform any per-instance initialization.
   // This is called on the decode task queue.
   virtual nsresult Startup() { return NS_OK; };
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
@@ -92,19 +92,19 @@ GMPDecoderModule::DecoderNeedsConversion
 static uint32_t sPreferredAacGmp = 0;
 static uint32_t sPreferredH264Gmp = 0;
 
 /* static */
 void
 GMPDecoderModule::Init()
 {
   Preferences::AddUintVarCache(&sPreferredAacGmp,
-                               "media.fragmented-mp4.gmp.aac", 0);
+                               "media.gmp.decoder.aac", 0);
   Preferences::AddUintVarCache(&sPreferredH264Gmp,
-                               "media.fragmented-mp4.gmp.h264", 0);
+                               "media.gmp.decoder.h264", 0);
 }
 
 /* static */
 const Maybe<nsCString>
 GMPDecoderModule::PreferredGMP(const nsACString& aMimeType)
 {
   Maybe<nsCString> rv;
   if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -419,13 +419,13 @@ function SetupEMEPref(callback) {
   var prefs = [
     [ "media.mediasource.enabled", true ],
     [ "media.eme.apiVisible", true ],
   ];
 
   if (SpecialPowers.Services.appinfo.name == "B2G" ||
       !manifestVideo().canPlayType("video/mp4")) {
     // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
-    prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+    prefs.push([ "media.use-blank-decoder", true ]);
   }
 
   SpecialPowers.pushPrefEnv({ "set" : prefs }, callback);
 }
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -612,36 +612,36 @@ skip-if = (toolkit == 'android' && proce
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_dormant_playback.html]
 skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk')
 [test_eme_canvas_blocked.html]
-skip-if = toolkit == 'android' || (os == 'win') # bug 1043403, bug 1140675, win debug : Bug 1202683
+skip-if = toolkit == 'android' # bug 1149374
 [test_eme_non_mse_fails.html]
-skip-if = toolkit == 'android' || (os == 'win' && debug) # bug 1043403, win debug : Bug 1202683
+skip-if = toolkit == 'android' # bug 1149374
 [test_eme_request_notifications.html]
-skip-if = toolkit == 'android' || (os == 'win' && debug) # bug 1043403, win debug : Bug 1202683
+skip-if = toolkit == 'android' # bug 1149374
 [test_eme_persistent_sessions.html]
-skip-if = toolkit == 'android' || (os == 'win' && debug) # bug 1043403, win debug : Bug 1202683
+skip-if = toolkit == 'android' # bug 1149374
 [test_eme_playback.html]
-skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1187903, bug 1186406, bug 1193351
+skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
 [test_eme_requestKeySystemAccess.html]
-skip-if = toolkit == 'android' || (os == 'win' && debug) # bug 1043403, win debug : Bug 1202683
+skip-if = toolkit == 'android' # bug 1149374
 [test_eme_stream_capture_blocked_case1.html]
 tags=msg capturestream
-skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1140675, bug 1187903, bug 1193351
+skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
 [test_eme_stream_capture_blocked_case2.html]
 tags=msg capturestream
-skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1140675, bug 1187903, bug 1193351
+skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
 [test_eme_stream_capture_blocked_case3.html]
 tags=msg capturestream
-skip-if = toolkit == 'android' || toolkit == 'gonk' || os == 'win' # bug 1043403, bug 1140675, bug 1187903, bug 1193351
+skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
 [test_empty_resource.html]
 skip-if = os == 'win' && debug #win debug : Bug 1202683
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
@@ -680,20 +680,20 @@ tags=msg
 tags=msg
 [test_mediarecorder_record_audiocontext.html]
 tags=msg
 [test_mediarecorder_record_audiocontext_mlk.html]
 tags=msg
 [test_mediarecorder_record_audionode.html]
 tags=msg
 [test_mediarecorder_record_canvas_captureStream.html]
-skip-if = (toolkit == 'android' || toolkit == 'gonk') # Bug 1210286 to fix canvas capturestream support in the OMX backend
+skip-if = android_version < '17' # Android/Gonk before SDK version 17 does not have the OMX Encoder API.
 tags=msg
 [test_mediarecorder_record_changing_video_resolution.html]
-skip-if = (toolkit == 'android' || toolkit == 'gonk') # Bug 1210286 to fix canvas capturestream support in the OMX backend
+skip-if = android_version < '17' # Android/Gonk before SDK version 17 does not have the OMX Encoder API.
 tags=msg
 [test_mediarecorder_record_gum_video_timeslice.html]
 tags=msg
 [test_mediarecorder_record_immediate_stop.html]
 tags=msg capturestream
 [test_mediarecorder_record_no_timeslice.html]
 tags=msg capturestream
 [test_mediarecorder_record_nosrc.html]
--- a/dom/media/test/test_can_play_type_mpeg.html
+++ b/dom/media/test/test_can_play_type_mpeg.html
@@ -119,33 +119,29 @@ function getPref(name) {
   } catch(ex) { }
   return pref;
 }
 
 function IsJellyBeanOrLater() {
   return androidVersion >= 16;
 }
 
-// Check whether we should expect the new MP4Reader-based support to work.
-function IsMP4ReaderAvailable() {
-  var prefs = getPref("media.fragmented-mp4.enabled");
-  return prefs && (IsWindowsVistaOrLater() || IsMacOSSnowLeopardOrLater() || IsJellyBeanOrLater());
-}
-
-var haveMp4 = IsWindowsVistaOrLater() ||
+var haveMp4 = (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) ||
+              IsMacOSSnowLeopardOrLater() ||
+              IsJellyBeanOrLater() ||
               getPref("media.omx.enabled") ||
               getPref("media.gstreamer.enabled") ||
-              IsMP4ReaderAvailable();
-// TODO:  Add "getPref("media.plugins.enabled")" once MP4 works on Gingerbread.
+              getPref("media.ffmpeg.enabled");
 
 check_mp4(document.getElementById('v'), haveMp4);
 
 var haveMp3 = getPref("media.directshow.enabled") ||
-              (getPref("media.windows-media-foundation.enabled") && IsWindowsVistaOrLater()) ||
-               getPref("media.gstreamer.enabled") ||
-               getPref("media.apple.mp3.enabled");
+              (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) ||
+              getPref("media.gstreamer.enabled") ||
+              getPref("media.ffmpeg.enabled") ||
+              getPref("media.apple.mp3.enabled");
 check_mp3(document.getElementById('v'), haveMp3);
 
 mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_gmp_playback.html
+++ b/dom/media/test/test_gmp_playback.html
@@ -22,19 +22,19 @@ function startTest() {
     ok(true, "Reached end");
     SimpleTest.finish();
   });
   document.body.appendChild(v);
   v.play();
 }
 
 var testPrefs = [
-  ['media.fragmented-mp4.gmp.aac', 1],  // gmp-clearkey
-  ['media.fragmented-mp4.gmp.h264', 1], // gmp-clearkey
-  ['media.fragmented-mp4.gmp.enabled', true]
+  ['media.gmp.decoder.aac', 1],  // gmp-clearkey
+  ['media.gmp.decoder.h264', 1], // gmp-clearkey
+  ['media.gmp.decoder.enabled', true]
 ];
 
 SpecialPowers.pushPrefEnv({'set': testPrefs}, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -24,19 +24,16 @@ try {
  * This class provides helpers around analysing the audio content in a stream
  * using WebAudio AnalyserNodes.
  *
  * @constructor
  * @param {object} stream
  *                 A MediaStream object whose audio track we shall analyse.
  */
 function AudioStreamAnalyser(ac, stream) {
-  if (stream.getAudioTracks().length === 0) {
-    throw new Error("No audio track in stream");
-  }
   this.audioContext = ac;
   this.stream = stream;
   this.sourceNode = this.audioContext.createMediaStreamSource(this.stream);
   this.analyser = this.audioContext.createAnalyser();
   this.sourceNode.connect(this.analyser);
   this.data = new Uint8Array(this.analyser.frequencyBinCount);
 }
 
@@ -51,39 +48,54 @@ AudioStreamAnalyser.prototype = {
     return this.data;
   },
 
   /**
    * Append a canvas to the DOM where the frequency data are drawn.
    * Useful to debug tests.
    */
   enableDebugCanvas: function() {
-    var cvs = document.createElement("canvas");
+    var cvs = this.debugCanvas = document.createElement("canvas");
     document.getElementById("content").appendChild(cvs);
 
     // Easy: 1px per bin
     cvs.width = this.analyser.frequencyBinCount;
     cvs.height = 256;
     cvs.style.border = "1px solid red";
 
     var c = cvs.getContext('2d');
 
     var self = this;
     function render() {
       c.clearRect(0, 0, cvs.width, cvs.height);
       var array = self.getByteFrequencyData();
       for (var i = 0; i < array.length; i++) {
         c.fillRect(i, (256 - (array[i])), 1, 256);
       }
-      requestAnimationFrame(render);
+      if (!cvs.stopDrawing) {
+        requestAnimationFrame(render);
+      }
     }
     requestAnimationFrame(render);
   },
 
   /**
+   * Stop drawing of and remove the debug canvas from the DOM if it was
+   * previously added.
+   */
+  disableDebugCanvas: function() {
+    if (!this.debugCanvas || !this.debugCanvas.parentElement) {
+      return;
+    }
+
+    this.debugCanvas.stopDrawing = true;
+    this.debugCanvas.parentElement.removeChild(this.debugCanvas);
+  },
+
+  /**
    * Return a Promise, that will be resolved when the function passed as
    * argument, when called, returns true (meaning the analysis was a
    * success).
    *
    * @param {function} analysisFunction
    *        A fonction that performs an analysis, and returns true if the
    *        analysis was a success (i.e. it found what it was looking for)
    */
@@ -125,16 +137,36 @@ AudioStreamAnalyser.prototype = {
   frequencyForBinIndex: function(index) {
     return (index - 1) *
            this.audioContext.sampleRate /
            this.analyser.fftSize;
   }
 };
 
 /**
+ * Creates a MediaStream with an audio track containing a sine tone at the
+ * given frequency.
+ *
+ * @param {AudioContext} ac
+ *        AudioContext in which to create the OscillatorNode backing the stream
+ * @param {double} frequency
+ *        The frequency in Hz of the generated sine tone
+ * @returns {MediaStream} the MediaStream containing sine tone audio track
+ */
+function createOscillatorStream(ac, frequency) {
+  var osc = ac.createOscillator();
+  osc.frequency.value = frequency;
+
+  var oscDest = ac.createMediaStreamDestination();
+  osc.connect(oscDest);
+  osc.start();
+  return oscDest.stream;
+}
+
+/**
  * Create the necessary HTML elements for head and body as used by Mochitests
  *
  * @param {object} meta
  *        Meta information of the test
  * @param {string} meta.title
  *        Description of the test
  * @param {string} [meta.bug]
  *        Bug the test was created for
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -46,16 +46,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_getUserMedia_basicWindowshare.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
 [test_getUserMedia_constraints.html]
 [test_getUserMedia_callbacks.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || buildapp == 'mulet' # Bug 1063290, intermittent timeout # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
 [test_getUserMedia_gumWithinGum.html]
+[test_getUserMedia_mediaStreamConstructors.html]
 [test_getUserMedia_playAudioTwice.html]
 [test_getUserMedia_playVideoAudioTwice.html]
 [test_getUserMedia_playVideoTwice.html]
 [test_getUserMedia_spinEventLoop.html]
 skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # copied from basicAudio
 [test_getUserMedia_stopAudioStream.html]
 [test_getUserMedia_stopAudioStreamWithFollowupAudio.html]
 [test_getUserMedia_stopVideoAudioStream.html]
--- a/dom/media/tests/mochitest/test_getUserMedia_addTrackRemoveTrack.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_addTrackRemoveTrack.html
@@ -100,26 +100,16 @@
 
         var elem = createMediaElement('video', 'testAddRemoveOriginalTrackVideo');
         var playback = new LocalMediaStreamPlayback(elem, audioStream);
         return playback.playMediaWithMediaStreamTracksStop(false);
       }))
     .then(() => {
       var ac = new AudioContext();
 
-      function createOscillatorStream(ac, frequency) {
-        var osc = ac.createOscillator();
-        osc.frequency.value = frequency;
-
-        var oscDest = ac.createMediaStreamDestination();
-        osc.connect(oscDest);
-        osc.start();
-        return oscDest.stream;
-      }
-
       var osc1k = createOscillatorStream(ac, 1000);
       var audioTrack1k = osc1k.getTracks()[0];
 
       var osc5k = createOscillatorStream(ac, 5000);
       var audioTrack5k = osc5k.getTracks()[0];
 
       var osc10k = createOscillatorStream(ac, 10000);
       var audioTrack10k = osc10k.getTracks()[0];
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_mediaStreamConstructors.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  "use strict";
+
+  createHTML({
+    title: "MediaStream constructors with getUserMedia streams Test",
+    bug: "1070216",
+    visible: true
+  });
+
+  var audioContext = new AudioContext();
+  var videoElement;
+
+  runTest(() => Promise.resolve()
+    .then(() => videoElement = createMediaElement('video', 'constructorsTest'))
+    .then(() => getUserMedia({video: true})).then(gUMStream => {
+      info("Test default constructor with video");
+      var track = gUMStream.getTracks()[0];
+
+      var stream = new MediaStream();
+      checkMediaStreamContains(stream, [], "Default constructed stream");
+
+      stream.addTrack(track);
+      checkMediaStreamContains(stream, [track], "Added video track");
+
+      var playback = new MediaStreamPlayback(videoElement, stream);
+      return playback.playMedia(false).then(() => gUMStream.stop());
+    })
+    .then(() => getUserMedia({video: true})).then(gUMStream => {
+      info("Test copy constructor with gUM stream");
+      var track = gUMStream.getTracks()[0];
+
+      var stream = new MediaStream(gUMStream);
+      checkMediaStreamContains(stream, [track], "Copy constructed video track");
+
+      var playback = new MediaStreamPlayback(videoElement, stream);
+      return playback.playMedia(false).then(() => gUMStream.stop());
+    })
+    .then(() => getUserMedia({video: true})).then(gUMStream => {
+      info("Test list constructor with empty list");
+      var track = gUMStream.getTracks()[0];
+
+      var stream = new MediaStream([]);
+      checkMediaStreamContains(stream, [], "Empty-list constructed stream");
+
+      stream.addTrack(track);
+      checkMediaStreamContains(stream, [track], "Added video track");
+
+      var playback = new MediaStreamPlayback(videoElement, stream);
+      return playback.playMedia(false).then(() => gUMStream.stop());
+    })
+    .then(() => getUserMedia({audio: true, video: true})).then(gUMStream => {
+      info("Test list constructor with a gUM audio/video stream");
+      var audioTrack = gUMStream.getAudioTracks()[0];
+      var videoTrack = gUMStream.getVideoTracks()[0];
+
+      var stream = new MediaStream([audioTrack, videoTrack]);
+      checkMediaStreamContains(stream, [audioTrack, videoTrack],
+                               "List constructed audio and video tracks");
+
+      var playback = new MediaStreamPlayback(videoElement, stream);
+      return playback.playMedia(false).then(() => gUMStream.stop());
+    })
+    .then(() => getUserMedia({video: true})).then(gUMStream => {
+      info("Test list constructor with gUM-video and WebAudio tracks");
+      var audioStream = createOscillatorStream(audioContext, 2000);
+      var audioTrack = audioStream.getTracks()[0];
+      var videoTrack = gUMStream.getTracks()[0];
+
+      var stream = new MediaStream([audioTrack, videoTrack]);
+      checkMediaStreamContains(stream, [audioTrack, videoTrack],
+                               "List constructed WebAudio and gUM-video tracks");
+
+      var playback = new MediaStreamPlayback(videoElement, stream);
+      return playback.playMedia(false).then(() => gUMStream.stop());
+    })
+    .then(() => {
+      var osc1k = createOscillatorStream(audioContext, 1000);
+      var audioTrack1k = osc1k.getTracks()[0];
+
+      var osc5k = createOscillatorStream(audioContext, 5000);
+      var audioTrack5k = osc5k.getTracks()[0];
+
+      var osc10k = createOscillatorStream(audioContext, 10000);
+      var audioTrack10k = osc10k.getTracks()[0];
+
+      return Promise.resolve().then(() => {
+        info("Analysing audio output with empty default constructed stream");
+        var stream = new MediaStream();
+        var analyser = new AudioStreamAnalyser(audioContext, stream);
+        analyser.enableDebugCanvas();
+        return analyser.waitForAnalysisSuccess(array =>
+          array[analyser.binIndexForFrequency(1000)]  < 50 &&
+          array[analyser.binIndexForFrequency(5000)]  < 50 &&
+          array[analyser.binIndexForFrequency(10000)] < 50)
+          .then(() => analyser.disableDebugCanvas());
+      }).then(() => {
+        info("Analysing audio output with copy constructed 5k stream");
+        var stream = new MediaStream(osc5k);
+        var analyser = new AudioStreamAnalyser(audioContext, stream);
+        analyser.enableDebugCanvas();
+        return analyser.waitForAnalysisSuccess(array =>
+          array[analyser.binIndexForFrequency(1000)]  < 50 &&
+          array[analyser.binIndexForFrequency(5000)]  > 200 &&
+          array[analyser.binIndexForFrequency(10000)] < 50)
+          .then(() => analyser.disableDebugCanvas());
+      }).then(() => {
+        info("Analysing audio output with empty-list constructed stream");
+        var stream = new MediaStream([]);
+        var analyser = new AudioStreamAnalyser(audioContext, stream);
+        analyser.enableDebugCanvas();
+        return analyser.waitForAnalysisSuccess(array =>
+          array[analyser.binIndexForFrequency(1000)]  < 50 &&
+          array[analyser.binIndexForFrequency(5000)]  < 50 &&
+          array[analyser.binIndexForFrequency(10000)] < 50)
+          .then(() => analyser.disableDebugCanvas());
+      }).then(() => {
+        info("Analysing audio output with list constructed 1k, 5k and 10k tracks");
+        var stream = new MediaStream([audioTrack1k, audioTrack5k, audioTrack10k]);
+        var analyser = new AudioStreamAnalyser(audioContext, stream);
+        analyser.enableDebugCanvas();
+        return analyser.waitForAnalysisSuccess(array =>
+          array[analyser.binIndexForFrequency(50)]    < 50 &&
+          array[analyser.binIndexForFrequency(1000)]  > 200 &&
+          array[analyser.binIndexForFrequency(2500)]  < 50 &&
+          array[analyser.binIndexForFrequency(5000)]  > 200 &&
+          array[analyser.binIndexForFrequency(7500)]  < 50 &&
+          array[analyser.binIndexForFrequency(10000)] > 200 &&
+          array[analyser.binIndexForFrequency(11000)] < 50)
+          .then(() => analyser.disableDebugCanvas());
+      });
+    }));
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -183,19 +183,31 @@ public:
   virtual uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) = 0;
 
 protected:
   // Only class' own members can be initialized in constructor initializer list.
   explicit MediaEngineSource(MediaEngineState aState)
     : mState(aState)
+#ifdef DEBUG
+    , mOwningThread(PR_GetCurrentThread())
+#endif
     , mHasFakeTracks(false)
   {}
+
+  void AssertIsOnOwningThread()
+  {
+    MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+  }
+
   MediaEngineState mState;
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
   bool mHasFakeTracks;
 };
 
 /**
  * Video source and friends.
  */
 class MediaEnginePrefs {
 public:
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -23,16 +23,17 @@ public:
   explicit MediaEngineCameraVideoSource(int aIndex,
                                         const char* aMonitorName = "Camera.Monitor")
     : MediaEngineVideoSource(kReleased)
     , mMonitor(aMonitorName)
     , mWidth(0)
     , mHeight(0)
     , mInitDone(false)
     , mHasDirectListeners(false)
+    , mNrAllocations(0)
     , mCaptureIndex(aIndex)
     , mTrackID(0)
   {}
 
 
   virtual void GetName(nsAString& aName) override;
   virtual void GetUUID(nsACString& aUUID) override;
   virtual void SetDirectListeners(bool aHasListeners) override;
@@ -110,16 +111,17 @@ static void LogCapability(const char* aH
   nsRefPtr<layers::Image> mImage;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   int mWidth, mHeight; // protected with mMonitor on Gonk due to different threading
   // end of data protected by mMonitor
 
 
   bool mInitDone;
   bool mHasDirectListeners;
+  int mNrAllocations; // When this becomes 0, we shut down HW
   int mCaptureIndex;
   TrackID mTrackID;
 
   webrtc::CaptureCapability mCapability;
 
   nsTArray<webrtc::CaptureCapability> mHardcodedCapabilities; // For OSX & B2G
 private:
   nsString mDeviceName;
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -91,18 +91,24 @@ MediaEngineRemoteVideoSource::Shutdown()
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                        const MediaEnginePrefs& aPrefs,
                                        const nsString& aDeviceId)
 {
   LOG((__PRETTY_FUNCTION__));
+  AssertIsOnOwningThread();
 
-  if (mState == kReleased && mInitDone) {
+  if (!mInitDone) {
+    LOG(("Init not done"));
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mState == kReleased) {
     // Note: if shared, we don't allow a later opener to affect the resolution.
     // (This may change depending on spec changes for Constraints/settings)
 
     if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     if (mozilla::camera::AllocateCaptureDevice(mCapEngine,
@@ -116,45 +122,48 @@ MediaEngineRemoteVideoSource::Allocate(c
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
       LOG(("Video device %d reallocated", mCaptureIndex));
     } else {
       LOG(("Video device %d allocated shared", mCaptureIndex));
     }
   }
 
+  ++mNrAllocations;
+
   return NS_OK;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Deallocate()
 {
-  LOG((__FUNCTION__));
-  bool empty;
-  {
-    MonitorAutoLock lock(mMonitor);
-    empty = mSources.IsEmpty();
-  }
-  if (empty) {
+  LOG((__PRETTY_FUNCTION__));
+  AssertIsOnOwningThread();
+
+  --mNrAllocations;
+  MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
+
+  if (mNrAllocations == 0) {
     if (mState != kStopped && mState != kAllocated) {
       return NS_ERROR_FAILURE;
     }
     mozilla::camera::ReleaseCaptureDevice(mCapEngine, mCaptureIndex);
     mState = kReleased;
     LOG(("Video device %d deallocated", mCaptureIndex));
   } else {
     LOG(("Video device %d deallocated but still in use", mCaptureIndex));
   }
   return NS_OK;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
 {
   LOG((__PRETTY_FUNCTION__));
+  AssertIsOnOwningThread();
   if (!mInitDone || !aStream) {
     LOG(("No stream or init not done"));
     return NS_ERROR_FAILURE;
   }
 
   {
     MonitorAutoLock lock(mMonitor);
     mSources.AppendElement(aStream);
@@ -179,16 +188,17 @@ MediaEngineRemoteVideoSource::Start(Sour
   return NS_OK;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
                                    mozilla::TrackID aID)
 {
   LOG((__PRETTY_FUNCTION__));
+  AssertIsOnOwningThread();
   {
     MonitorAutoLock lock(mMonitor);
 
     if (!mSources.RemoveElement(aSource)) {
       // Already stopped - this is allowed
       return NS_OK;
     }
 
@@ -212,16 +222,17 @@ MediaEngineRemoteVideoSource::Stop(mozil
   return NS_OK;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
                                       const MediaEnginePrefs& aPrefs,
                                       const nsString& aDeviceId)
 {
+  AssertIsOnOwningThread();
   if (!mInitDone) {
     LOG(("Init not done"));
     return NS_ERROR_FAILURE;
   }
   if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   if (mState != kStarted) {
@@ -387,16 +398,17 @@ MediaEngineRemoteVideoSource::NumCapabil
   return mHardcodedCapabilities.Length();
 }
 
 bool
 MediaEngineRemoteVideoSource::ChooseCapability(const MediaTrackConstraints &aConstraints,
     const MediaEnginePrefs &aPrefs,
     const nsString& aDeviceId)
 {
+  AssertIsOnOwningThread();
 
   switch(mMediaSource) {
     case dom::MediaSourceEnum::Screen:
     case dom::MediaSourceEnum::Window:
     case dom::MediaSourceEnum::Application: {
       FlattenedConstraints c(aConstraints);
       mCapability.width = c.mWidth.Clamp(c.mWidth.mIdeal.WasPassed() ?
         c.mWidth.mIdeal.Value() : aPrefs.mWidth);
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -130,16 +130,17 @@ public:
                                     const char* name,
                                     const char* uuid)
     : MediaEngineAudioSource(kReleased)
     , mVoiceEngine(aVoiceEnginePtr)
     , mMonitor("WebRTCMic.Monitor")
     , mThread(aThread)
     , mCapIndex(aIndex)
     , mChannel(-1)
+    , mNrAllocations(0)
     , mInitDone(false)
     , mStarted(false)
     , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
     , mEchoOn(false), mAgcOn(false), mNoiseOn(false)
     , mEchoCancel(webrtc::kEcDefault)
     , mAGC(webrtc::kAgcDefault)
     , mNoiseSuppress(webrtc::kNsDefault)
     , mPlayoutDelay(0)
@@ -210,20 +211,21 @@ private:
   ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender;
   ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork;
   ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing;
 
   // mMonitor protects mSources[] access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack()).
   // mSources[] is accessed from webrtc threads.
   Monitor mMonitor;
-  nsTArray<nsRefPtr<SourceMediaStream>> mSources; // When this goes empty, we shut down HW
+  nsTArray<nsRefPtr<SourceMediaStream>> mSources;
   nsCOMPtr<nsIThread> mThread;
   int mCapIndex;
   int mChannel;
+  int mNrAllocations; // When this becomes 0, we shut down HW
   TrackID mTrackID;
   bool mInitDone;
   bool mStarted;
 
   nsString mDeviceName;
   nsCString mDeviceUUID;
 
   uint32_t mSampleFrequency;
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -280,16 +280,17 @@ uint32_t MediaEngineWebRTCMicrophoneSour
   return distance;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                             const MediaEnginePrefs &aPrefs,
                                             const nsString& aDeviceId)
 {
+  AssertIsOnOwningThread();
   if (mState == kReleased) {
     if (mInitDone) {
       ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw(webrtc::VoEHardware::GetInterface(mVoiceEngine));
       if (!ptrVoEHw || ptrVoEHw->SetRecordingDevice(mCapIndex)) {
         return NS_ERROR_FAILURE;
       }
       mState = kAllocated;
       LOG(("Audio device %d allocated", mCapIndex));
@@ -300,28 +301,27 @@ MediaEngineWebRTCMicrophoneSource::Alloc
   } else if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
       LOG(("Audio device %d reallocated", mCapIndex));
     } else {
       LOG(("Audio device %d allocated shared", mCapIndex));
     }
   }
+  ++mNrAllocations;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Deallocate()
 {
-  bool empty;
-  {
-    MonitorAutoLock lock(mMonitor);
-    empty = mSources.IsEmpty();
-  }
-  if (empty) {
+  AssertIsOnOwningThread();
+  --mNrAllocations;
+  MOZ_ASSERT(mNrAllocations >= 0, "Double-deallocations are prohibited");
+  if (mNrAllocations == 0) {
     // If empty, no callbacks to deliver data should be occuring
     if (mState != kStopped && mState != kAllocated) {
       return NS_ERROR_FAILURE;
     }
 
     mState = kReleased;
     LOG(("Audio device %d deallocated", mCapIndex));
   } else {
@@ -329,16 +329,17 @@ MediaEngineWebRTCMicrophoneSource::Deall
   }
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
                                          TrackID aID)
 {
+  AssertIsOnOwningThread();
   if (!mInitDone || !aStream) {
     return NS_ERROR_FAILURE;
   }
 
   {
     MonitorAutoLock lock(mMonitor);
     mSources.AppendElement(aStream);
   }
@@ -382,16 +383,17 @@ MediaEngineWebRTCMicrophoneSource::Start
   mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
+  AssertIsOnOwningThread();
   {
     MonitorAutoLock lock(mMonitor);
 
     if (!mSources.RemoveElement(aSource)) {
       // Already stopped - this is allowed
       return NS_OK;
     }
 
@@ -658,24 +660,26 @@ MediaEngineWebRTCAudioCaptureSource::Get
   // Remove {} and the null terminator
   aUUID.Assign(Substring(asciiString, 1, NSID_LENGTH - 3));
 }
 
 nsresult
 MediaEngineWebRTCAudioCaptureSource::Start(SourceMediaStream *aMediaStream,
                                            TrackID aId)
 {
+  AssertIsOnOwningThread();
   aMediaStream->AddTrack(aId, 0, new AudioSegment());
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCAudioCaptureSource::Stop(SourceMediaStream *aMediaStream,
                                           TrackID aId)
 {
+  AssertIsOnOwningThread();
   aMediaStream->EndAllTrackAndFinish();
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCAudioCaptureSource::Restart(
     const dom::MediaTrackConstraints& aConstraints,
     const MediaEnginePrefs &aPrefs,
--- a/dom/network/TCPSocket.cpp
+++ b/dom/network/TCPSocket.cpp
@@ -1035,17 +1035,17 @@ TCPSocket::OnDataAvailable(nsIRequest* a
 
     AutoJSAPI api;
     if (!api.Init(GetOwnerGlobal())) {
       return NS_ERROR_FAILURE;
     }
     JSContext* cx = api.cx();
 
     JS::Rooted<JS::Value> value(cx);
-    if (!ToJSValue(cx, TypedArrayCreator<Uint8Array>(buffer), &value)) {
+    if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(buffer), &value)) {
       return NS_ERROR_FAILURE;
     }
     FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
     return NS_OK;
   }
 
   nsCString data;
   nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
--- a/dom/network/tests/tcpsocket_test.jsm
+++ b/dom/network/tests/tcpsocket_test.jsm
@@ -1,13 +1,20 @@
-this.EXPORTED_SYMBOLS = ['createSocket', 'createServer', 'enablePrefsAndPermissions'];
+this.EXPORTED_SYMBOLS = [
+  'createSocket', 'createServer', 'enablePrefsAndPermissions',
+  'socketCompartmentInstanceOfArrayBuffer'];
 
 this.createSocket = function(host, port, options) {
   return new TCPSocket(host, port, options);
 }
 
 this.createServer = function(port, options, backlog) {
   return new TCPServerSocket(port, options, backlog);
 }
 
 this.enablePrefsAndPermissions = function() {
   return false;
 }
+
+// See test_tcpsocket_client_and_server_basics.html's version for rationale.
+this.socketCompartmentInstanceOfArrayBuffer = function(obj) {
+  return obj instanceof ArrayBuffer;
+}
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.html
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
@@ -20,16 +20,27 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     function createSocket(host, port, options) {
       return new TCPSocket(host, port, options);
     }
 
     function enablePrefsAndPermissions() {
       return true;
     }
+
+    // In the JSM case, ArrayBuffers will be created in the compartment of the
+    // JSM with different globals than the
+    // test_tcpsocket_client_and_server_basics.js test logic sees, so we (and
+    // tcpsocket_test.jsm) need to do something.  To avoid complexity relating
+    // to wrappers and the varying nuances of the module scope and global scope
+    // in JSM's (they differ on B2G), we hardcode ArrayBuffer rather than taking
+    // a string that we look up, etc.
+    function socketCompartmentInstanceOfArrayBuffer(obj) {
+      return obj instanceof ArrayBuffer;
+    }
   </script>
   <script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -50,16 +50,18 @@ function listenForEventsOnSocket(socket,
 
   socket.onopen = handleGenericEvent;
   socket.ondrain = handleGenericEvent;
   socket.onerror = handleGenericEvent;
   socket.onclose = handleGenericEvent;
   socket.ondata = function(event) {
     dump('(' + socketType + ' event: ' + event.type + ' length: ' +
          event.data.byteLength + ')\n');
+    ok(socketCompartmentInstanceOfArrayBuffer(event.data),
+       'payload is ArrayBuffer');
     var arr = new Uint8Array(event.data);
     if (receivedData === null) {
       receivedData = arr;
     } else {
       receivedData = concatUint8Arrays(receivedData, arr);
     }
     if (wantDataLength !== null &&
         receivedData.length >= wantDataLength) {
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -22,20 +22,20 @@ dictionary MediaStreamConstraints {
                         // wave if audio is enabled.
     boolean fakeTracks; // For testing purpose, works only if fake is
                         // enabled. Enable fakeTracks returns a stream
                         // with two extra empty video tracks and three
                         // extra empty audio tracks.
     DOMString? peerIdentity = null;
 };
 
-// [Exposed=Window,
-//  Constructor,
-//  Constructor (MediaStream stream),
-//  Constructor (sequence<MediaStreamTrack> tracks)]
+[Exposed=Window,
+ Constructor,
+ Constructor (MediaStream stream),
+ Constructor (sequence<MediaStreamTrack> tracks)]
 interface MediaStream : EventTarget {
     readonly    attribute DOMString    id;
     sequence<AudioStreamTrack> getAudioTracks ();
     sequence<VideoStreamTrack> getVideoTracks ();
     sequence<MediaStreamTrack> getTracks ();
     // MediaStreamTrack?          getTrackById (DOMString trackId);
     void                       addTrack (MediaStreamTrack track);
     void                       removeTrack (MediaStreamTrack track);
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -86,17 +86,17 @@ static void
  * @destroy: Callback to call when @data is not needed anymore.
  *
  * Creates a new "blob" object wrapping @data.  The @mode parameter is used
  * to negotiate ownership and lifecycle of @data.
  *
  * Return value: New blob, or the empty blob if something failed or if @length is
  * zero.  Destroy with hb_blob_destroy().
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_blob_create (const char        *data,
 		unsigned int       length,
 		hb_memory_mode_t   mode,
 		void              *user_data,
 		hb_destroy_func_t  destroy)
 {
@@ -142,17 +142,17 @@ hb_blob_create (const char        *data,
  * is.
  *
  * Makes @parent immutable.
  *
  * Return value: New blob, or the empty blob if something failed or if
  * @length is zero or @offset is beyond the end of @parent's data.  Destroy
  * with hb_blob_destroy().
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_blob_create_sub_blob (hb_blob_t    *parent,
 			 unsigned int  offset,
 			 unsigned int  length)
 {
   hb_blob_t *blob;
 
@@ -174,17 +174,17 @@ hb_blob_create_sub_blob (hb_blob_t    *p
  * hb_blob_get_empty:
  *
  * Returns the singleton empty blob.
  *
  * See TODO:link object types for more information.
  *
  * Return value: (transfer full): the empty blob.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_blob_get_empty (void)
 {
   static const hb_blob_t _hb_blob_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     true, /* immutable */
@@ -205,17 +205,17 @@ hb_blob_get_empty (void)
  * @blob: a blob.
  *
  * Increases the reference count on @blob.
  *
  * See TODO:link object types for more information.
  *
  * Return value: @blob.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_blob_reference (hb_blob_t *blob)
 {
   return hb_object_reference (blob);
 }
 
 /**
@@ -223,17 +223,17 @@ hb_blob_reference (hb_blob_t *blob)
  * @blob: a blob.
  *
  * Descreases the reference count on @blob, and if it reaches zero, destroys
  * @blob, freeing all memory, possibly calling the destroy-callback the blob
  * was created for if it has not been called already.
  *
  * See TODO:link object types for more information.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_blob_destroy (hb_blob_t *blob)
 {
   if (!hb_object_destroy (blob)) return;
 
   _hb_blob_destroy_user_data (blob);
 
@@ -245,17 +245,17 @@ hb_blob_destroy (hb_blob_t *blob)
  * @blob: a blob.
  * @key: key for data to set.
  * @data: data to set.
  * @destroy: callback to call when @data is not needed anymore.
  * @replace: whether to replace an existing data with the same key.
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_blob_set_user_data (hb_blob_t          *blob,
 		       hb_user_data_key_t *key,
 		       void *              data,
 		       hb_destroy_func_t   destroy,
 		       hb_bool_t           replace)
 {
@@ -266,33 +266,33 @@ hb_blob_set_user_data (hb_blob_t        
  * hb_blob_get_user_data: (skip)
  * @blob: a blob.
  * @key: key for data to get.
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_blob_get_user_data (hb_blob_t          *blob,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (blob, key);
 }
 
 
 /**
  * hb_blob_make_immutable:
  * @blob: a blob.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_blob_make_immutable (hb_blob_t *blob)
 {
   if (hb_object_is_inert (blob))
     return;
 
   blob->immutable = true;
@@ -301,51 +301,51 @@ hb_blob_make_immutable (hb_blob_t *blob)
 /**
  * hb_blob_is_immutable:
  * @blob: a blob.
  *
  * 
  *
  * Return value: TODO
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_blob_is_immutable (hb_blob_t *blob)
 {
   return blob->immutable;
 }
 
 
 /**
  * hb_blob_get_length:
  * @blob: a blob.
  *
  * 
  *
  * Return value: the length of blob data in bytes.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_blob_get_length (hb_blob_t *blob)
 {
   return blob->length;
 }
 
 /**
  * hb_blob_get_data:
  * @blob: a blob.
  * @length: (out):
  *
  * 
  *
  * Returns: (transfer none) (array length=length): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char *
 hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
 {
   if (length)
     *length = blob->length;
 
   return blob->data;
@@ -360,17 +360,17 @@ hb_blob_get_data (hb_blob_t *blob, unsig
  * return pointer to data.
  *
  * Fails if blob has been made immutable, or if memory allocation
  * fails.
  *
  * Returns: (transfer none) (array length=length): Writable blob data,
  * or %NULL if failed.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 char *
 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
 {
   if (!_try_writable (blob)) {
     if (length)
       *length = 0;
 
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc
+++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc
@@ -35,51 +35,51 @@ static const char *serialize_formats[] =
 
 /**
  * hb_buffer_serialize_list_formats:
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char **
 hb_buffer_serialize_list_formats (void)
 {
   return serialize_formats;
 }
 
 /**
  * hb_buffer_serialize_format_from_string:
  * @str: 
  * @len: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_buffer_serialize_format_t
 hb_buffer_serialize_format_from_string (const char *str, int len)
 {
   /* Upper-case it. */
   return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u);
 }
 
 /**
  * hb_buffer_serialize_format_to_string:
  * @format: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char *
 hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
 {
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
@@ -254,17 +254,17 @@ static unsigned int
  * @font: 
  * @format: 
  * @flags: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
 			    unsigned int start,
 			    unsigned int end,
 			    char *buf,
 			    unsigned int buf_size,
 			    unsigned int *buf_consumed, /* May be NULL */
@@ -361,17 +361,17 @@ parse_int (const char *pp, const char *e
  * @end_ptr: (out):
  * @font: 
  * @format: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
 			      const char *buf,
 			      int buf_len, /* -1 means nul-terminated */
 			      const char **end_ptr, /* May be NULL */
 			      hb_font_t *font, /* May be NULL */
 			      hb_buffer_serialize_format_t format)
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -699,17 +699,17 @@ void hb_buffer_t::deallocate_var_all (vo
 
 /**
  * hb_buffer_create: (Xconstructor)
  *
  * 
  *
  * Return value: (transfer full)
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_buffer_t *
 hb_buffer_create (void)
 {
   hb_buffer_t *buffer;
 
   if (!(buffer = hb_object_create<hb_buffer_t> ()))
     return hb_buffer_get_empty ();
@@ -721,17 +721,17 @@ hb_buffer_create (void)
 
 /**
  * hb_buffer_get_empty:
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 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),
@@ -754,31 +754,31 @@ hb_buffer_get_empty (void)
 /**
  * hb_buffer_reference: (skip)
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_buffer_t *
 hb_buffer_reference (hb_buffer_t *buffer)
 {
   return hb_object_reference (buffer);
 }
 
 /**
  * hb_buffer_destroy: (skip)
  * @buffer: a buffer.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_destroy (hb_buffer_t *buffer)
 {
   if (!hb_object_destroy (buffer)) return;
 
   hb_unicode_funcs_destroy (buffer->unicode);
 
@@ -795,17 +795,17 @@ hb_buffer_destroy (hb_buffer_t *buffer)
  * @data: 
  * @destroy: 
  * @replace: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_buffer_set_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key,
 			 void *              data,
 			 hb_destroy_func_t   destroy,
 			 hb_bool_t           replace)
 {
@@ -816,17 +816,17 @@ hb_buffer_set_user_data (hb_buffer_t    
  * hb_buffer_get_user_data: (skip)
  * @buffer: a buffer.
  * @key: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (buffer, key);
 }
 
@@ -866,17 +866,17 @@ hb_buffer_get_content_type (hb_buffer_t 
 
 /**
  * hb_buffer_set_unicode_funcs:
  * @buffer: a buffer.
  * @unicode_funcs: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode_funcs)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -892,32 +892,32 @@ hb_buffer_set_unicode_funcs (hb_buffer_t
 /**
  * hb_buffer_get_unicode_funcs:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_unicode_funcs_t *
 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
 {
   return buffer->unicode;
 }
 
 /**
  * hb_buffer_set_direction:
  * @buffer: a buffer.
  * @direction: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_set_direction (hb_buffer_t    *buffer,
 			 hb_direction_t  direction)
 
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
@@ -928,32 +928,32 @@ hb_buffer_set_direction (hb_buffer_t    
 /**
  * hb_buffer_get_direction:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_direction_t
 hb_buffer_get_direction (hb_buffer_t    *buffer)
 {
   return buffer->props.direction;
 }
 
 /**
  * hb_buffer_set_script:
  * @buffer: a buffer.
  * @script: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_set_script (hb_buffer_t *buffer,
 		      hb_script_t  script)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -963,32 +963,32 @@ hb_buffer_set_script (hb_buffer_t *buffe
 /**
  * hb_buffer_get_script:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_script_t
 hb_buffer_get_script (hb_buffer_t *buffer)
 {
   return buffer->props.script;
 }
 
 /**
  * hb_buffer_set_language:
  * @buffer: a buffer.
  * @language: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_set_language (hb_buffer_t   *buffer,
 			hb_language_t  language)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
@@ -998,17 +998,17 @@ hb_buffer_set_language (hb_buffer_t   *b
 /**
  * hb_buffer_get_language:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_language_t
 hb_buffer_get_language (hb_buffer_t *buffer)
 {
   return buffer->props.language;
 }
 
 /**
@@ -1155,17 +1155,17 @@ hb_buffer_get_replacement_codepoint (hb_
 
 
 /**
  * hb_buffer_reset:
  * @buffer: a buffer.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_reset (hb_buffer_t *buffer)
 {
   buffer->reset ();
 }
 
 /**
@@ -1186,49 +1186,49 @@ hb_buffer_clear_contents (hb_buffer_t *b
  * hb_buffer_pre_allocate:
  * @buffer: a buffer.
  * @size: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
 {
   return buffer->ensure (size);
 }
 
 /**
  * hb_buffer_allocation_successful:
  * @buffer: a buffer.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_buffer_allocation_successful (hb_buffer_t  *buffer)
 {
   return !buffer->in_error;
 }
 
 /**
  * hb_buffer_add:
  * @buffer: a buffer.
  * @codepoint: 
  * @cluster: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.7
  **/
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
 	       unsigned int    cluster)
 {
   buffer->add (codepoint, cluster);
   buffer->clear_context (1);
@@ -1238,17 +1238,17 @@ hb_buffer_add (hb_buffer_t    *buffer,
  * hb_buffer_set_length:
  * @buffer: a buffer.
  * @length: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return length == 0;
 
@@ -1277,17 +1277,17 @@ hb_buffer_set_length (hb_buffer_t  *buff
 /**
  * hb_buffer_get_length:
  * @buffer: a buffer.
  *
  * Returns the number of items in the buffer.
  *
  * Return value: buffer length.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_buffer_get_length (hb_buffer_t *buffer)
 {
   return buffer->len;
 }
 
 /**
@@ -1295,17 +1295,17 @@ hb_buffer_get_length (hb_buffer_t *buffe
  * @buffer: a buffer.
  * @length: (out): output array length.
  *
  * Returns buffer glyph information array.  Returned pointer
  * is valid as long as buffer contents are not modified.
  *
  * Return value: (transfer none) (array length=length): buffer glyph information array.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_glyph_info_t *
 hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
                            unsigned int *length)
 {
   if (length)
     *length = buffer->len;
 
@@ -1317,17 +1317,17 @@ hb_buffer_get_glyph_infos (hb_buffer_t  
  * @buffer: a buffer.
  * @length: (out): output length.
  *
  * Returns buffer glyph position array.  Returned pointer
  * is valid as long as buffer contents are not modified.
  *
  * Return value: (transfer none) (array length=length): buffer glyph position array.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_glyph_position_t *
 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
                                unsigned int *length)
 {
   if (!buffer->have_positions)
     buffer->clear_positions ();
 
@@ -1338,50 +1338,50 @@ hb_buffer_get_glyph_positions (hb_buffer
 }
 
 /**
  * hb_buffer_reverse:
  * @buffer: a buffer.
  *
  * Reverses buffer contents.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 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.
+ * Reverses buffer contents between start to end.
  *
- * Since: 1.0
+ * Since: 0.9.41
  **/
 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
+ * Since: 0.9.2
  **/
 void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer)
 {
   buffer->reverse_clusters ();
 }
 
 /**
@@ -1488,17 +1488,17 @@ hb_buffer_add_utf (hb_buffer_t  *buffer,
  * @buffer: a buffer.
  * @text: (array length=text_length) (element-type uint8_t):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
 		    const char   *text,
 		    int           text_length,
 		    unsigned int  item_offset,
 		    int           item_length)
 {
@@ -1510,17 +1510,17 @@ hb_buffer_add_utf8 (hb_buffer_t  *buffer
  * @buffer: a buffer.
  * @text: (array length=text_length):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_add_utf16 (hb_buffer_t    *buffer,
 		     const uint16_t *text,
 		     int             text_length,
 		     unsigned int    item_offset,
 		     int             item_length)
 {
@@ -1532,17 +1532,17 @@ hb_buffer_add_utf16 (hb_buffer_t    *buf
  * @buffer: a buffer.
  * @text: (array length=text_length):
  * @text_length: 
  * @item_offset: 
  * @item_length: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_buffer_add_utf32 (hb_buffer_t    *buffer,
 		     const uint32_t *text,
 		     int             text_length,
 		     unsigned int    item_offset,
 		     int             item_length)
 {
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -166,30 +166,36 @@ hb_buffer_set_segment_properties (hb_buf
 void
 hb_buffer_get_segment_properties (hb_buffer_t *buffer,
 				  hb_segment_properties_t *props);
 
 void
 hb_buffer_guess_segment_properties (hb_buffer_t *buffer);
 
 
+/*
+ * Since: 0.9.20
+ */
 typedef enum { /*< flags >*/
   HB_BUFFER_FLAG_DEFAULT			= 0x00000000u,
   HB_BUFFER_FLAG_BOT				= 0x00000001u, /* Beginning-of-text */
   HB_BUFFER_FLAG_EOT				= 0x00000002u, /* End-of-text */
   HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES	= 0x00000004u
 } hb_buffer_flags_t;
 
 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);
 
+/*
+ * Since: 0.9.42
+ */
 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
@@ -314,16 +320,19 @@ hb_buffer_get_glyph_positions (hb_buffer
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
 
 
 /*
  * Serialize
  */
 
+/*
+ * Since: 0.9.20
+ */
 typedef enum { /*< flags >*/
   HB_BUFFER_SERIALIZE_FLAG_DEFAULT		= 0x00000000u,
   HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS		= 0x00000001u,
   HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS		= 0x00000002u,
   HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004u,
   HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u
 } hb_buffer_serialize_flags_t;
 
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -59,17 +59,17 @@ void
  * hb_tag_from_string:
  * @str: (array length=len) (element-type uint8_t): 
  * @len: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_tag_t
 hb_tag_from_string (const char *str, int len)
 {
   char tag[4];
   unsigned int i;
 
   if (!str || !len || !*str)
@@ -117,17 +117,17 @@ const char direction_strings[][4] = {
  * hb_direction_from_string:
  * @str: (array length=len) (element-type uint8_t): 
  * @len: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_direction_t
 hb_direction_from_string (const char *str, int len)
 {
   if (unlikely (!str || !len || !*str))
     return HB_DIRECTION_INVALID;
 
   /* Lets match loosely: just match the first letter, such that
@@ -144,17 +144,17 @@ hb_direction_from_string (const char *st
 /**
  * hb_direction_to_string:
  * @direction: 
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char *
 hb_direction_to_string (hb_direction_t direction)
 {
   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
 	      < ARRAY_LENGTH (direction_strings)))
     return direction_strings[direction - HB_DIRECTION_LTR];
 
@@ -283,65 +283,65 @@ retry:
  * hb_language_from_string:
  * @str: (array length=len) (element-type uint8_t): 
  * @len: 
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_language_t
 hb_language_from_string (const char *str, int len)
 {
-  char strbuf[64];
-
   if (!str || !len || !*str)
     return HB_LANGUAGE_INVALID;
 
+  hb_language_item_t *item = NULL;
   if (len >= 0)
   {
     /* NUL-terminate it. */
+    char strbuf[64];
     len = MIN (len, (int) sizeof (strbuf) - 1);
     memcpy (strbuf, str, len);
     strbuf[len] = '\0';
-    str = strbuf;
+    item = lang_find_or_insert (strbuf);
   }
-
-  hb_language_item_t *item = lang_find_or_insert (str);
+  else
+    item = lang_find_or_insert (str);
 
   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
 }
 
 /**
  * hb_language_to_string:
  * @language: 
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char *
 hb_language_to_string (hb_language_t language)
 {
   /* This is actually NULL-safe! */
   return language->s;
 }
 
 /**
  * hb_language_get_default:
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_language_t
 hb_language_get_default (void)
 {
   static hb_language_t default_language = HB_LANGUAGE_INVALID;
 
   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
   if (unlikely (language == HB_LANGUAGE_INVALID)) {
@@ -358,17 +358,17 @@ hb_language_get_default (void)
 /**
  * hb_script_from_iso15924_tag:
  * @tag: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_script_t
 hb_script_from_iso15924_tag (hb_tag_t tag)
 {
   if (unlikely (tag == HB_TAG_NONE))
     return HB_SCRIPT_INVALID;
 
   /* Be lenient, adjust case (one capital letter followed by three small letters) */
@@ -403,49 +403,49 @@ hb_script_from_iso15924_tag (hb_tag_t ta
  * hb_script_from_string:
  * @s: (array length=len) (element-type uint8_t): 
  * @len: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_script_t
 hb_script_from_string (const char *s, int len)
 {
   return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
 }
 
 /**
  * hb_script_to_iso15924_tag:
  * @script: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_tag_t
 hb_script_to_iso15924_tag (hb_script_t script)
 {
   return (hb_tag_t) script;
 }
 
 /**
  * hb_script_get_horizontal_direction:
  * @script: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_direction_t
 hb_script_get_horizontal_direction (hb_script_t script)
 {
   /* http://goo.gl/x9ilM */
   switch ((hb_tag_t) script)
   {
     /* Unicode-1.1 additions */
@@ -540,17 +540,17 @@ hb_user_data_array_t::get (hb_user_data_
 /**
  * hb_version:
  * @major: (out): Library major version component.
  * @minor: (out): Library minor version component.
  * @micro: (out): Library micro version component.
  *
  * Returns library version as three integer components.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_version (unsigned int *major,
 	    unsigned int *minor,
 	    unsigned int *micro)
 {
   *major = HB_VERSION_MAJOR;
   *minor = HB_VERSION_MINOR;
@@ -559,17 +559,17 @@ hb_version (unsigned int *major,
 
 /**
  * hb_version_string:
  *
  * Returns library version as a string with three components.
  *
  * Return value: library version string.
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 const char *
 hb_version_string (void)
 {
   return HB_VERSION_STRING;
 }
 
 /**
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -267,16 +267,19 @@ typedef enum
   /*6.1*/ HB_SCRIPT_CHAKMA			= HB_TAG ('C','a','k','m'),
   /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE		= HB_TAG ('M','e','r','c'),
   /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS	= HB_TAG ('M','e','r','o'),
   /*6.1*/ HB_SCRIPT_MIAO			= HB_TAG ('P','l','r','d'),
   /*6.1*/ HB_SCRIPT_SHARADA			= HB_TAG ('S','h','r','d'),
   /*6.1*/ HB_SCRIPT_SORA_SOMPENG		= HB_TAG ('S','o','r','a'),
   /*6.1*/ HB_SCRIPT_TAKRI			= HB_TAG ('T','a','k','r'),
 
+  /*
+   * Since: 0.9.30
+   */
   /*7.0*/ HB_SCRIPT_BASSA_VAH			= HB_TAG ('B','a','s','s'),
   /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN		= HB_TAG ('A','g','h','b'),
   /*7.0*/ HB_SCRIPT_DUPLOYAN			= HB_TAG ('D','u','p','l'),
   /*7.0*/ HB_SCRIPT_ELBASAN			= HB_TAG ('E','l','b','a'),
   /*7.0*/ HB_SCRIPT_GRANTHA			= HB_TAG ('G','r','a','n'),
   /*7.0*/ HB_SCRIPT_KHOJKI			= HB_TAG ('K','h','o','j'),
   /*7.0*/ HB_SCRIPT_KHUDAWADI			= HB_TAG ('S','i','n','d'),
   /*7.0*/ HB_SCRIPT_LINEAR_A			= HB_TAG ('L','i','n','a'),
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -120,16 +120,19 @@ hb_coretext_shaper_face_data_t *
 }
 
 void
 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
 {
   CFRelease (data);
 }
 
+/*
+ * Since: 0.9.10
+ */
 CGFontRef
 hb_coretext_face_get_cg_font (hb_face_t *face)
 {
   if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
   hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
   return face_data;
 }
 
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -72,17 +72,17 @@ const hb_face_t _hb_face_nil = {
  * @reference_table_func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data: 
  * @destroy: 
  *
  * 
  *
  * Return value: (transfer full)
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_face_create_for_tables (hb_reference_table_func_t  reference_table_func,
 			   void                      *user_data,
 			   hb_destroy_func_t          destroy)
 {
   hb_face_t *face;
 
@@ -108,17 +108,17 @@ typedef struct hb_face_for_data_closure_
   unsigned int  index;
 } hb_face_for_data_closure_t;
 
 static hb_face_for_data_closure_t *
 _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index)
 {
   hb_face_for_data_closure_t *closure;
 
-  closure = (hb_face_for_data_closure_t *) malloc (sizeof (hb_face_for_data_closure_t));
+  closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t));
   if (unlikely (!closure))
     return NULL;
 
   closure->blob = blob;
   closure->index = index;
 
   return closure;
 }
@@ -152,17 +152,17 @@ static hb_blob_t *
  * hb_face_create: (Xconstructor)
  * @blob: 
  * @index: 
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index)
 {
   hb_face_t *face;
 
   if (unlikely (!blob))
@@ -184,48 +184,48 @@ hb_face_create (hb_blob_t    *blob,
 
 /**
  * hb_face_get_empty:
  *
  * 
  *
  * Return value: (transfer full)
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_face_get_empty (void)
 {
   return const_cast<hb_face_t *> (&_hb_face_nil);
 }
 
 
 /**
  * hb_face_reference: (skip)
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_face_reference (hb_face_t *face)
 {
   return hb_object_reference (face);
 }
 
 /**
  * hb_face_destroy: (skip)
  * @face: a face.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_face_destroy (hb_face_t *face)
 {
   if (!hb_object_destroy (face)) return;
 
   for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
   {
@@ -252,17 +252,17 @@ hb_face_destroy (hb_face_t *face)
  * @data: 
  * @destroy: 
  * @replace: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_face_set_user_data (hb_face_t          *face,
 		       hb_user_data_key_t *key,
 		       void *              data,
 		       hb_destroy_func_t   destroy,
 		       hb_bool_t           replace)
 {
@@ -273,32 +273,32 @@ hb_face_set_user_data (hb_face_t        
  * hb_face_get_user_data: (skip)
  * @face: a face.
  * @key: 
  *
  * 
  *
  * Return value: (transfer none):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_face_get_user_data (hb_face_t          *face,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (face, key);
 }
 
 /**
  * hb_face_make_immutable:
  * @face: a face.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_face_make_immutable (hb_face_t *face)
 {
   if (unlikely (hb_object_is_inert (face)))
     return;
 
   face->immutable = true;
@@ -307,17 +307,17 @@ hb_face_make_immutable (hb_face_t *face)
 /**
  * hb_face_is_immutable:
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_face_is_immutable (hb_face_t *face)
 {
   return face->immutable;
 }
 
 
@@ -325,17 +325,17 @@ hb_face_is_immutable (hb_face_t *face)
  * hb_face_reference_table:
  * @face: a face.
  * @tag: 
  *
  * 
  *
  * Return value: (transfer full):
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_blob_t *
 hb_face_reference_table (hb_face_t *face,
 			 hb_tag_t   tag)
 {
   return face->reference_table (tag);
 }
 
@@ -412,17 +412,17 @@ hb_face_set_upem (hb_face_t    *face,
 /**
  * hb_face_get_upem:
  * @face: a face.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 unsigned int
 hb_face_get_upem (hb_face_t *face)
 {
   return face->get_upem ();
 }
 
 void
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -263,17 +263,17 @@ struct hb_font_t {
   }
 
   /* Internal only */
   inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
 					     hb_position_t *x, hb_position_t *y)
   {
     *x = get_glyph_h_advance (glyph) / 2;
 
-    /* TODO use font_metics.ascent */
+    /* TODO use font_metrics.ascent */
     *y = y_scale;
   }
 
   inline void get_glyph_origin_for_direction (hb_codepoint_t glyph,
 					      hb_direction_t direction,
 					      hb_position_t *x, hb_position_t *y)
   {
     if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -231,17 +231,17 @@ static const hb_font_funcs_t _hb_font_fu
 
 /**
  * hb_font_funcs_create: (Xconstructor)
  *
  * 
  *
  * Return value: (transfer full): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_funcs_t *
 hb_font_funcs_create (void)
 {
   hb_font_funcs_t *ffuncs;
 
   if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
     return hb_font_funcs_get_empty ();
@@ -253,47 +253,47 @@ hb_font_funcs_create (void)
 
 /**
  * hb_font_funcs_get_empty:
  *
  * 
  *
  * Return value: (transfer full): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void)
 {
   return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil);
 }
 
 /**
  * hb_font_funcs_reference: (skip)
  * @ffuncs: font functions.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_funcs_t *
 hb_font_funcs_reference (hb_font_funcs_t *ffuncs)
 {
   return hb_object_reference (ffuncs);
 }
 
 /**
  * hb_font_funcs_destroy: (skip)
  * @ffuncs: font functions.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
 {
   if (!hb_object_destroy (ffuncs)) return;
 
 #define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \
   ffuncs->destroy.name (ffuncs->user_data.name);
@@ -310,17 +310,17 @@ hb_font_funcs_destroy (hb_font_funcs_t *
  * @data: 
  * @destroy: 
  * @replace: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_funcs_set_user_data (hb_font_funcs_t    *ffuncs,
 			     hb_user_data_key_t *key,
 			     void *              data,
 			     hb_destroy_func_t   destroy,
 			     hb_bool_t           replace)
 {
@@ -331,33 +331,33 @@ hb_font_funcs_set_user_data (hb_font_fun
  * hb_font_funcs_get_user_data: (skip)
  * @ffuncs: font functions.
  * @key: 
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_font_funcs_get_user_data (hb_font_funcs_t    *ffuncs,
 			     hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (ffuncs, key);
 }
 
 
 /**
  * hb_font_funcs_make_immutable:
  * @ffuncs: font functions.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
 {
   if (unlikely (hb_object_is_inert (ffuncs)))
     return;
 
   ffuncs->immutable = true;
@@ -366,17 +366,17 @@ hb_font_funcs_make_immutable (hb_font_fu
 /**
  * hb_font_funcs_is_immutable:
  * @ffuncs: font functions.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
 {
   return ffuncs->immutable;
 }
 
 
@@ -420,17 +420,17 @@ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
  * @unicode: 
  * @variation_selector: 
  * @glyph: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph (hb_font_t *font,
 		   hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 		   hb_codepoint_t *glyph)
 {
   return font->get_glyph (unicode, variation_selector, glyph);
 }
@@ -439,17 +439,17 @@ hb_font_get_glyph (hb_font_t *font,
  * hb_font_get_glyph_h_advance:
  * @font: a font.
  * @glyph: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_position_t
 hb_font_get_glyph_h_advance (hb_font_t *font,
 			     hb_codepoint_t glyph)
 {
   return font->get_glyph_h_advance (glyph);
 }
 
@@ -457,17 +457,17 @@ hb_font_get_glyph_h_advance (hb_font_t *
  * hb_font_get_glyph_v_advance:
  * @font: a font.
  * @glyph: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_position_t
 hb_font_get_glyph_v_advance (hb_font_t *font,
 			     hb_codepoint_t glyph)
 {
   return font->get_glyph_v_advance (glyph);
 }
 
@@ -477,17 +477,17 @@ hb_font_get_glyph_v_advance (hb_font_t *
  * @glyph: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_h_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_h_origin (glyph, x, y);
 }
@@ -498,17 +498,17 @@ hb_font_get_glyph_h_origin (hb_font_t *f
  * @glyph: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_v_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_v_origin (glyph, x, y);
 }
@@ -518,17 +518,17 @@ hb_font_get_glyph_v_origin (hb_font_t *f
  * @font: a font.
  * @left_glyph: 
  * @right_glyph: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_position_t
 hb_font_get_glyph_h_kerning (hb_font_t *font,
 			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
 {
   return font->get_glyph_h_kerning (left_glyph, right_glyph);
 }
 
@@ -537,17 +537,17 @@ hb_font_get_glyph_h_kerning (hb_font_t *
  * @font: a font.
  * @top_glyph: 
  * @bottom_glyph: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_position_t
 hb_font_get_glyph_v_kerning (hb_font_t *font,
 			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph)
 {
   return font->get_glyph_v_kerning (top_glyph, bottom_glyph);
 }
 
@@ -556,17 +556,17 @@ hb_font_get_glyph_v_kerning (hb_font_t *
  * @font: a font.
  * @glyph: 
  * @extents: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_extents (hb_font_t *font,
 			   hb_codepoint_t glyph,
 			   hb_glyph_extents_t *extents)
 {
   return font->get_glyph_extents (glyph, extents);
 }
@@ -578,17 +578,17 @@ hb_font_get_glyph_extents (hb_font_t *fo
  * @point_index: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_contour_point (hb_font_t *font,
 				 hb_codepoint_t glyph, unsigned int point_index,
 				 hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_contour_point (glyph, point_index, x, y);
 }
@@ -643,17 +643,17 @@ hb_font_get_glyph_from_name (hb_font_t *
  * @font: a font.
  * @glyph: 
  * @direction: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_get_glyph_advance_for_direction (hb_font_t *font,
 					 hb_codepoint_t glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_advance_for_direction (glyph, direction, x, y);
@@ -664,17 +664,17 @@ hb_font_get_glyph_advance_for_direction 
  * @font: a font.
  * @glyph: 
  * @direction: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_get_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
 					hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_origin_for_direction (glyph, direction, x, y);
@@ -685,17 +685,17 @@ hb_font_get_glyph_origin_for_direction (
  * @font: a font.
  * @glyph: 
  * @direction: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_add_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
 					hb_position_t *x, hb_position_t *y)
 {
   return font->add_glyph_origin_for_direction (glyph, direction, x, y);
@@ -706,17 +706,17 @@ hb_font_add_glyph_origin_for_direction (
  * @font: a font.
  * @glyph: 
  * @direction: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_subtract_glyph_origin_for_direction (hb_font_t *font,
 					     hb_codepoint_t glyph,
 					     hb_direction_t direction,
 					     hb_position_t *x, hb_position_t *y)
 {
   return font->subtract_glyph_origin_for_direction (glyph, direction, x, y);
@@ -728,17 +728,17 @@ hb_font_subtract_glyph_origin_for_direct
  * @first_glyph: 
  * @second_glyph: 
  * @direction: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
 					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y);
@@ -750,17 +750,17 @@ hb_font_get_glyph_kerning_for_direction 
  * @glyph: 
  * @direction: 
  * @extents: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_extents_for_origin (hb_font_t *font,
 				      hb_codepoint_t glyph,
 				      hb_direction_t direction,
 				      hb_glyph_extents_t *extents)
 {
   return font->get_glyph_extents_for_origin (glyph, direction, extents);
@@ -774,17 +774,17 @@ hb_font_get_glyph_extents_for_origin (hb
  * @direction: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_get_glyph_contour_point_for_origin (hb_font_t *font,
 					    hb_codepoint_t glyph, unsigned int point_index,
 					    hb_direction_t direction,
 					    hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y);
@@ -840,57 +840,58 @@ hb_font_glyph_from_string (hb_font_t *fo
 /**
  * hb_font_create: (Xconstructor)
  * @face: a face.
  *
  * 
  *
  * Return value: (transfer full): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_t *
 hb_font_create (hb_face_t *face)
 {
   hb_font_t *font;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
   if (!(font = hb_object_create<hb_font_t> ()))
     return hb_font_get_empty ();
 
   hb_face_make_immutable (face);
   font->face = hb_face_reference (face);
   font->klass = hb_font_funcs_get_empty ();
 
+  font->x_scale = font->y_scale = hb_face_get_upem (face);
+
   return font;
 }
 
 /**
  * hb_font_create_sub_font:
  * @parent: parent font.
  *
  * 
  *
  * Return value: (transfer full): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_t *
 hb_font_create_sub_font (hb_font_t *parent)
 {
   if (unlikely (!parent))
     parent = hb_font_get_empty ();
 
   hb_font_t *font = hb_font_create (parent->face);
 
   if (unlikely (hb_object_is_inert (font)))
     return font;
 
-  hb_font_make_immutable (parent);
   font->parent = hb_font_reference (parent);
 
   font->x_scale = parent->x_scale;
   font->y_scale = parent->y_scale;
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
 
   return font;
@@ -898,17 +899,17 @@ hb_font_create_sub_font (hb_font_t *pare
 
 /**
  * hb_font_get_empty:
  *
  * 
  *
  * Return value: (transfer full)
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_t *
 hb_font_get_empty (void)
 {
   static const hb_font_t _hb_font_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     true, /* immutable */
@@ -939,31 +940,31 @@ hb_font_get_empty (void)
 /**
  * hb_font_reference: (skip)
  * @font: a font.
  *
  * 
  *
  * Return value: (transfer full): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_t *
 hb_font_reference (hb_font_t *font)
 {
   return hb_object_reference (font);
 }
 
 /**
  * hb_font_destroy: (skip)
  * @font: a font.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_destroy (hb_font_t *font)
 {
   if (!hb_object_destroy (font)) return;
 
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font);
 #include "hb-shaper-list.hh"
@@ -986,17 +987,17 @@ hb_font_destroy (hb_font_t *font)
  * @data: 
  * @destroy: 
  * @replace: 
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_set_user_data (hb_font_t          *font,
 		       hb_user_data_key_t *key,
 		       void *              data,
 		       hb_destroy_func_t   destroy,
 		       hb_bool_t           replace)
 {
@@ -1007,67 +1008,96 @@ hb_font_set_user_data (hb_font_t        
  * hb_font_get_user_data: (skip)
  * @font: a font.
  * @key: 
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void *
 hb_font_get_user_data (hb_font_t          *font,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (font, key);
 }
 
 /**
  * hb_font_make_immutable:
  * @font: a font.
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_make_immutable (hb_font_t *font)
 {
   if (unlikely (hb_object_is_inert (font)))
     return;
 
+  if (font->parent)
+    hb_font_make_immutable (font->parent);
+
   font->immutable = true;
 }
 
 /**
  * hb_font_is_immutable:
  * @font: a font.
  *
  * 
  *
  * Return value: 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_bool_t
 hb_font_is_immutable (hb_font_t *font)
 {
   return font->immutable;
 }
 
 /**
+ * hb_font_set_parent:
+ * @font: a font.
+ * @parent: new parent.
+ *
+ * Sets parent font of @font.
+ *
+ * Since: 1.0.5
+ **/
+void
+hb_font_set_parent (hb_font_t *font,
+		    hb_font_t *parent)
+{
+  if (font->immutable)
+    return;
+
+  if (!parent)
+    parent = hb_font_get_empty ();
+
+  hb_font_t *old = font->parent;
+
+  font->parent = hb_font_reference (parent);
+
+  hb_font_destroy (old);
+}
+
+/**
  * hb_font_get_parent:
  * @font: a font.
  *
  * 
  *
  * Return value: (transfer none): 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_t *
 hb_font_get_parent (hb_font_t *font)
 {
   return font->parent;
 }
 
 /**
@@ -1156,17 +1186,17 @@ hb_font_set_funcs_data (hb_font_t       
 /**
  * hb_font_set_scale:
  * @font: a font.
  * @x_scale: 
  * @y_scale: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_set_scale (hb_font_t *font,
 		   int x_scale,
 		   int y_scale)
 {
   if (font->immutable)
     return;
@@ -1178,17 +1208,17 @@ hb_font_set_scale (hb_font_t *font,
 /**
  * hb_font_get_scale:
  * @font: a font.
  * @x_scale: (out): 
  * @y_scale: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_get_scale (hb_font_t *font,
 		   int *x_scale,
 		   int *y_scale)
 {
   if (x_scale) *x_scale = font->x_scale;
   if (y_scale) *y_scale = font->y_scale;
@@ -1197,17 +1227,17 @@ hb_font_get_scale (hb_font_t *font,
 /**
  * hb_font_set_ppem:
  * @font: a font.
  * @x_ppem: 
  * @y_ppem: 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_set_ppem (hb_font_t *font,
 		  unsigned int x_ppem,
 		  unsigned int y_ppem)
 {
   if (font->immutable)
     return;
@@ -1219,17 +1249,17 @@ hb_font_set_ppem (hb_font_t *font,
 /**
  * hb_font_get_ppem:
  * @font: a font.
  * @x_ppem: (out): 
  * @y_ppem: (out): 
  *
  * 
  *
- * Since: 1.0
+ * Since: 0.9.2
  **/
 void
 hb_font_get_ppem (hb_font_t *font,
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem)
 {
   if (x_ppem) *x_ppem = font->x_ppem;
   if (y_ppem) *y_ppem = font->y_ppem;
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -144,145 +144,145 @@ typedef hb_bool_t (*hb_font_get_glyph_fr
  * hb_font_funcs_set_glyph_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_func (hb_font_funcs_t *ffuncs,
 			      hb_font_get_glyph_func_t func,
 			      void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_h_advance_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_h_advance_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_h_advance_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_v_advance_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_v_advance_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_v_advance_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_h_origin_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_h_origin_func (hb_font_funcs_t *ffuncs,
 				       hb_font_get_glyph_h_origin_func_t func,
 				       void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_v_origin_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_v_origin_func (hb_font_funcs_t *ffuncs,
 				       hb_font_get_glyph_v_origin_func_t func,
 				       void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_h_kerning_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_h_kerning_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_h_kerning_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_v_kerning_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_v_kerning_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_v_kerning_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_extents_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_extents_func (hb_font_funcs_t *ffuncs,
 				      hb_font_get_glyph_extents_func_t func,
 				      void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_contour_point_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_contour_point_func (hb_font_funcs_t *ffuncs,
 					    hb_font_get_glyph_contour_point_func_t func,
 					    void *user_data, hb_destroy_func_t destroy);
 
 /**
  * hb_font_funcs_set_glyph_name_func:
@@ -454,16 +454,20 @@ hb_font_get_user_data (hb_font_t        
 		       hb_user_data_key_t *key);
 
 void
 hb_font_make_immutable (hb_font_t *font);
 
 hb_bool_t
 hb_font_is_immutable (hb_font_t *font);
 
+void
+hb_font_set_parent (hb_font_t *font,
+		    hb_font_t *parent);
+
 hb_font_t *
 hb_font_get_parent (hb_font_t *font);
 
 hb_face_t *
 hb_font_get_face (hb_font_t *font);
 
 
 void
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -1,11 +1,12 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
  * Copyright © 2009  Keith Stribley
+ * 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.
@@ -18,16 +19,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-ft.h"
 
 #include "hb-font-private.hh"
 
@@ -41,90 +43,174 @@
 #endif
 
 
 /* TODO:
  *
  * In general, this file does a fine job of what it's supposed to do.
  * There are, however, things that need more work:
  *
- *   - We don't handle any load_flags.  That definitely has API implications. :(
- *     I believe hb_ft_font_create() should take load_flags input.
- *     In particular, FT_Get_Advance() without the NO_HINTING flag seems to be
- *     buggy.
+ *   - I remember seeing FT_Get_Advance() without the NO_HINTING flag to be buggy.
+ *     Have not investigated.
  *
- *     FreeType works in 26.6 mode.  Clients can decide to use that mode, and everything
+ *   - FreeType works in 26.6 mode.  Clients can decide to use that mode, and everything
  *     would work fine.  However, we also abuse this API for performing in font-space,
  *     but don't pass the correct flags to FreeType.  We just abuse the no-hinting mode
  *     for that, such that no rounding etc happens.  As such, we don't set ppem, and
- *     pass NO_HINTING around.  This seems to work best, until we go ahead and add a full
- *     load_flags API.
+ *     pass NO_HINTING as load_flags.  Would be much better to use NO_SCALE, and scale
+ *     ourselves, like we do in uniscribe, etc.
  *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
  *   - In the future, we should add constructors to create fonts in font space?
  *
  *   - FT_Load_Glyph() is exteremely costly.  Do something about it?
  */
 
 
+struct hb_ft_font_t
+{
+  FT_Face ft_face;
+  int load_flags;
+  bool unref; /* Whether to destroy ft_face when done. */
+};
+
+static hb_ft_font_t *
+_hb_ft_font_create (FT_Face ft_face, bool unref)
+{
+  hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t));
+
+  if (unlikely (!ft_font))
+    return NULL;
+
+  ft_font->ft_face = ft_face;
+  ft_font->unref = unref;
+
+  ft_font->load_flags = FT_LOAD_DEFAULT;
+
+  return ft_font;
+}
+
+static void
+_hb_ft_font_destroy (hb_ft_font_t *ft_font)
+{
+  if (ft_font->unref)
+    FT_Done_Face (ft_font->ft_face);
+
+  free (ft_font);
+}
+
+/**
+ * hb_ft_font_set_load_flags:
+ * @font:
+ * @load_flags:
+ *
+ * 
+ *
+ * Since: 1.0.5
+ **/
+void
+hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
+{
+  if (font->immutable)
+    return;
+
+  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+    return;
+
+  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
+
+  ft_font->load_flags = load_flags;
+}
+
+/**
+ * hb_ft_font_get_load_flags:
+ * @font:
+ *
+ * 
+ *
+ * Return value:
+ * Since: 1.0.5
+ **/
+int
+hb_ft_font_get_load_flags (hb_font_t *font)
+{
+  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+    return 0;
+
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
+
+  return ft_font->load_flags;
+}
+
+FT_Face
+hb_ft_font_get_face (hb_font_t *font)
+{
+  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+    return NULL;
+
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
+
+  return ft_font->ft_face;
+}
+
+
+
 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)
 
 {
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   unsigned int g;
-  FT_Face ft_face = (FT_Face) font_data;
 
   if (likely (!variation_selector))
-    g = FT_Get_Char_Index (ft_face, unicode);
+    g = FT_Get_Char_Index (ft_font->ft_face, unicode);
   else
-    g = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
+    g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector);
 
   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)
 {
-  FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   FT_Fixed v;
 
-  if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v)))
+  if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags, &v)))
     return 0;
 
   if (font->x_scale < 0)
     v = -v;
 
   return (v + (1<<9)) >> 10;
 }
 
 static hb_position_t
 hb_ft_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING | FT_LOAD_VERTICAL_LAYOUT;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   FT_Fixed v;
 
-  if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v)))
+  if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v)))
     return 0;
 
   if (font->y_scale < 0)
     v = -v;
 
   /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
    * have a Y growing upward.  Hence the extra negation. */
   return (-v + (1<<9)) >> 10;
@@ -145,20 +231,20 @@ hb_ft_get_glyph_h_origin (hb_font_t *fon
 static hb_bool_t
 hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED,
 			  void *font_data,
 			  hb_codepoint_t glyph,
 			  hb_position_t *x,
 			  hb_position_t *y,
 			  void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  FT_Face ft_face = ft_font->ft_face;
 
-  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+  if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
     return false;
 
   /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
    * have a Y growing upward.  Hence the extra negation. */
   *x = ft_face->glyph->metrics.horiBearingX -   ft_face->glyph->metrics.vertBearingX;
   *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY);
 
   if (font->x_scale < 0)
@@ -171,21 +257,21 @@ hb_ft_get_glyph_v_origin (hb_font_t *fon
 
 static hb_position_t
 hb_ft_get_glyph_h_kerning (hb_font_t *font,
 			   void *font_data,
 			   hb_codepoint_t left_glyph,
 			   hb_codepoint_t right_glyph,
 			   void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   FT_Vector kerningv;
 
   FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED;
-  if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, mode, &kerningv))
+  if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv))
     return 0;
 
   return kerningv.x;
 }
 
 static hb_position_t
 hb_ft_get_glyph_v_kerning (hb_font_t *font HB_UNUSED,
 			   void *font_data HB_UNUSED,
@@ -199,20 +285,20 @@ hb_ft_get_glyph_v_kerning (hb_font_t *fo
 
 static hb_bool_t
 hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
 			 void *font_data,
 			 hb_codepoint_t glyph,
 			 hb_glyph_extents_t *extents,
 			 void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  FT_Face ft_face = ft_font->ft_face;
 
-  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+  if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
     return false;
 
   extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
   extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
   extents->width = ft_face->glyph->metrics.width;
   extents->height = -ft_face->glyph->metrics.height;
   return true;
 }
@@ -221,20 +307,20 @@ static hb_bool_t
 hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED,
 			       void *font_data,
 			       hb_codepoint_t glyph,
 			       unsigned int point_index,
 			       hb_position_t *x,
 			       hb_position_t *y,
 			       void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
-  int load_flags = FT_LOAD_DEFAULT;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  FT_Face ft_face = ft_font->ft_face;
 
-  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+  if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
       return false;
 
   if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
       return false;
 
   if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
       return false;
 
@@ -246,33 +332,34 @@ hb_ft_get_glyph_contour_point (hb_font_t
 
 static hb_bool_t
 hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED,
 		      void *font_data,
 		      hb_codepoint_t glyph,
 		      char *name, unsigned int size,
 		      void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
 
-  hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
+  hb_bool_t ret = !FT_Get_Glyph_Name (ft_font->ft_face, glyph, name, size);
   if (ret && (size && !*name))
     ret = false;
 
   return ret;
 }
 
 static hb_bool_t
 hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED,
 			   void *font_data,
 			   const char *name, int len, /* -1 means nul-terminated */
 			   hb_codepoint_t *glyph,
 			   void *user_data HB_UNUSED)
 {
-  FT_Face ft_face = (FT_Face) font_data;
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  FT_Face ft_face = ft_font->ft_face;
 
   if (len < 0)
     *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name);
   else {
     /* Make a nul-terminated version. */
     char buf[128];
     len = MIN (len, (int) sizeof (buf) - 1);
     strncpy (buf, name, len);
@@ -288,32 +375,35 @@ hb_ft_get_glyph_from_name (hb_font_t *fo
         len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len))
       return true;
   }
 
   return *glyph != 0;
 }
 
 
-static hb_font_funcs_t *
-_hb_ft_get_font_funcs (void)
+static void
+_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
 {
   static const hb_font_funcs_t ft_ffuncs = {
     HB_OBJECT_HEADER_STATIC,
 
     true, /* immutable */
 
     {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
   };
 
-  return const_cast<hb_font_funcs_t *> (&ft_ffuncs);
+  hb_font_set_funcs (font,
+		     const_cast<hb_font_funcs_t *> (&ft_ffuncs),
+		     _hb_ft_font_create (ft_face, unref),
+		     (hb_destroy_func_t) _hb_ft_font_destroy);
 }
 
 
 static hb_blob_t *
 reference_table  (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   FT_Face ft_face = (FT_Face) user_data;
   FT_Byte *buffer;
@@ -342,17 +432,17 @@ reference_table  (hb_face_t *face HB_UNU
 /**
  * hb_ft_face_create:
  * @ft_face: (destroy destroy) (scope notified): 
  * @destroy:
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_ft_face_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy)
 {
   hb_face_t *face;
 
   if (ft_face->stream->read == NULL) {
@@ -398,62 +488,55 @@ hb_ft_face_finalize (FT_Face ft_face)
 
 /**
  * hb_ft_face_create_cached:
  * @ft_face: 
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_face_t *
 hb_ft_face_create_cached (FT_Face ft_face)
 {
   if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize))
   {
     if (ft_face->generic.finalizer)
       ft_face->generic.finalizer (ft_face);
 
     ft_face->generic.data = hb_ft_face_create (ft_face, NULL);
     ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
   }
 
   return hb_face_reference ((hb_face_t *) ft_face->generic.data);
 }
 
-static void
-_do_nothing (void)
-{
-}
-
 
 /**
  * hb_ft_font_create:
  * @ft_face: (destroy destroy) (scope notified): 
  * @destroy:
  *
  * 
  *
  * Return value: (transfer full): 
- * Since: 1.0
+ * Since: 0.9.2
  **/
 hb_font_t *
 hb_ft_font_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy)
 {
   hb_font_t *font;
   hb_face_t *face;
 
   face = hb_ft_face_create (ft_face, destroy);
   font = hb_font_create (face);
   hb_face_destroy (face);
-  hb_font_set_funcs (font,
-		     _hb_ft_get_font_funcs (),
-		     ft_face, (hb_destroy_func_t) _do_nothing);
+  _hb_ft_font_set_funcs (font, ft_face, false);
   hb_font_set_scale (font,
 		     (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16),
 		     (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16));
 #if 0 /* hb-ft works in no-hinting model */
   hb_font_set_ppem (font,
 		    ft_face->size->metrics.x_ppem,
 		    ft_face->size->metrics.y_ppem);
 #endif
@@ -557,23 +640,11 @@ hb_ft_font_set_funcs (hb_font_t *font)
     FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
 			  0, font->y_scale < 0 ? -1 : +1};
     FT_Set_Transform (ft_face, &matrix, NULL);
   }
 
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
 
-  hb_font_set_funcs (font,
-		     _hb_ft_get_font_funcs (),
-		     ft_face,
-		     (hb_destroy_func_t) FT_Done_Face);
+  _hb_ft_font_set_funcs (font, ft_face, true);
+  hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
 }
-
-FT_Face
-hb_ft_font_get_face (hb_font_t *font)
-{
-  if (font->destroy == (hb_destroy_func_t) FT_Done_Face ||
-      font->destroy == (hb_destroy_func_t) _do_nothing)
-    return (FT_Face) font->user_data;
-
-  return NULL;
-}
--- a/gfx/harfbuzz/src/hb-ft.h
+++ b/gfx/harfbuzz/src/hb-ft.h
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
+ * 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.
@@ -17,16 +18,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FT_H
 #define HB_FT_H
 
 #include "hb.h"
 
 #include <ft2build.h>
@@ -100,20 +102,25 @@ hb_font_t *
 hb_ft_font_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy);
 
 /* See notes on hb_ft_face_create_referenced() re lifecycle-management
  * issues. */
 hb_font_t *
 hb_ft_font_create_referenced (FT_Face ft_face);
 
+FT_Face
+hb_ft_font_get_face (hb_font_t *font);
+
+void
+hb_ft_font_set_load_flags (hb_font_t *font, int load_flags);
+
+int
+hb_ft_font_get_load_flags (hb_font_t *font);
 
 /* Makes an hb_font_t use FreeType internally to implement font functions. */
 void
 hb_ft_font_set_funcs (hb_font_t *font);
 
-FT_Face
-hb_ft_font_get_face (hb_font_t *font);
-
 
 HB_END_DECLS
 
 #endif /* HB_FT_H */
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -133,16 +133,19 @@ void
     free (old);
   }
 
   gr_face_destroy (data->grface);
 
   free (data);
 }
 
+/*
+ * Since: 0.9.10
+ */
 gr_face *
 hb_graphite2_face_get_gr_face (hb_face_t *face)
 {
   if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL;
   return HB_SHAPER_DATA_GET (face)->grface;
 }
 
 
@@ -167,16 +170,19 @@ hb_graphite2_shaper_font_data_t *
 }
 
 void
 _hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data)
 {
   gr_font_destroy (data);
 }
 
+/*
+ * Since: 0.9.10
+ */
 gr_font *
 hb_graphite2_font_get_gr_font (hb_font_t *font)
 {
   if (unlikely (!hb_graphite2_shaper_font_data_ensure (font))) return NULL;
   return HB_SHAPER_DATA_GET (font);
 }
 
 
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -51,17 +51,17 @@ struct OffsetTable;
 struct TTCHeader;
 
 
 typedef struct TableRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   Tag		tag;		/* 4-byte identifier. */
   CheckSum	checkSum;	/* CheckSum for this table. */
   ULONG		offset;		/* Offset from beginning of TrueType font
 				 * file. */
   ULONG		length;		/* Length of this table. */
   public:
@@ -101,17 +101,17 @@ typedef struct OffsetTable
     find_table_index (tag, &table_index);
     return get_table (table_index);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
+    return_trace (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
 
   protected:
   Tag		sfnt_version;	/* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
   USHORT	numTables;	/* Number of tables. */
   USHORT	searchRangeZ;	/* (Maximum power of 2 <= numTables) x 16 */
   USHORT	entrySelectorZ;	/* Log2(maximum power of 2 <= numTables). */
   USHORT	rangeShiftZ;	/* NumTables x 16-searchRange. */
@@ -130,17 +130,17 @@ struct TTCHeaderVersion1
   friend struct TTCHeader;
 
   inline unsigned int get_face_count (void) const { return table.len; }
   inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (table.sanitize (c, this));
+    return_trace (table.sanitize (c, this));
   }
 
   protected:
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion	version;	/* Version of the TTC Header (1.0),
 				 * 0x00010000u */
   ArrayOf<OffsetTo<OffsetTable, ULONG>, ULONG>
 		table;		/* Array of offsets to the OffsetTable for each font
@@ -170,21 +170,21 @@ struct TTCHeader
     case 1: return u.version1.get_face (i);
     default:return Null(OpenTypeFontFace);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false);
+    if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
     switch (u.header.version.major) {
     case 2: /* version 2 is compatible with version 1 */
-    case 1: return TRACE_RETURN (u.version1.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return_trace (u.version1.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   protected:
   union {
   struct {
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion	version;	/* Version of the TTC Header (1.0 or 2.0),
@@ -235,24 +235,24 @@ struct OpenTypeFontFile
     case TTCTag:	return u.ttcHeader.get_face (i);
     default:		return Null(OpenTypeFontFace);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false);
+    if (unlikely (!u.tag.sanitize (c))) return_trace (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
-    case TrueTypeTag:	return TRACE_RETURN (u.fontFace.sanitize (c));
-    case TTCTag:	return TRACE_RETURN (u.ttcHeader.sanitize (c));
-    default:		return TRACE_RETURN (true);
+    case TrueTypeTag:	return_trace (u.fontFace.sanitize (c));
+    case TTCTag:	return_trace (u.ttcHeader.sanitize (c));
+    default:		return_trace (true);
     }
   }
 
   protected:
   union {
   Tag			tag;		/* 4-byte identifier. */
   OpenTypeFontFace	fontFace;
   TTCHeader		ttcHeader;
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -149,16 +149,30 @@ template <> \
   return *CastP<Type> (_Null##Type); \
 } /* The following line really exists such that we end in a place needing semicolon */ \
 ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
 
 /* Accessor macro. */
 #define Null(Type) Null<Type>()
 
 
+/*
+ * Dispatch
+ */
+
+template <typename Context, typename Return, unsigned int MaxDebugDepth>
+struct hb_dispatch_context_t
+{
+  static const unsigned int max_debug_depth = MaxDebugDepth;
+  typedef Return return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
+  static return_t no_dispatch_return_value (void) { return Context::default_return_value (); }
+};
+
 
 /*
  * Sanitize
  */
 
 #ifndef HB_DEBUG_SANITIZE
 #define HB_DEBUG_SANITIZE (HB_DEBUG+0)
 #endif
@@ -169,27 +183,33 @@ ASSERT_STATIC (Type::min_size + 1 <= siz
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "");
 
 /* This limits sanitizing time on really broken fonts. */
 #ifndef HB_SANITIZE_MAX_EDITS
 #define HB_SANITIZE_MAX_EDITS 100
 #endif
 
-struct hb_sanitize_context_t
+struct hb_sanitize_context_t :
+       hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
 {
+  inline hb_sanitize_context_t (void) :
+	debug_depth (0),
+	start (NULL), end (NULL),
+	writable (false), edit_count (0),
+	blob (NULL) {}
+
   inline const char *get_name (void) { return "SANITIZE"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_SANITIZE;
-  typedef bool return_t;
   template <typename T, typename F>
   inline bool may_dispatch (const T *obj, const F *format)
   { return format->sanitize (this); }
   template <typename T>
   inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
   static return_t default_return_value (void) { return true; }
+  static return_t no_dispatch_return_value (void) { return false; }
   bool stop_sublookup_iteration (const return_t r) const { return !r; }
 
   inline void init (hb_blob_t *b)
   {
     this->blob = hb_blob_reference (b);
     this->writable = false;
   }
 
@@ -290,17 +310,17 @@ struct hb_sanitize_context_t
 
 
 
 /* Template to sanitize an object. */
 template <typename Type>
 struct Sanitizer
 {
   static hb_blob_t *sanitize (hb_blob_t *blob) {
-    hb_sanitize_context_t c[1] = {{0, NULL, NULL, false, 0, NULL}};
+    hb_sanitize_context_t c[1];
     bool sane;
 
     /* TODO is_sane() stuff */
 
     c->init (blob);
 
   retry:
     DEBUG_MSG_FUNC (SANITIZE, c->start, "start");
@@ -619,17 +639,17 @@ struct IntType
     if (sizeof (Type) < sizeof (int))
       return (int) a - (int) b;
     else
       return a < b ? -1 : a == b ? 0 : +1;
   }
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (likely (c->check_struct (this)));
+    return_trace (likely (c->check_struct (this)));
   }
   protected:
   BEInt<Type, Size> v;
   public:
   DEFINE_SIZE_STATIC (Size);
 };
 
 typedef	IntType<uint8_t	, 1> BYTE;	/* 8-bit unsigned integer. */
@@ -647,17 +667,17 @@ typedef USHORT UFWORD;
 
 /* Date represented in number of seconds since 12:00 midnight, January 1,
  * 1904. The value is represented as a signed 64-bit integer. */
 struct LONGDATETIME
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (likely (c->check_struct (this)));
+    return_trace (likely (c->check_struct (this)));
   }
   protected:
   LONG major;
   ULONG minor;
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
@@ -724,17 +744,17 @@ struct CheckSum : ULONG
 
 struct FixedVersion
 {
   inline uint32_t to_int (void) const { return (major << 16) + minor; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   USHORT major;
   USHORT minor;
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
@@ -760,31 +780,31 @@ struct OffsetTo : Offset<OffsetType>
     Type *t = c->start_embed<Type> ();
     this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
     return *t;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
+    if (unlikely (!c->check_struct (this))) return_trace (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return TRACE_RETURN (true);
+    if (unlikely (!offset)) return_trace (true);
     const Type &obj = StructAtOffset<Type> (base, offset);
-    return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c));
+    return_trace (likely (obj.sanitize (c)) || neuter (c));
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
+    if (unlikely (!c->check_struct (this))) return_trace (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return TRACE_RETURN (true);
+    if (unlikely (!offset)) return_trace (true);
     const Type &obj = StructAtOffset<Type> (base, offset);
-    return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c));
+    return_trace (likely (obj.sanitize (c, user_data)) || neuter (c));
   }
 
   /* Set the offset to Null */
   inline bool neuter (hb_sanitize_context_t *c) const {
     return c->try_set (this, 0);
   }
   DEFINE_SIZE_STATIC (sizeof(OffsetType));
 };
@@ -825,87 +845,87 @@ struct ArrayOf
   }
   inline unsigned int get_size (void) const
   { return len.static_size + len * Type::static_size; }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 unsigned int items_len)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
     len.set (items_len); /* TODO(serialize) Overflow? */
-    if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!c->extend (*this))) return_trace (false);
+    return_trace (true);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<Type> &items,
 			 unsigned int items_len)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false);
+    if (unlikely (!serialize (c, items_len))) return_trace (false);
     for (unsigned int i = 0; i < items_len; i++)
       array[i] = items[i];
     items.advance (items_len);
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
+    if (unlikely (!sanitize_shallow (c))) return_trace (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
      * pointed to do have a simple sanitize(), ie. they do not
      * reference other structs via offsets.
      */
     (void) (false && array[0].sanitize (c));
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
+    if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!array[i].sanitize (c, base)))
-        return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+        return_trace (false);
+    return_trace (true);
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
+    if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!array[i].sanitize (c, base, user_data)))
-        return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+        return_trace (false);
+    return_trace (true);
   }
 
   template <typename SearchType>
   inline int lsearch (const SearchType &x) const
   {
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (!this->array[i].cmp (x))
         return i;
     return -1;
   }
 
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
+    return_trace (c->check_struct (this) && c->check_array (array, Type::static_size, len));
   }
 
   public:
   LenType len;
   Type array[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
@@ -922,23 +942,23 @@ struct OffsetListOf : OffsetArrayOf<Type
   {
     if (unlikely (i >= this->len)) return Null(Type);
     return this+this->array[i];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this));
+    return_trace (OffsetArrayOf<Type>::sanitize (c, this));
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, T user_data) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
+    return_trace (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
 };
 
 
 /* An array starting at second element. */
 template <typename Type, typename LenType=USHORT>
 struct HeadlessArrayOf
 {
@@ -950,47 +970,47 @@ struct HeadlessArrayOf
   inline unsigned int get_size (void) const
   { return len.static_size + (len ? len - 1 : 0) * Type::static_size; }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<Type> &items,
 			 unsigned int items_len)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
     len.set (items_len); /* TODO(serialize) Overflow? */
-    if (unlikely (!items_len)) return TRACE_RETURN (true);
-    if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
+    if (unlikely (!items_len)) return_trace (true);
+    if (unlikely (!c->extend (*this))) return_trace (false);
     for (unsigned int i = 0; i < items_len - 1; i++)
       array[i] = items[i];
     items.advance (items_len - 1);
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     return c->check_struct (this)
 	&& c->check_array (this, Type::static_size, len);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
+    if (unlikely (!sanitize_shallow (c))) return_trace (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
      * pointed to do have a simple sanitize(), ie. they do not
      * reference other structs via offsets.
      */
     (void) (false && array[0].sanitize (c));
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   LenType len;
   Type array[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh
@@ -49,17 +49,17 @@ struct CmapSubtableFormat0
       return false;
     *glyph = gid;
     return true;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   protected:
   USHORT	format;		/* Format number is set to 0. */
   USHORT	lengthZ;	/* Byte length of this subtable. */
   USHORT	languageZ;	/* Ignore. */
   BYTE		glyphIdArray[256];/* An array that maps character
 				 * code to glyph index values. */
@@ -125,31 +125,31 @@ struct CmapSubtableFormat4
     *glyph = gid & 0xFFFFu;
     return true;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this)))
-      return TRACE_RETURN (false);
+      return_trace (false);
 
     if (unlikely (!c->check_range (this, length)))
     {
       /* Some broken fonts have too long of a "length" value.
        * If that is the case, just change the value to truncate
        * the subtable at the end of the blob. */
       uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535,
 					    (uintptr_t) (c->end -
 							 (char *) this));
       if (!c->try_set (&length, new_length))
-	return TRACE_RETURN (false);
+	return_trace (false);
     }
 
-    return TRACE_RETURN (16 + 4 * (unsigned int) segCountX2 <= length);
+    return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
   }
 
   protected:
   USHORT	format;		/* Format number is set to 4. */
   USHORT	length;		/* This is the length in bytes of the
 				 * subtable. */
   USHORT	languageZ;	/* Ignore. */
   USHORT	segCountX2;	/* 2 x segCount. */
@@ -182,17 +182,17 @@ struct CmapSubtableLongGroup
     if (codepoint < startCharCode) return -1;
     if (codepoint > endCharCode)   return +1;
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   private:
   ULONG		startCharCode;	/* First character code in this group. */
   ULONG		endCharCode;	/* Last character code in this group. */
   ULONG		glyphID;	/* Glyph index; interpretation depends on
 				 * subtable format. */
   public:
@@ -210,17 +210,17 @@ struct CmapSubtableTrimmed
       return false;
     *glyph = gid;
     return true;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && glyphIdArray.sanitize (c));
+    return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
   }
 
   protected:
   UINT		formatReserved;	/* Subtable format and (maybe) padding. */
   UINT		lengthZ;	/* Byte length of this subtable. */
   UINT		languageZ;	/* Ignore. */
   UINT		startCharCode;	/* First character code covered. */
   ArrayOf<GlyphID, UINT>
@@ -243,17 +243,17 @@ struct CmapSubtableLongSegmented
       return false;
     *glyph = T::group_get_glyph (groups[i], codepoint);
     return true;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && groups.sanitize (c));
+    return_trace (c->check_struct (this) && groups.sanitize (c));
   }
 
   protected:
   USHORT	format;		/* Subtable format; set to 12. */
   USHORT	reservedZ;	/* Reserved; set to 0. */
   ULONG		lengthZ;	/* Byte length of this subtable. */
   ULONG		languageZ;	/* Ignore. */
   SortedArrayOf<CmapSubtableLongGroup, ULONG>
@@ -290,17 +290,17 @@ struct UnicodeValueRange
     if (codepoint < startUnicodeValue) return -1;
     if (codepoint > startUnicodeValue + additionalCount) return +1;
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   UINT24	startUnicodeValue;	/* First value in this range. */
   BYTE		additionalCount;	/* Number of additional values in this
 					 * range. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -312,17 +312,17 @@ struct UVSMapping
   inline int cmp (const hb_codepoint_t &codepoint) const
   {
     return unicodeValue.cmp (codepoint);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   UINT24	unicodeValue;	/* Base Unicode value of the UVS */
   GlyphID	glyphID;	/* Glyph ID of the UVS */
   public:
   DEFINE_SIZE_STATIC (5);
 };
 
@@ -352,19 +352,19 @@ struct VariationSelectorRecord
   inline int cmp (const hb_codepoint_t &variation_selector) const
   {
     return varSelector.cmp (variation_selector);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) &&
-			 defaultUVS.sanitize (c, base) &&
-			 nonDefaultUVS.sanitize (c, base));
+    return_trace (c->check_struct (this) &&
+		  defaultUVS.sanitize (c, base) &&
+		  nonDefaultUVS.sanitize (c, base));
   }
 
   UINT24	varSelector;	/* Variation selector. */
   OffsetTo<DefaultUVS, ULONG>
 		defaultUVS;	/* Offset to Default UVS Table. May be 0. */
   OffsetTo<NonDefaultUVS, ULONG>
 		nonDefaultUVS;	/* Offset to Non-Default UVS Table. May be 0. */
   public:
@@ -378,18 +378,18 @@ struct CmapSubtableFormat14
 					    hb_codepoint_t *glyph) const
   {
     return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) &&
-			 record.sanitize (c, this));
+    return_trace (c->check_struct (this) &&
+		  record.sanitize (c, this));
   }
 
   protected:
   USHORT	format;		/* Format number is set to 0. */
   ULONG		lengthZ;	/* Byte length of this subtable. */
   SortedArrayOf<VariationSelectorRecord, ULONG>
 		record;		/* Variation selector records; sorted
 				 * in increasing order of `varSelector'. */
@@ -424,26 +424,26 @@ struct CmapSubtable
     case 14: return u.format14.get_glyph_variant(codepoint, variation_selector, glyph);
     default: return GLYPH_VARIANT_NOT_FOUND;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    if (!u.format.sanitize (c)) return_trace (false);
     switch (u.format) {
-    case  0: return TRACE_RETURN (u.format0 .sanitize (c));
-    case  4: return TRACE_RETURN (u.format4 .sanitize (c));
-    case  6: return TRACE_RETURN (u.format6 .sanitize (c));
-    case 10: return TRACE_RETURN (u.format10.sanitize (c));
-    case 12: return TRACE_RETURN (u.format12.sanitize (c));
-    case 13: return TRACE_RETURN (u.format13.sanitize (c));
-    case 14: return TRACE_RETURN (u.format14.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case  0: return_trace (u.format0 .sanitize (c));
+    case  4: return_trace (u.format4 .sanitize (c));
+    case  6: return_trace (u.format6 .sanitize (c));
+    case 10: return_trace (u.format10.sanitize (c));
+    case 12: return_trace (u.format12.sanitize (c));
+    case 13: return_trace (u.format13.sanitize (c));
+    case 14: return_trace (u.format14.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   CmapSubtableFormat0	format0;
   CmapSubtableFormat4	format4;
@@ -468,18 +468,18 @@ struct EncodingRecord
     ret = encodingID.cmp (other.encodingID);
     if (ret) return ret;
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) &&
-			 subtable.sanitize (c, base));
+    return_trace (c->check_struct (this) &&
+		  subtable.sanitize (c, base));
   }
 
   USHORT	platformID;	/* Platform ID. */
   USHORT	encodingID;	/* Platform-specific encoding ID. */
   OffsetTo<CmapSubtable, ULONG>
 		subtable;	/* Byte offset from beginning of table to the subtable for this encoding. */
   public:
   DEFINE_SIZE_STATIC (8);
@@ -504,19 +504,19 @@ struct cmap
       return NULL;
 
     return &(this+encodingRecord[result].subtable);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) &&
-			 likely (version == 0) &&
-			 encodingRecord.sanitize (c, this));
+    return_trace (c->check_struct (this) &&
+		  likely (version == 0) &&
+		  encodingRecord.sanitize (c, this));
   }
 
   USHORT		version;	/* Table version number (0). */
   SortedArrayOf<EncodingRecord>
 			encodingRecord;	/* Encoding tables. */
   public:
   DEFINE_SIZE_ARRAY (4, encodingRecord);
 };
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -233,20 +233,19 @@ struct hb_ot_font_t
   hb_ot_face_cmap_accelerator_t cmap;
   hb_ot_face_metrics_accelerator_t h_metrics;
   hb_ot_face_metrics_accelerator_t v_metrics;
   hb_ot_face_glyf_accelerator_t glyf;
 };
 
 
 static hb_ot_font_t *
-_hb_ot_font_create (hb_font_t *font)
+_hb_ot_font_create (hb_face_t *face)
 {
   hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
-  hb_face_t *face = font->face;
 
   if (unlikely (!ot_font))
     return NULL;
 
   unsigned int upem = face->get_upem ();
 
   ot_font->cmap.init (face);
   ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, upem>>1);
@@ -257,16 +256,17 @@ static hb_ot_font_t *
 }
 
 static void
 _hb_ot_font_destroy (hb_ot_font_t *ot_font)
 {
   ot_font->cmap.fini ();
   ot_font->h_metrics.fini ();
   ot_font->v_metrics.fini ();
+  ot_font->glyf.fini ();
 
   free (ot_font);
 }
 
 
 static hb_bool_t
 hb_ot_get_glyph (hb_font_t *font HB_UNUSED,
 		 void *font_data,
@@ -418,17 +418,17 @@ static hb_font_funcs_t *
 
 
 /**
  * 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);
+  hb_ot_font_t *ot_font = _hb_ot_font_create (font->face);
   if (unlikely (!ot_font))
     return;
 
   hb_font_set_funcs (font,
 		     _hb_ot_get_font_funcs (),
 		     ot_font,
 		     (hb_destroy_func_t) _hb_ot_font_destroy);
 }
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-glyf-table.hh
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_GLYF_TABLE_HH
+#define HB_OT_GLYF_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+
+namespace OT {
+
+
+/*
+ * loca -- Index to Location
+ */
+
+#define HB_OT_TAG_loca HB_TAG('l','o','c','a')
+
+
+struct loca
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_loca;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (true);
+  }
+
+  public:
+  union {
+    USHORT	shortsZ[VAR];		/* Location offset divided by 2. */
+    ULONG	longsZ[VAR];		/* Location offset. */
+  } u;
+  DEFINE_SIZE_ARRAY (0, u.longsZ);
+};
+
+
+/*
+ * glyf -- TrueType Glyph Data
+ */
+
+#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
+
+
+struct glyf
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_glyf;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    /* We don't check for anything specific here.  The users of the
+     * struct do all the hard work... */
+    return_trace (true);
+  }
+
+  public:
+  BYTE		dataX[VAR];		/* Glyphs data. */
+
+  DEFINE_SIZE_ARRAY (0, dataX);
+};
+
+struct glyfGlyphHeader
+{
+  SHORT		numberOfContours;	/* If the number of contours is
+					 * greater than or equal to zero,
+					 * this is a simple glyph; if negative,
+					 * this is a composite glyph. */
+  SHORT		xMin;			/* Minimum x for coordinate data. */
+  SHORT		yMin;			/* Minimum y for coordinate data. */
+  SHORT		xMax;			/* Maximum x for coordinate data. */
+  SHORT		yMax;			/* Maximum y for coordinate data. */
+
+  DEFINE_SIZE_STATIC (10);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_GLYF_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -50,17 +50,17 @@ struct head
     unsigned int upem = unitsPerEm;
     /* If no valid head table found, assume 1000, which matches typical Type1 usage. */
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
+    return_trace (c->check_struct (this) && likely (version.major == 1));
   }
 
   protected:
   FixedVersion	version;		/* Version of the head table--currently
 					 * 0x00010000u for version 1.0. */
   FixedVersion	fontRevision;		/* Set by font manufacturer. */
   ULONG		checkSumAdjustment;	/* To compute: set it to 0, sum the
 					 * entire font as ULONG, then store
--- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh
@@ -47,17 +47,17 @@ struct _hea
   static const hb_tag_t tableTag = HB_TAG('_','h','e','a');
 
   static const hb_tag_t hheaTag	= HB_OT_TAG_hhea;
   static const hb_tag_t vheaTag	= HB_OT_TAG_vhea;
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
+    return_trace (c->check_struct (this) && likely (version.major == 1));
   }
 
   public:
   FixedVersion	version;		/* 0x00010000u for version 1.0. */
   FWORD		ascender;		/* Typographic ascent. */
   FWORD		descender;		/* Typographic descent. */
   FWORD		lineGap;		/* Typographic line gap. */
   UFWORD	advanceMax;		/* Maximum advance width/height value in
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -57,17 +57,17 @@ struct _mtx
   static const hb_tag_t hmtxTag	= HB_OT_TAG_hmtx;
   static const hb_tag_t vmtxTag	= HB_OT_TAG_vmtx;
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   public:
   LongMetric	longMetric[VAR];	/* Paired advance width and leading
 					 * bearing values for each glyph. The
 					 * value numOfHMetrics comes from
 					 * the 'hhea' table. If the font is
 					 * monospaced, only one entry need
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -39,17 +39,17 @@ namespace OT {
 
 #define TRACE_DISPATCH(this, format) \
 	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "format %d", (int) format);
 
 
 #define NOT_COVERED		((unsigned int) -1)
-#define MAX_NESTING_LEVEL	8
+#define MAX_NESTING_LEVEL	6
 #define MAX_CONTEXT_LENGTH	64
 
 
 
 /*
  *
  * OpenType Layout Common Table Formats
  *
@@ -70,17 +70,17 @@ struct Record
   struct sanitize_closure_t {
     hb_tag_t tag;
     const void *list_base;
   };
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     const sanitize_closure_t closure = {tag, base};
-    return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base, &closure));
+    return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
   }
 
   Tag		tag;		/* 4-byte Tag identifier */
   OffsetTo<Type>
 		offset;		/* Offset from beginning of object holding
 				 * the Record */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -126,31 +126,31 @@ template <typename Type>
 struct RecordListOf : RecordArrayOf<Type>
 {
   inline const Type& operator [] (unsigned int i) const
   { return this+RecordArrayOf<Type>::operator [](i).offset; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
+    return_trace (RecordArrayOf<Type>::sanitize (c, this));
   }
 };
 
 
 struct RangeRecord
 {
   inline int cmp (hb_codepoint_t g) const {
     return g < start ? -1 : g <= end ? 0 : +1 ;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   inline bool intersects (const hb_set_t *glyphs) const {
     return glyphs->intersects (start, end);
   }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const {
@@ -206,17 +206,17 @@ struct LangSys
       return Index::NOT_FOUND_INDEX;
    return reqFeatureIndex;;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c,
 			const Record<LangSys>::sanitize_closure_t * = NULL) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c));
+    return_trace (c->check_struct (this) && featureIndex.sanitize (c));
   }
 
   Offset<>	lookupOrderZ;	/* = Null (reserved for an offset to a
 				 * reordering table) */
   USHORT	reqFeatureIndex;/* Index of a feature required for this
 				 * language system--if no required features
 				 * = 0xFFFFu */
   IndexArray	featureIndex;	/* Array of indices into the FeatureList */
@@ -246,17 +246,17 @@ struct Script
 
   inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
   inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
 			const Record<Script>::sanitize_closure_t * = NULL) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
+    return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
 
   protected:
   OffsetTo<LangSys>
 		defaultLangSys;	/* Offset to DefaultLangSys table--from
 				 * beginning of Script table--may be Null */
   RecordArrayOf<LangSys>
 		langSys;	/* Array of LangSysRecords--listed
@@ -269,17 +269,17 @@ typedef RecordListOf<Script> ScriptList;
 
 
 /* http://www.microsoft.com/typography/otspec/features_pt.htm#size */
 struct FeatureParamsSize
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
+    if (unlikely (!c->check_struct (this))) return_trace (false);
 
     /* This subtable has some "history", if you will.  Some earlier versions of
      * Adobe tools calculated the offset of the FeatureParams sutable from the
      * beginning of the FeatureList table!  Now, that is dealt with in the
      * Feature implementation.  But we still need to be able to tell junk from
      * real data.  Note: We don't check that the nameID actually exists.
      *
      * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk :
@@ -321,29 +321,29 @@ struct FeatureParamsSize
      *     "menu name ID"  > 32767 or
      *     menu name ID is not a name ID which is actually in the name table)
      *     fails test
      * Else
      *     passes test.
      */
 
     if (!designSize)
-      return TRACE_RETURN (false);
+      return_trace (false);
     else if (subfamilyID == 0 &&
 	     subfamilyNameID == 0 &&
 	     rangeStart == 0 &&
 	     rangeEnd == 0)
-      return TRACE_RETURN (true);
+      return_trace (true);
     else if (designSize < rangeStart ||
 	     designSize > rangeEnd ||
 	     subfamilyNameID < 256 ||
 	     subfamilyNameID > 32767)
-      return TRACE_RETURN (false);
+      return_trace (false);
     else
-      return TRACE_RETURN (true);
+      return_trace (true);
   }
 
   USHORT	designSize;	/* Represents the design size in 720/inch
 				 * units (decipoints).  The design size entry
 				 * must be non-zero.  When there is a design
 				 * size but no recommended size range, the
 				 * rest of the array will consist of zeros. */
   USHORT	subfamilyID;	/* Has no independent meaning, but serves
@@ -383,17 +383,17 @@ struct FeatureParamsSize
 /* http://www.microsoft.com/typography/otspec/features_pt.htm#ssxx */
 struct FeatureParamsStylisticSet
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* Right now minorVersion is at zero.  Which means, any table supports
      * the uiNameID field. */
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   USHORT	version;	/* (set to 0): This corresponds to a “minor”
 				 * version number. Additional data may be
 				 * added to the end of this Feature Parameters
 				 * table in the future. */
 
   USHORT	uiNameID;	/* The 'name' table name ID that specifies a
@@ -415,18 +415,18 @@ struct FeatureParamsStylisticSet
 };
 
 /* http://www.microsoft.com/typography/otspec/features_ae.htm#cv01-cv99 */
 struct FeatureParamsCharacterVariants
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) &&
-			 characters.sanitize (c));
+    return_trace (c->check_struct (this) &&
+		  characters.sanitize (c));
   }
 
   USHORT	format;			/* Format number is set to 0. */
   USHORT	featUILableNameID;	/* The ‘name’ table name ID that
 					 * specifies a string (or strings,
 					 * for multiple languages) for a
 					 * user-interface label for this
 					 * feature. (May be NULL.) */
@@ -457,22 +457,22 @@ struct FeatureParamsCharacterVariants
 };
 
 struct FeatureParams
 {
   inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const
   {
     TRACE_SANITIZE (this);
     if (tag == HB_TAG ('s','i','z','e'))
-      return TRACE_RETURN (u.size.sanitize (c));
+      return_trace (u.size.sanitize (c));
     if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */
-      return TRACE_RETURN (u.stylisticSet.sanitize (c));
+      return_trace (u.stylisticSet.sanitize (c));
     if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */
-      return TRACE_RETURN (u.characterVariants.sanitize (c));
-    return TRACE_RETURN (true);
+      return_trace (u.characterVariants.sanitize (c));
+    return_trace (true);
   }
 
   inline const FeatureParamsSize& get_size_params (hb_tag_t tag) const
   {
     if (tag == HB_TAG ('s','i','z','e'))
       return u.size;
     return Null(FeatureParamsSize);
   }
@@ -500,53 +500,53 @@ struct Feature
   inline const FeatureParams &get_feature_params (void) const
   { return this+featureParams; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
 			const Record<Feature>::sanitize_closure_t *closure) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
-      return TRACE_RETURN (false);
+      return_trace (false);
 
     /* Some earlier versions of Adobe tools calculated the offset of the
      * FeatureParams subtable from the beginning of the FeatureList table!
      *
      * If sanitizing "failed" for the FeatureParams subtable, try it with the
      * alternative location.  We would know sanitize "failed" if old value
      * of the offset was non-zero, but it's zeroed now.
      *
      * Only do this for the 'size' feature, since at the time of the faulty
      * Adobe tools, only the 'size' feature had FeatureParams defined.
      */
 
     OffsetTo<FeatureParams> orig_offset = featureParams;
     if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)))
-      return TRACE_RETURN (false);
+      return_trace (false);
 
     if (likely (orig_offset.is_null ()))
-      return TRACE_RETURN (true);
+      return_trace (true);
 
     if (featureParams == 0 && closure &&
 	closure->tag == HB_TAG ('s','i','z','e') &&
 	closure->list_base && closure->list_base < this)
     {
       unsigned int new_offset_int = (unsigned int) orig_offset -
 				    (((char *) this) - ((char *) closure->list_base));
 
       OffsetTo<FeatureParams> new_offset;
       /* Check that it did not overflow. */
       new_offset.set (new_offset_int);
       if (new_offset == new_offset_int &&
 	  c->try_set (&featureParams, new_offset) &&
 	  !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))
-	return TRACE_RETURN (false);
+	return_trace (false);
     }
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   OffsetTo<FeatureParams>
 		 featureParams;	/* Offset to Feature Parameters table (if one
 				 * has been defined for the feature), relative
 				 * to the beginning of the Feature Table; = Null
 				 * if not required */
   IndexArray	 lookupIndex;	/* Array of LookupList indices */
@@ -608,50 +608,50 @@ struct Lookup
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     unsigned int lookup_type = get_type ();
     TRACE_DISPATCH (this, lookup_type);
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
       typename context_t::return_t r = get_subtable<SubTableType> (i).dispatch (c, lookup_type);
       if (c->stop_sublookup_iteration (r))
-        return TRACE_RETURN (r);
+        return_trace (r);
     }
-    return TRACE_RETURN (c->default_return_value ());
+    return_trace (c->default_return_value ());
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 unsigned int lookup_type,
 			 uint32_t lookup_props,
 			 unsigned int num_subtables)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
     lookupType.set (lookup_type);
     lookupFlag.set (lookup_props & 0xFFFFu);
-    if (unlikely (!subTable.serialize (c, num_subtables))) return TRACE_RETURN (false);
+    if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
       markFilteringSet.set (lookup_props >> 16);
     }
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
-    if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
+    if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
       const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
-      if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
+      if (!markFilteringSet.sanitize (c)) return_trace (false);
     }
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   private:
   USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
   USHORT	lookupFlag;		/* Lookup qualifiers */
   ArrayOf<Offset<> >
 		subTable;		/* Array of SubTables */
   USHORT	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
@@ -680,29 +680,29 @@ struct CoverageFormat1
     return i;
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
     glyphArray.len.set (num_glyphs);
-    if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend (glyphArray))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       glyphArray[i] = glyphs[i];
     glyphs.advance (num_glyphs);
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (glyphArray.sanitize (c));
+    return_trace (glyphArray.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     return glyphs->has (glyphArray[index]);
   }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const {
@@ -749,47 +749,47 @@ struct CoverageFormat2
     return NOT_COVERED;
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
 
-    if (unlikely (!num_glyphs)) return TRACE_RETURN (true);
+    if (unlikely (!num_glyphs)) return_trace (true);
 
     unsigned int num_ranges = 1;
     for (unsigned int i = 1; i < num_glyphs; i++)
       if (glyphs[i - 1] + 1 != glyphs[i])
         num_ranges++;
     rangeRecord.len.set (num_ranges);
-    if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend (rangeRecord))) return_trace (false);
 
     unsigned int range = 0;
     rangeRecord[range].start = glyphs[0];
     rangeRecord[range].value.set (0);
     for (unsigned int i = 1; i < num_glyphs; i++)
       if (glyphs[i - 1] + 1 != glyphs[i]) {
 	range++;
 	rangeRecord[range].start = glyphs[i];
 	rangeRecord[range].value.set (i);
         rangeRecord[range].end = glyphs[i];
       } else {
         rangeRecord[range].end = glyphs[i];
       }
     glyphs.advance (num_glyphs);
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (rangeRecord.sanitize (c));
+    return_trace (rangeRecord.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     unsigned int i;
     unsigned int count = rangeRecord.len;
     for (i = 0; i < count; i++) {
       const RangeRecord &range = rangeRecord[i];
       if (range.value <= index &&
@@ -859,37 +859,37 @@ struct Coverage
     }
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
     unsigned int num_ranges = 1;
     for (unsigned int i = 1; i < num_glyphs; i++)
       if (glyphs[i - 1] + 1 != glyphs[i])
         num_ranges++;
     u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs));
-    case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, num_glyphs));
-    default:return TRACE_RETURN (false);
+    case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs));
+    case 2: return_trace (u.format2.serialize (c, glyphs, num_glyphs));
+    default:return_trace (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    if (!u.format.sanitize (c)) return_trace (false);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   inline bool intersects (const hb_set_t *glyphs) const {
     /* TODO speed this up */
     Coverage::Iter iter;
     for (iter.init (*this); iter.more (); iter.next ()) {
       if (glyphs->has (iter.get_glyph ()))
@@ -988,17 +988,17 @@ struct ClassDefFormat1
     if (unlikely (i < classValue.len))
       return classValue[i];
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
+    return_trace (c->check_struct (this) && classValue.sanitize (c));
   }
 
   template <typename set_t>
   inline void add_class (set_t *glyphs, unsigned int klass) const {
     unsigned int count = classValue.len;
     for (unsigned int i = 0; i < count; i++)
       if (classValue[i] == klass)
         glyphs->add (startGlyph + i);
@@ -1045,17 +1045,17 @@ struct ClassDefFormat2
     if (unlikely (i != -1))
       return rangeRecord[i].value;
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (rangeRecord.sanitize (c));
+    return_trace (rangeRecord.sanitize (c));
   }
 
   template <typename set_t>
   inline void add_class (set_t *glyphs, unsigned int klass) const {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].value == klass)
         rangeRecord[i].add_coverage (glyphs);
@@ -1103,21 +1103,21 @@ struct ClassDef
     case 2: return u.format2.get_class(glyph_id);
     default:return 0;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    if (!u.format.sanitize (c)) return_trace (false);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   inline void add_class (hb_set_t *glyphs, unsigned int klass) const {
     switch (u.format) {
     case 1: u.format1.add_class (glyphs, klass); return;
     case 2: u.format2.add_class (glyphs, klass); return;
     default:return;
@@ -1196,17 +1196,17 @@ struct Device
     unsigned int f = deltaFormat;
     if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
     return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
+    return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
   protected:
   USHORT	startSize;		/* Smallest size to correct--in ppem */
   USHORT	endSize;		/* Largest size to correct--in ppem */
   USHORT	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
 					 * 1	Signed 2-bit value, 8 values per uint16
 					 * 2	Signed 4-bit value, 4 values per uint16
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -69,17 +69,17 @@ struct AttachList
     }
 
     return points.len;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
 
   protected:
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table -- from
 					 * beginning of AttachList table */
   OffsetArrayOf<AttachPoint>
 		attachPoint;		/* Array of AttachPoint tables
@@ -100,17 +100,17 @@ struct CaretValueFormat1
   inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id HB_UNUSED) const
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   protected:
   USHORT	caretValueFormat;	/* Format identifier--format = 1 */
   SHORT		coordinate;		/* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -127,17 +127,17 @@ struct CaretValueFormat2
       return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
     else
       return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   protected:
   USHORT	caretValueFormat;	/* Format identifier--format = 2 */
   USHORT	caretValuePoint;	/* Contour point index on glyph */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -151,17 +151,17 @@ struct CaretValueFormat3
     return HB_DIRECTION_IS_HORIZONTAL (direction) ?
            font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font) :
            font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
+    return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
   protected:
   USHORT	caretValueFormat;	/* Format identifier--format = 3 */
   SHORT		coordinate;		/* X or Y value, in design units */
   OffsetTo<Device>
 		deviceTable;		/* Offset to Device table for X or Y
 					 * value--from beginning of CaretValue
@@ -180,22 +180,22 @@ struct CaretValue
     case 3: return u.format3.get_caret_value (font, direction, glyph_id);
     default:return 0;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    if (!u.format.sanitize (c)) return_trace (false);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    case 3: return TRACE_RETURN (u.format3.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 3: return_trace (u.format3.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   CaretValueFormat1	format1;
   CaretValueFormat2	format2;
@@ -222,17 +222,17 @@ struct LigGlyph
     }
 
     return carets.len;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (carets.sanitize (c, this));
+    return_trace (carets.sanitize (c, this));
   }
 
   protected:
   OffsetArrayOf<CaretValue>
 		carets;			/* Offset array of CaretValue tables
 					 * --from beginning of LigGlyph table
 					 * --in increasing coordinate order */
   public:
@@ -257,17 +257,17 @@ struct LigCaretList
     }
     const LigGlyph &lig_glyph = this+ligGlyph[index];
     return lig_glyph.get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
 
   protected:
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of LigCaretList table */
   OffsetArrayOf<LigGlyph>
 		ligGlyph;		/* Array of LigGlyph tables
@@ -280,17 +280,17 @@ struct LigCaretList
 struct MarkGlyphSetsFormat1
 {
   inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   ArrayOf<OffsetTo<Coverage, ULONG> >
 		coverage;		/* Array of long offsets to mark set
 					 * coverage tables */
   public:
@@ -305,20 +305,20 @@ struct MarkGlyphSets
     case 1: return u.format1.covers (set_index, glyph_id);
     default:return false;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    if (!u.format.sanitize (c)) return_trace (false);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return_trace (u.format1.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkGlyphSetsFormat1	format1;
   } u;
@@ -371,23 +371,23 @@ struct GDEF
 
   inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002u && markGlyphSetsDef[0] != 0; }
   inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (version.sanitize (c) &&
-			 likely (version.major == 1) &&
-			 glyphClassDef.sanitize (c, this) &&
-			 attachList.sanitize (c, this) &&
-			 ligCaretList.sanitize (c, this) &&
-			 markAttachClassDef.sanitize (c, this) &&
-			 (version.to_int () < 0x00010002u || markGlyphSetsDef[0].sanitize (c, this)));
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  glyphClassDef.sanitize (c, this) &&
+		  attachList.sanitize (c, this) &&
+		  ligCaretList.sanitize (c, this) &&
+		  markAttachClassDef.sanitize (c, this) &&
+		  (version.to_int () < 0x00010002u || markGlyphSetsDef[0].sanitize (c, this)));
   }
 
 
   /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
    * glyph class and other bits, and high 8-bit gthe mark attachment type (if any).
    * Not to be confused with lookup_props which is very similar. */
   inline unsigned int get_glyph_props (hb_codepoint_t glyph) const
   {
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -176,68 +176,68 @@ struct ValueFormat : USHORT
   inline bool has_device (void) const {
     unsigned int format = *this;
     return (format & devices) != 0;
   }
 
   inline bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
+    return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
   }
 
   inline bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     unsigned int len = get_len ();
 
-    if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
+    if (!c->check_array (values, get_size (), count)) return_trace (false);
 
-    if (!has_device ()) return TRACE_RETURN (true);
+    if (!has_device ()) return_trace (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return TRACE_RETURN (false);
+        return_trace (false);
       values += len;
     }
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
   inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
   {
     TRACE_SANITIZE (this);
 
-    if (!has_device ()) return TRACE_RETURN (true);
+    if (!has_device ()) return_trace (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return TRACE_RETURN (false);
+        return_trace (false);
       values += stride;
     }
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 };
 
 
 struct AnchorFormat1
 {
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
 			  hb_position_t *x, hb_position_t *y) const
   {
       *x = font->em_scale_x (xCoordinate);
       *y = font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   SHORT		xCoordinate;		/* Horizontal value--in design units */
   SHORT		yCoordinate;		/* Vertical value--in design units */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -257,17 +257,17 @@ struct AnchorFormat2
              font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
       *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
       *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 2 */
   SHORT		xCoordinate;		/* Horizontal value--in design units */
   SHORT		yCoordinate;		/* Vertical value--in design units */
   USHORT	anchorPoint;		/* Index to glyph contour point */
   public:
@@ -286,17 +286,17 @@ struct AnchorFormat3
 	*x += (this+xDeviceTable).get_x_delta (font);
       if (font->y_ppem)
 	*y += (this+yDeviceTable).get_x_delta (font);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
+    return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 3 */
   SHORT		xCoordinate;		/* Horizontal value--in design units */
   SHORT		yCoordinate;		/* Vertical value--in design units */
   OffsetTo<Device>
 		xDeviceTable;		/* Offset to Device table for X
@@ -322,22 +322,22 @@ struct Anchor
     case 3: u.format3.get_anchor (font, glyph_id, x, y); return;
     default:						 return;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    if (!u.format.sanitize (c)) return_trace (false);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    case 3: return TRACE_RETURN (u.format3.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 3: return_trace (u.format3.sanitize (c));
+    default:return_trace (true);
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   AnchorFormat1		format1;
   AnchorFormat2		format2;
@@ -355,23 +355,23 @@ struct AnchorMatrix
     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
     *found = !matrixZ[row * cols + col].is_null ();
     return this+matrixZ[row * cols + col];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
   {
     TRACE_SANITIZE (this);
-    if (!c->check_struct (this)) return TRACE_RETURN (false);
-    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
+    if (!c->check_struct (this)) return_trace (false);
+    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return_trace (false);
     unsigned int count = rows * cols;
-    if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return TRACE_RETURN (false);
+    if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!matrixZ[i].sanitize (c, this)) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+      if (!matrixZ[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
   }
 
   USHORT	rows;			/* Number of rows */
   protected:
   OffsetTo<Anchor>
 		matrixZ[VAR];		/* Matrix of offsets to Anchor tables--
 					 * from beginning of AnchorMatrix table */
   public:
@@ -381,17 +381,17 @@ struct AnchorMatrix
 
 struct MarkRecord
 {
   friend struct MarkArray;
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
+    return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
   protected:
   USHORT	klass;			/* Class defined for this mark */
   OffsetTo<Anchor>
 		markAnchor;		/* Offset to Anchor table--from
 					 * beginning of MarkArray table */
   public:
@@ -410,36 +410,36 @@ struct MarkArray : ArrayOf<MarkRecord>	/
     const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
     unsigned int mark_class = record.klass;
 
     const Anchor& mark_anchor = this + record.markAnchor;
     bool found;
     const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
     /* If this subtable doesn't have an anchor for this base and this class,
      * return false such that the subsequent subtables have a chance at it. */
-    if (unlikely (!found)) return TRACE_RETURN (false);
+    if (unlikely (!found)) return_trace (false);
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
     mark_anchor.get_anchor (c->font, buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c->font, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
     hb_glyph_position_t &o = buffer->cur_pos();
     o.x_offset = base_x - mark_x;
     o.y_offset = base_y - mark_y;
     o.attach_lookback() = buffer->idx - glyph_pos;
 
     buffer->idx++;
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
+    return_trace (ArrayOf<MarkRecord>::sanitize (c, this));
   }
 };
 
 
 /* Lookups */
 
 struct SinglePosFormat1
 {
@@ -454,31 +454,31 @@ struct SinglePosFormat1
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
 			     values, buffer->cur_pos());
 
     buffer->idx++;
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this)
-        && coverage.sanitize (c, this)
-	&& valueFormat.sanitize_value (c, this, values));
+    return_trace (c->check_struct (this) &&
+		  coverage.sanitize (c, this) &&
+		  valueFormat.sanitize_value (c, this, values));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat;		/* Defines the types of data in the
@@ -503,34 +503,34 @@ struct SinglePosFormat2
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
-    if (likely (index >= valueCount)) return TRACE_RETURN (false);
+    if (likely (index >= valueCount)) return_trace (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
 			     &values[index * valueFormat.get_len ()],
 			     buffer->cur_pos());
 
     buffer->idx++;
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& valueFormat.sanitize_values (c, this, values, valueCount));
+    return_trace (c->check_struct (this) &&
+		  coverage.sanitize (c, this) &&
+		  valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat;		/* Defines the types of data in the
@@ -543,21 +543,21 @@ struct SinglePosFormat2
 };
 
 struct SinglePos
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    case 2: return TRACE_RETURN (c->dispatch (u.format2));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    case 2: return_trace (c->dispatch (u.format2));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   SinglePosFormat1	format1;
   SinglePosFormat2	format2;
@@ -610,17 +610,17 @@ struct PairSet
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
 
     const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ);
     unsigned int count = len;
 
     /* Hand-coded bsearch. */
     if (unlikely (!count))
-      return TRACE_RETURN (false);
+      return_trace (false);
     hb_codepoint_t x = buffer->info[pos].codepoint;
     int min = 0, max = (int) count - 1;
     while (min <= max)
     {
       int mid = (min + max) / 2;
       const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
       hb_codepoint_t mid_x = record->secondGlyph;
       if (x < mid_x)
@@ -631,40 +631,40 @@ struct PairSet
       {
 	valueFormats[0].apply_value (c->font, c->direction, this,
 				     &record->values[0], buffer->cur_pos());
 	valueFormats[1].apply_value (c->font, c->direction, this,
 				     &record->values[len1], buffer->pos[pos]);
 	if (len2)
 	  pos++;
 	buffer->idx = pos;
-	return TRACE_RETURN (true);
+	return_trace (true);
       }
     }
 
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   struct sanitize_closure_t {
     const void *base;
     const ValueFormat *valueFormats;
     unsigned int len1; /* valueFormats[0].get_len() */
     unsigned int stride; /* 1 + len1 + len2 */
   };
 
   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
-       && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
+       && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return_trace (false);
 
     unsigned int count = len;
     const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
-    return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
-		      && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
+    return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) &&
+		  closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
 
   protected:
   USHORT	len;			/* Number of PairValueRecords */
   USHORT	arrayZ[VAR];		/* Array of PairValueRecords--ordered
 					 * by GlyphID of the second glyph */
   public:
   DEFINE_SIZE_ARRAY (2, arrayZ);
@@ -686,39 +686,41 @@ struct PairPosFormat1
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
-    if (!skippy_iter.next ()) return TRACE_RETURN (false);
+    if (!skippy_iter.next ()) return_trace (false);
 
-    return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
+    return_trace ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
 
+    if (!c->check_struct (this)) return_trace (false);
+
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     PairSet::sanitize_closure_t closure = {
       this,
       &valueFormat1,
       len1,
       1 + len1 + len2
     };
 
-    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
+    return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat1;		/* Defines the types of data in
@@ -757,59 +759,59 @@ struct PairPosFormat2
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
-    if (!skippy_iter.next ()) return TRACE_RETURN (false);
+    if (!skippy_iter.next ()) return_trace (false);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int record_len = len1 + len2;
 
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
-    if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
+    if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c->font, c->direction, this,
 			      v, buffer->cur_pos());
     valueFormat2.apply_value (c->font, c->direction, this,
 			      v + len1, buffer->pos[skippy_iter.idx]);
 
     buffer->idx = skippy_iter.idx;
     if (len2)
       buffer->idx++;
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
        && classDef1.sanitize (c, this)
-       && classDef2.sanitize (c, this))) return TRACE_RETURN (false);
+       && classDef2.sanitize (c, this))) return_trace (false);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int stride = len1 + len2;
     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
-    return TRACE_RETURN (c->check_array (values, record_size, count) &&
-			 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
-			 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
+    return_trace (c->check_array (values, record_size, count) &&
+		  valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+		  valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat1;		/* ValueRecord definition--for the
@@ -838,21 +840,21 @@ struct PairPosFormat2
 };
 
 struct PairPos
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    case 2: return TRACE_RETURN (c->dispatch (u.format2));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    case 2: return_trace (c->dispatch (u.format2));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   PairPosFormat1	format1;
   PairPosFormat2	format2;
@@ -862,17 +864,17 @@ struct PairPos
 
 struct EntryExitRecord
 {
   friend struct CursivePosFormat1;
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
+    return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
   protected:
   OffsetTo<Anchor>
 		entryAnchor;		/* Offset to EntryAnchor table--from
 					 * beginning of CursivePos
 					 * subtable--may be NULL */
   OffsetTo<Anchor>
@@ -900,27 +902,27 @@ struct CursivePosFormat1
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
 
     /* We don't handle mark glyphs here. */
-    if (unlikely (_hb_glyph_info_is_mark (&buffer->cur()))) return TRACE_RETURN (false);
+    if (unlikely (_hb_glyph_info_is_mark (&buffer->cur()))) return_trace (false);
 
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
-    if (!this_record.exitAnchor) return TRACE_RETURN (false);
+    if (!this_record.exitAnchor) return_trace (false);
 
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
-    if (!skippy_iter.next ()) return TRACE_RETURN (false);
+    if (!skippy_iter.next ()) return_trace (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
-    if (!next_record.entryAnchor) return TRACE_RETURN (false);
+    if (!next_record.entryAnchor) return_trace (false);
 
     unsigned int i = buffer->idx;
     unsigned int j = skippy_iter.idx;
 
     hb_position_t entry_x, entry_y, exit_x, exit_y;
     (this+this_record.exitAnchor).get_anchor (c->font, buffer->info[i].codepoint, &exit_x, &exit_y);
     (this+next_record.entryAnchor).get_anchor (c->font, buffer->info[j].codepoint, &entry_x, &entry_y);
 
@@ -992,23 +994,23 @@ struct CursivePosFormat1
 
     pos[child].cursive_chain() = parent - child;
     if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
       pos[child].y_offset = y_offset;
     else
       pos[child].x_offset = x_offset;
 
     buffer->idx = j;
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ArrayOf<EntryExitRecord>
@@ -1019,20 +1021,20 @@ struct CursivePosFormat1
 };
 
 struct CursivePos
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   CursivePosFormat1	format1;
   } u;
@@ -1058,43 +1060,46 @@ struct MarkBasePosFormat1
     return this+markCoverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* now we search backwards for a non-mark glyph */
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
-      if (!skippy_iter.prev ()) return TRACE_RETURN (false);
+      if (!skippy_iter.prev ()) return_trace (false);
       /* We only want to attach to the first of a MultipleSubst sequence.  Reject others. */
       if (0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx])) break;
       skippy_iter.reject ();
     } while (1);
 
     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
-    if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { /*return TRACE_RETURN (false);*/ }
+    if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { /*return_trace (false);*/ }
 
     unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
-    if (base_index == NOT_COVERED) return TRACE_RETURN (false);
+    if (base_index == NOT_COVERED) return_trace (false);
 
-    return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
+    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) &&
-			 markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount));
+    return_trace (c->check_struct (this) &&
+		  markCoverage.sanitize (c, this) &&
+		  baseCoverage.sanitize (c, this) &&
+		  markArray.sanitize (c, this) &&
+		  baseArray.sanitize (c, this, (unsigned int) classCount));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to MarkCoverage table--from
 					 * beginning of MarkBasePos subtable */
   OffsetTo<Coverage>
@@ -1112,20 +1117,20 @@ struct MarkBasePosFormat1
 };
 
 struct MarkBasePos
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkBasePosFormat1	format1;
   } u;
@@ -1156,59 +1161,62 @@ struct MarkLigPosFormat1
     return this+markCoverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* now we search backwards for a non-mark glyph */
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    if (!skippy_iter.prev ()) return TRACE_RETURN (false);
+    if (!skippy_iter.prev ()) return_trace (false);
 
     /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
-    if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { /*return TRACE_RETURN (false);*/ }
+    if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { /*return_trace (false);*/ }
 
     unsigned int j = skippy_iter.idx;
     unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
-    if (lig_index == NOT_COVERED) return TRACE_RETURN (false);
+    if (lig_index == NOT_COVERED) return_trace (false);
 
     const LigatureArray& lig_array = this+ligatureArray;
     const LigatureAttach& lig_attach = lig_array[lig_index];
 
     /* Find component to attach to */
     unsigned int comp_count = lig_attach.rows;
-    if (unlikely (!comp_count)) return TRACE_RETURN (false);
+    if (unlikely (!comp_count)) return_trace (false);
 
     /* We must now check whether the ligature ID of the current mark glyph
      * is identical to the ligature ID of the found ligature.  If yes, we
      * can directly use the component index.  If not, we attach the mark
      * glyph to the last component of the ligature. */
     unsigned int comp_index;
     unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
     unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
     unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
     if (lig_id && lig_id == mark_id && mark_comp > 0)
       comp_index = MIN (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1;
     else
       comp_index = comp_count - 1;
 
-    return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
+    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) &&
-			 markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount));
+    return_trace (c->check_struct (this) &&
+		  markCoverage.sanitize (c, this) &&
+		  ligatureCoverage.sanitize (c, this) &&
+		  markArray.sanitize (c, this) &&
+		  ligatureArray.sanitize (c, this, (unsigned int) classCount));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to Mark Coverage table--from
 					 * beginning of MarkLigPos subtable */
   OffsetTo<Coverage>
@@ -1227,20 +1235,20 @@ struct MarkLigPosFormat1
 };
 
 struct MarkLigPos
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkLigPosFormat1	format1;
   } u;
@@ -1266,25 +1274,25 @@ struct MarkMarkPosFormat1
     return this+mark1Coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int mark1_index = (this+mark1Coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (mark1_index == NOT_COVERED)) return_trace (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
-    if (!skippy_iter.prev ()) return TRACE_RETURN (false);
+    if (!skippy_iter.prev ()) return_trace (false);
 
-    if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return TRACE_RETURN (false); }
+    if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return_trace (false); }
 
     unsigned int j = skippy_iter.idx;
 
     unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
     unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
     unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
     unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
 
@@ -1296,31 +1304,33 @@ struct MarkMarkPosFormat1
     } else {
       /* If ligature ids don't match, it may be the case that one of the marks
        * itself is a ligature.  In which case match. */
       if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
 	goto good;
     }
 
     /* Didn't match. */
-    return TRACE_RETURN (false);
+    return_trace (false);
 
     good:
     unsigned int mark2_index = (this+mark2Coverage).get_coverage  (buffer->info[j].codepoint);
-    if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
+    if (mark2_index == NOT_COVERED) return_trace (false);
 
-    return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
+    return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) &&
-			 mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this)
-			 && mark2Array.sanitize (c, this, (unsigned int) classCount));
+    return_trace (c->check_struct (this) &&
+		  mark1Coverage.sanitize (c, this) &&
+		  mark2Coverage.sanitize (c, this) &&
+		  mark1Array.sanitize (c, this) &&
+		  mark2Array.sanitize (c, this, (unsigned int) classCount));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		mark1Coverage;		/* Offset to Combining Mark1 Coverage
 					 * table--from beginning of MarkMarkPos
 					 * subtable */
@@ -1340,20 +1350,20 @@ struct MarkMarkPosFormat1
 };
 
 struct MarkMarkPos
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkMarkPosFormat1	format1;
   } u;
@@ -1391,29 +1401,28 @@ struct PosLookupSubTable
     ChainContext	= 8,
     Extension		= 9
   };
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
-    /* The sub_format passed to may_dispatch is unnecessary but harmless. */
-    if (unlikely (!c->may_dispatch (this, &u.sub_format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ());
     switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.dispatch (c));
-    case Pair:			return TRACE_RETURN (u.pair.dispatch (c));
-    case Cursive:		return TRACE_RETURN (u.cursive.dispatch (c));
-    case MarkBase:		return TRACE_RETURN (u.markBase.dispatch (c));
-    case MarkLig:		return TRACE_RETURN (u.markLig.dispatch (c));
-    case MarkMark:		return TRACE_RETURN (u.markMark.dispatch (c));
-    case Context:		return TRACE_RETURN (u.context.dispatch (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.dispatch (c));
-    case Extension:		return TRACE_RETURN (u.extension.dispatch (c));
-    default:			return TRACE_RETURN (c->default_return_value ());
+    case Single:		return_trace (u.single.dispatch (c));
+    case Pair:			return_trace (u.pair.dispatch (c));
+    case Cursive:		return_trace (u.cursive.dispatch (c));
+    case MarkBase:		return_trace (u.markBase.dispatch (c));
+    case MarkLig:		return_trace (u.markLig.dispatch (c));
+    case MarkMark:		return_trace (u.markMark.dispatch (c));
+    case Context:		return_trace (u.context.dispatch (c));
+    case ChainContext:		return_trace (u.chainContext.dispatch (c));
+    case Extension:		return_trace (u.extension.dispatch (c));
+    default:			return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		sub_format;
   SinglePos		single;
   PairPos		pair;
@@ -1438,23 +1447,23 @@ struct PosLookup : Lookup
   inline bool is_reverse (void) const
   {
     return false;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    return TRACE_RETURN (dispatch (c));
+    return_trace (dispatch (c));
   }
 
   inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    return TRACE_RETURN (dispatch (c));
+    return_trace (dispatch (c));
   }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
     hb_add_coverage_context_t<set_t> c (glyphs);
     dispatch (&c);
   }
@@ -1466,18 +1475,18 @@ struct PosLookup : Lookup
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   { return Lookup::dispatch<PosLookupSubTable> (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
-    return TRACE_RETURN (dispatch (c));
+    if (unlikely (!Lookup::sanitize (c))) return_trace (false);
+    return_trace (dispatch (c));
   }
 };
 
 typedef OffsetListOf<PosLookup> PosLookupList;
 
 /*
  * GPOS -- The Glyph Positioning Table
  */
@@ -1490,19 +1499,19 @@ struct GPOS : GSUBGPOS
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer);
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
+    if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
     const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
-    return TRACE_RETURN (list.sanitize (c, this));
+    return_trace (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 static void
 reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent)
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -62,50 +62,50 @@ struct SingleSubstFormat1
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
-    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+    return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     /* According to the Adobe Annotated OpenType Suite, result is always
      * limited to 16bit. */
     glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
     c->replace_glyph (glyph_id);
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs,
 			 int delta)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
+    return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   SHORT		deltaGlyphID;		/* Add to original GlyphID to get
@@ -139,50 +139,50 @@ struct SingleSubstFormat2
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
-    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+    return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
-    if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
+    if (unlikely (index >= substitute.len)) return_trace (false);
 
     glyph_id = substitute[index];
     c->replace_glyph (glyph_id);
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<GlyphID> &substitutes,
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return_trace (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
+    return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   ArrayOf<GlyphID>
@@ -195,46 +195,46 @@ struct SingleSubstFormat2
 struct SingleSubst
 {
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<GlyphID> &substitutes,
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 2;
     int delta = 0;
     if (num_glyphs) {
       format = 1;
       /* TODO(serialize) check for wrap-around */
       delta = substitutes[0] - glyphs[0];
       for (unsigned int i = 1; i < num_glyphs; i++)
 	if (delta != substitutes[i] - glyphs[i]) {
 	  format = 2;
 	  break;
 	}
     }
     u.format.set (format);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs, delta));
-    case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, substitutes, num_glyphs));
-    default:return TRACE_RETURN (false);
+    case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs, delta));
+    case 2: return_trace (u.format2.serialize (c, glyphs, substitutes, num_glyphs));
+    default:return_trace (false);
     }
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    case 2: return TRACE_RETURN (c->dispatch (u.format2));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    case 2: return_trace (c->dispatch (u.format2));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   SingleSubstFormat1	format1;
   SingleSubstFormat2	format2;
@@ -268,52 +268,52 @@ struct Sequence
     /* TODO:
      * Testing shows that Uniscribe actually allows zero-len susbstitute,
      * which essentially deletes a glyph.  We don't allow for now.  It
      * can be confusing to the client since the cluster from the deleted
      * glyph won't be merged with any output cluster...  Also, currently
      * buffer->move_to() makes assumptions about this too.  Perhaps fix
      * in the future after figuring out what to do with the clusters.
      */
-    if (unlikely (!count)) return TRACE_RETURN (false);
+    if (unlikely (!count)) return_trace (false);
 
     /* Special-case to make it in-place and not consider this
      * as a "multiplied" substitution. */
     if (unlikely (count == 1))
     {
       c->replace_glyph (substitute.array[0]);
-      return TRACE_RETURN (true);
+      return_trace (true);
     }
 
     unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
 			 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
 
     for (unsigned int i = 0; i < count; i++) {
       _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
       c->output_glyph_for_component (substitute.array[i], klass);
     }
     c->buffer->skip_glyph ();
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return_trace (false);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (substitute.sanitize (c));
+    return_trace (substitute.sanitize (c));
   }
 
   protected:
   ArrayOf<GlyphID>
 		substitute;		/* String of GlyphIDs to substitute */
   public:
   DEFINE_SIZE_ARRAY (2, substitute);
 };
@@ -342,51 +342,51 @@ struct MultipleSubstFormat1
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
-    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+    return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
-    return TRACE_RETURN ((this+sequence[index]).apply (c));
+    return_trace ((this+sequence[index]).apply (c));
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<unsigned int> &substitute_len_list,
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!sequence.serialize (c, num_glyphs))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       if (unlikely (!sequence[i].serialize (c, this).serialize (c,
 								substitute_glyphs_list,
-								substitute_len_list[i]))) return TRACE_RETURN (false);
+								substitute_len_list[i]))) return_trace (false);
     substitute_len_list.advance (num_glyphs);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<Sequence>
@@ -400,33 +400,33 @@ struct MultipleSubst
 {
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<unsigned int> &substitute_len_list,
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list));
-    default:return TRACE_RETURN (false);
+    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list));
+    default:return_trace (false);
     }
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   MultipleSubstFormat1	format1;
   } u;
@@ -468,69 +468,69 @@ struct AlternateSubstFormat1
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
-    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+    return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const AlternateSet &alt_set = this+alternateSet[index];
 
-    if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
+    if (unlikely (!alt_set.len)) return_trace (false);
 
     hb_mask_t glyph_mask = c->buffer->cur().mask;
     hb_mask_t lookup_mask = c->lookup_mask;
 
     /* Note: This breaks badly if two features enabled this lookup together. */
     unsigned int shift = _hb_ctz (lookup_mask);
     unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
 
-    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false);
+    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return_trace (false);
 
     glyph_id = alt_set[alt_index - 1];
 
     c->replace_glyph (glyph_id);
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<unsigned int> &alternate_len_list,
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!alternateSet.serialize (c, num_glyphs))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
 								    alternate_glyphs_list,
-								    alternate_len_list[i]))) return TRACE_RETURN (false);
+								    alternate_len_list[i]))) return_trace (false);
     alternate_len_list.advance (num_glyphs);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<AlternateSet>
@@ -544,33 +544,33 @@ struct AlternateSubst
 {
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<unsigned int> &alternate_len_list,
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list));
-    default:return TRACE_RETURN (false);
+    case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list));
+    default:return_trace (false);
     }
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   AlternateSubstFormat1	format1;
   } u;
@@ -597,84 +597,84 @@ struct Ligature
       c->input->add (component[i]);
     c->output->add (ligGlyph);
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     if (c->len != component.len)
-      return TRACE_RETURN (false);
+      return_trace (false);
 
     for (unsigned int i = 1; i < c->len; i++)
       if (likely (c->glyphs[i] != component[i]))
-	return TRACE_RETURN (false);
+	return_trace (false);
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = component.len;
 
-    if (unlikely (!count)) return TRACE_RETURN (false);
+    if (unlikely (!count)) return_trace (false);
 
     /* Special-case to make it in-place and not consider this
      * as a "ligated" substitution. */
     if (unlikely (count == 1))
     {
       c->replace_glyph (ligGlyph);
-      return TRACE_RETURN (true);
+      return_trace (true);
     }
 
     bool is_mark_ligature = false;
     unsigned int total_component_count = 0;
 
     unsigned int match_length = 0;
     unsigned int match_positions[MAX_CONTEXT_LENGTH];
 
     if (likely (!match_input (c, count,
 			      &component[1],
 			      match_glyph,
 			      NULL,
 			      &match_length,
 			      match_positions,
 			      &is_mark_ligature,
 			      &total_component_count)))
-      return TRACE_RETURN (false);
+      return_trace (false);
 
     ligate_input (c,
 		  count,
 		  match_positions,
 		  match_length,
 		  ligGlyph,
 		  is_mark_ligature,
 		  total_component_count);
 
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 GlyphID ligature,
 			 Supplier<GlyphID> &components, /* Starting from second */
 			 unsigned int num_components /* Including first component */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
     ligGlyph = ligature;
-    if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!component.serialize (c, components, num_components))) return_trace (false);
+    return_trace (true);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
+    return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
   }
 
   protected:
   GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
   HeadlessArrayOf<GlyphID>
 		component;		/* Array of component GlyphIDs--start
 					 * with the second  component--ordered
 					 * in writing direction */
@@ -703,57 +703,57 @@ struct LigatureSet
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
     {
       const Ligature &lig = this+ligature[i];
       if (lig.would_apply (c))
-        return TRACE_RETURN (true);
+        return_trace (true);
     }
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
     {
       const Ligature &lig = this+ligature[i];
-      if (lig.apply (c)) return TRACE_RETURN (true);
+      if (lig.apply (c)) return_trace (true);
     }
 
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &ligatures,
 			 Supplier<unsigned int> &component_count_list,
 			 unsigned int num_ligatures,
 			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!ligature.serialize (c, num_ligatures))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!ligature.serialize (c, num_ligatures))) return_trace (false);
     for (unsigned int i = 0; i < num_ligatures; i++)
       if (unlikely (!ligature[i].serialize (c, this).serialize (c,
 								ligatures[i],
 								component_list,
-								component_count_list[i]))) return TRACE_RETURN (false);
+								component_count_list[i]))) return_trace (false);
     ligatures.advance (num_ligatures);
     component_count_list.advance (num_ligatures);
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (ligature.sanitize (c, this));
+    return_trace (ligature.sanitize (c, this));
   }
 
   protected:
   OffsetArrayOf<Ligature>
 		ligature;		/* Array LigatureSet tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, ligature);
@@ -785,60 +785,60 @@ struct LigatureSubstFormat1
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
-    return TRACE_RETURN (lig_set.would_apply (c));
+    return_trace (lig_set.would_apply (c));
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
-    return TRACE_RETURN (lig_set.apply (c));
+    return_trace (lig_set.apply (c));
   }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &first_glyphs,
 			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
 			 unsigned int num_first_glyphs,
 			 Supplier<GlyphID> &ligatures_list,
 			 Supplier<unsigned int> &component_count_list,
 			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return_trace (false);
     for (unsigned int i = 0; i < num_first_glyphs; i++)
       if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
 								   ligatures_list,
 								   component_count_list,
 								   ligature_per_first_glyph_count_list[i],
-								   component_list))) return TRACE_RETURN (false);
+								   component_list))) return_trace (false);
     ligature_per_first_glyph_count_list.advance (num_first_glyphs);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return TRACE_RETURN (false);
-    return TRACE_RETURN (true);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false);
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<LigatureSet>
@@ -854,34 +854,39 @@ struct LigatureSubst
 			 Supplier<GlyphID> &first_glyphs,
 			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
 			 unsigned int num_first_glyphs,
 			 Supplier<GlyphID> &ligatures_list,
 			 Supplier<unsigned int> &component_count_list,
 			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, num_first_glyphs,
-						      ligatures_list, component_count_list, component_list));
-    default:return TRACE_RETURN (false);
+    case 1: return_trace (u.format1.serialize (c,
+					       first_glyphs,
+					       ligature_per_first_glyph_count_list,
+					       num_first_glyphs,
+					       ligatures_list,
+					       component_count_list,
+					       component_list));
+    default:return_trace (false);
     }
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   LigatureSubstFormat1	format1;
   } u;
@@ -954,59 +959,59 @@ struct ReverseChainSingleSubstFormat1
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
-    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+    return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
-      return TRACE_RETURN (false); /* No chaining to this type */
+      return_trace (false); /* No chaining to this type */
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
 
     if (match_backtrack (c,
 			 backtrack.len, (USHORT *) backtrack.array,
 			 match_coverage, this) &&
         match_lookahead (c,
 			 lookahead.len, (USHORT *) lookahead.array,
 			 match_coverage, this,
 			 1))
     {
       c->replace_glyph_inplace (substitute[index]);
       /* Note: We DON'T decrease buffer->idx.  The main loop does it
        * for us.  This is useful for preventing surprises if someone
        * calls us through a Context lookup. */
-      return TRACE_RETURN (true);
+      return_trace (true);
     }
 
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
-      return TRACE_RETURN (false);
+      return_trace (false);
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!lookahead.sanitize (c, this))
-      return TRACE_RETURN (false);
+      return_trace (false);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
-    return TRACE_RETURN (substitute.sanitize (c));
+    return_trace (substitute.sanitize (c));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<Coverage>
@@ -1025,20 +1030,20 @@ struct ReverseChainSingleSubstFormat1
 };
 
 struct ReverseChainSingleSubst
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT				format;		/* Format identifier */
   ReverseChainSingleSubstFormat1	format1;
   } u;
@@ -1064,28 +1069,27 @@ struct SubstLookupSubTable
     Extension		= 7,
     ReverseChainSingle	= 8
   };
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
-    /* The sub_format passed to may_dispatch is unnecessary but harmless. */
-    if (unlikely (!c->may_dispatch (this, &u.sub_format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ());
     switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.dispatch (c));
-    case Multiple:		return TRACE_RETURN (u.multiple.dispatch (c));
-    case Alternate:		return TRACE_RETURN (u.alternate.dispatch (c));
-    case Ligature:		return TRACE_RETURN (u.ligature.dispatch (c));
-    case Context:		return TRACE_RETURN (u.context.dispatch (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.dispatch (c));
-    case Extension:		return TRACE_RETURN (u.extension.dispatch (c));
-    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.dispatch (c));
-    default:			return TRACE_RETURN (c->default_return_value ());
+    case Single:		return_trace (u.single.dispatch (c));
+    case Multiple:		return_trace (u.multiple.dispatch (c));
+    case Alternate:		return_trace (u.alternate.dispatch (c));
+    case Ligature:		return_trace (u.ligature.dispatch (c));
+    case Context:		return_trace (u.context.dispatch (c));
+    case ChainContext:		return_trace (u.chainContext.dispatch (c));
+    case Extension:		return_trace (u.extension.dispatch (c));
+    case ReverseChainSingle:	return_trace (u.reverseChainContextSingle.dispatch (c));
+    default:			return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT			sub_format;
   SingleSubst			single;
   MultipleSubst			multiple;
@@ -1115,132 +1119,143 @@ struct SubstLookup : Lookup
     if (unlikely (type == SubstLookupSubTable::Extension))
       return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
     return lookup_type_is_reverse (type);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    return TRACE_RETURN (dispatch (c));
+    return_trace (dispatch (c));
   }
 
   inline hb_closure_context_t::return_t closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     c->set_recurse_func (dispatch_recurse_func<hb_closure_context_t>);
-    return TRACE_RETURN (dispatch (c));
+    return_trace (dispatch (c));
   }
 
   inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
-    return TRACE_RETURN (dispatch (c));
+    return_trace (dispatch (c));
   }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
     hb_add_coverage_context_t<set_t> c (glyphs);
     dispatch (&c);
   }
 
   inline bool would_apply (hb_would_apply_context_t *c,
 			   const hb_ot_layout_lookup_accelerator_t *accel) const
   {
     TRACE_WOULD_APPLY (this);
-    if (unlikely (!c->len))  return TRACE_RETURN (false);
-    if (!accel->may_have (c->glyphs[0]))  return TRACE_RETURN (false);
-      return TRACE_RETURN (dispatch (c));
+    if (unlikely (!c->len))  return_trace (false);
+    if (!accel->may_have (c->glyphs[0]))  return_trace (false);
+      return_trace (dispatch (c));
   }
 
   static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
 
   inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c,
 						  unsigned int i)
   { return get_subtables<SubstLookupSubTable> ()[i].serialize (c, this); }
 
   inline bool serialize_single (hb_serialize_context_t *c,
 				uint32_t lookup_props,
 			        Supplier<GlyphID> &glyphs,
 			        Supplier<GlyphID> &substitutes,
 			        unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return TRACE_RETURN (false);
-    return TRACE_RETURN (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs));
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return_trace (false);
+    return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs));
   }
 
   inline bool serialize_multiple (hb_serialize_context_t *c,
 				  uint32_t lookup_props,
 				  Supplier<GlyphID> &glyphs,
 				  Supplier<unsigned int> &substitute_len_list,
 				  unsigned int num_glyphs,
 				  Supplier<GlyphID> &substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return TRACE_RETURN (false);
-    return TRACE_RETURN (serialize_subtable (c, 0).u.multiple.serialize (c, glyphs, substitute_len_list, num_glyphs,
-									 substitute_glyphs_list));
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return_trace (false);
+    return_trace (serialize_subtable (c, 0).u.multiple.serialize (c,
+								  glyphs,
+								  substitute_len_list,
+								  num_glyphs,
+								  substitute_glyphs_list));
   }
 
   inline bool serialize_alternate (hb_serialize_context_t *c,
 				   uint32_t lookup_props,
 				   Supplier<GlyphID> &glyphs,
 				   Supplier<unsigned int> &alternate_len_list,
 				   unsigned int num_glyphs,
 				   Supplier<GlyphID> &alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return TRACE_RETURN (false);
-    return TRACE_RETURN (serialize_subtable (c, 0).u.alternate.serialize (c, glyphs, alternate_len_list, num_glyphs,
-									  alternate_glyphs_list));
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return_trace (false);
+    return_trace (serialize_subtable (c, 0).u.alternate.serialize (c,
+								   glyphs,
+								   alternate_len_list,
+								   num_glyphs,
+								   alternate_glyphs_list));
   }
 
   inline bool serialize_ligature (hb_serialize_context_t *c,
 				  uint32_t lookup_props,
 				  Supplier<GlyphID> &first_glyphs,
 				  Supplier<unsigned int> &ligature_per_first_glyph_count_list,
 				  unsigned int num_first_glyphs,
 				  Supplier<GlyphID> &ligatures_list,
 				  Supplier<unsigned int> &component_count_list,
 				  Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return TRACE_RETURN (false);
-    return TRACE_RETURN (serialize_subtable (c, 0).u.ligature.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, num_first_glyphs,
-									 ligatures_list, component_count_list, component_list));
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return_trace (false);
+    return_trace (serialize_subtable (c, 0).u.ligature.serialize (c,
+								  first_glyphs,
+								  ligature_per_first_glyph_count_list,
+								  num_first_glyphs,
+								  ligatures_list,
+								  component_count_list,
+								  component_list));
   }
 
   template <typename context_t>
   static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   { return Lookup::dispatch<SubstLookupSubTable> (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
-    if (unlikely (!dispatch (c))) return TRACE_RETURN (false);
+    if (unlikely (!Lookup::sanitize (c))) return_trace (false);
+    if (unlikely (!dispatch (c))) return_trace (false);
 
     if (unlikely (get_type () == SubstLookupSubTable::Extension))
     {
       /* The spec says all subtables of an Extension lookup should
        * have the same type.  This is specially important if one has
        * a reverse type! */
       unsigned int type = get_subtable (0).u.extension.get_type ();
       unsigned int count = get_subtable_count ();
       for (unsigned int i = 1; i < count; i++)
         if (get_subtable (i).u.extension.get_type () != type)
-	  return TRACE_RETURN (false);
+	  return_trace (false);
     }
-    return TRACE_RETURN (true);
+    return_trace (true);
   }
 };
 
 typedef OffsetListOf<SubstLookup> SubstLookupList;
 
 /*
  * GSUB -- The Glyph Substitution Table
  */
@@ -1253,19 +1268,19 @@ struct GSUB : GSUBGPOS
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
   static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer);
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
+    if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
     const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
-    return TRACE_RETURN (list.sanitize (c, this));
+    return_trace (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 void
 GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -41,24 +41,21 @@ namespace OT {
 #define HB_DEBUG_CLOSURE (HB_DEBUG+0)
 #endif
 
 #define TRACE_CLOSURE(this) \
 	hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "");
 
-struct hb_closure_context_t
+struct hb_closure_context_t :
+       hb_dispatch_context_t<hb_closure_context_t, hb_void_t, HB_DEBUG_CLOSURE>
 {
   inline const char *get_name (void) { return "CLOSURE"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_CLOSURE;
-  typedef hb_void_t return_t;
   typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
-  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) { obj.closure (this); return HB_VOID; }
   static return_t default_return_value (void) { return HB_VOID; }
   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
   return_t recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
       return default_return_value ();
@@ -93,23 +90,20 @@ struct hb_closure_context_t
 #define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
 #endif
 
 #define TRACE_WOULD_APPLY(this) \
 	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "%d glyphs", c->len);
 
-struct hb_would_apply_context_t
+struct hb_would_apply_context_t :
+       hb_dispatch_context_t<hb_would_apply_context_t, bool, HB_DEBUG_WOULD_APPLY>
 {
   inline const char *get_name (void) { return "WOULD_APPLY"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_WOULD_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 obj.would_apply (this); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
   hb_face_t *face;
   const hb_codepoint_t *glyphs;
   unsigned int len;
@@ -133,24 +127,21 @@ struct hb_would_apply_context_t
 #define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0)
 #endif
 
 #define TRACE_COLLECT_GLYPHS(this) \
 	hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "");
 
-struct hb_collect_glyphs_context_t
+struct hb_collect_glyphs_context_t :
+       hb_dispatch_context_t<hb_collect_glyphs_context_t, hb_void_t, HB_DEBUG_COLLECT_GLYPHS>
 {
   inline const char *get_name (void) { return "COLLECT_GLYPHS"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_COLLECT_GLYPHS;
-  typedef hb_void_t return_t;
   typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index);
-  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) { obj.collect_glyphs (this); return HB_VOID; }
   static return_t default_return_value (void) { return HB_VOID; }
   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
   return_t recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
       return default_return_value ();
@@ -227,24 +218,24 @@ struct hb_collect_glyphs_context_t
 };
 
 
 
 #ifndef HB_DEBUG_GET_COVERAGE
 #define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
 #endif
 
+/* XXX Can we remove this? */
+
 template <typename set_t>
-struct hb_add_coverage_context_t
+struct hb_add_coverage_context_t :
+       hb_dispatch_context_t<hb_add_coverage_context_t<set_t>, const Coverage &, HB_DEBUG_GET_COVERAGE>
 {
   inline const char *get_name (void) { return "GET_COVERAGE"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_GET_COVERAGE;
   typedef const Coverage &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 obj.get_coverage (); }
   static return_t default_return_value (void) { return Null(Coverage); }
   bool stop_sublookup_iteration (return_t r) const
   {
     r.add_coverage (set);
     return false;
   }
@@ -264,17 +255,18 @@ struct hb_add_coverage_context_t
 #endif
 
 #define TRACE_APPLY(this) \
 	hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "idx %d gid %u lookup %d", \
 	 c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index);
 
-struct hb_apply_context_t
+struct hb_apply_context_t :
+       hb_dispatch_context_t<hb_apply_context_t, bool, HB_DEBUG_APPLY>
 {
   struct matcher_t
   {
     inline matcher_t (void) :
 	     lookup_props (0),
 	     ignore_zwnj (false),
 	     ignore_zwj (false),
 	     mask (-1),
@@ -444,21 +436,17 @@ struct hb_apply_context_t
     const USHORT *match_glyph_data;
 
     unsigned int num_items;
     unsigned int end;
   };
 
 
   inline const char *get_name (void) { return "APPLY"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
-  typedef bool return_t;
   typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
-  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 obj.apply (this); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
   return_t recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
       return default_return_value ();
@@ -717,17 +705,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)) return TRACE_RETURN (false);
+  if (unlikely (count > MAX_CONTEXT_LENGTH)) return_trace (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);
 
   /*
@@ -754,52 +742,52 @@ static inline bool match_input (hb_apply
   total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->cur());
 
   unsigned int first_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur());
   unsigned int first_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
 
   match_positions[0] = buffer->idx;
   for (unsigned int i = 1; i < count; i++)
   {
-    if (!skippy_iter.next ()) return TRACE_RETURN (false);
+    if (!skippy_iter.next ()) return_trace (false);
 
     match_positions[i] = skippy_iter.idx;
 
     unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]);
     unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]);
 
     if (first_lig_id && first_lig_comp) {
       /* If first component was attached to a previous ligature component,
        * all subsequent components should be attached to the same ligature
        * component, otherwise we shouldn't ligate them. */
       if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
-	return TRACE_RETURN (false);
+	return_trace (false);
     } else {
       /* If first component was NOT attached to a previous ligature component,
        * all subsequent components should also NOT be attached to any ligature
        * component, unless they are attached to the first component itself! */
       if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id))
-	return TRACE_RETURN (false);
+	return_trace (false);
     }
 
     is_mark_ligature = is_mark_ligature && _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]);
     total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->info[skippy_iter.idx]);
   }
 
   *end_offset = skippy_iter.idx - buffer->idx + 1;
 
   if (p_is_mark_ligature)
     *p_is_mark_ligature = is_mark_ligature;
 
   if (p_total_component_count)
     *p_total_component_count = total_component_count;
 
-  return TRACE_RETURN (true);
+  return_trace (true);
 }
-static inline void ligate_input (hb_apply_context_t *c,
+static inline bool ligate_input (hb_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph */
 				 unsigned int match_positions[MAX_CONTEXT_LENGTH], /* Including the first glyph */
 				 unsigned int match_length,
 				 hb_codepoint_t lig_glyph,
 				 bool is_mark_ligature,
 				 unsigned int total_component_count)
 {
   TRACE_APPLY (NULL);
@@ -879,36 +867,36 @@ static inline void ligate_input (hb_appl
       if (last_lig_id == _hb_glyph_info_get_lig_id (&buffer->info[i])) {
 	unsigned int new_lig_comp = components_so_far - last_num_components +
 				    MIN (MAX (_hb_glyph_info_get_lig_comp (&buffer->info[i]), 1u), last_num_components);
 	_hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp);
       } else
 	break;
     }
   }
-  TRACE_RETURN (true);
+  return_trace (true);
 }
 
 static inline bool match_backtrack (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT backtrack[],
 				    match_func_t match_func,
 				    const void *match_data)
 {
   TRACE_APPLY (NULL);
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->backtrack_len (), count);
   skippy_iter.set_match_func (match_func, match_data, backtrack);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.prev ())
-      return TRACE_RETURN (false);
+      return_trace (false);
 
-  return TRACE_RETURN (true);
+  return_trace (true);
 }
 
 static inline bool match_lookahead (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT lookahead[],
 				    match_func_t match_func,
 				    const void *match_data,
 				    unsigned int offset)
@@ -916,29 +904,29 @@ static inline bool match_lookahead (hb_a
   TRACE_APPLY (NULL);
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->idx + offset - 1, count);
   skippy_iter.set_match_func (match_func, match_data, lookahead);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.next ())
-      return TRACE_RETURN (false);
+      return_trace (false);
 
-  return TRACE_RETURN (true);
+  return_trace (true);
 }
 
 
 
 struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return_trace (c->check_struct (this));
   }
 
   USHORT	sequenceIndex;		/* Index into current glyph
 					 * sequence--first glyph = 0 */
   USHORT	lookupListIndex;	/* Lookup to apply to that
 					 * position--zero--based */
   public:
   DEFINE_SIZE_STATIC (4);
@@ -1029,17 +1017,17 @@ static inline bool apply_lookup (hb_appl
 
     /* And fixup the rest. */
     for (; next < count; next++)
       match_positions[next] += delta;
   }
 
   buffer->move_to (end);
 
-  return TRACE_RETURN (true);
+  return_trace (true);
 }
 
 
 
 /* Contextual lookups */
 
 struct ContextClosureLookupContext
 {
@@ -1138,24 +1126,24 @@ struct Rule
 				   lookupCount, lookupRecord,
 				   lookup_context);
   }
 
   inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return TRACE_RETURN (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
+    return_trace (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return TRACE_RETURN (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
+    return_trace (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
@@ -1197,37 +1185,37 @@ struct RuleSet
 
   inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
       if ((this+rule[i]).would_apply (c, lookup_context))
-        return TRACE_RETURN (true);
+        return_trace (true);
     }
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
       if ((this+rule[i]).apply (c, lookup_context))
-        return TRACE_RETURN (true);
+        return_trace (true);
     }
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (rule.sanitize (c, this));
+    return_trace (rule.sanitize (c, this));
   }
 
   protected:
   OffsetArrayOf<Rule>
 		rule;			/* Array of Rule tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, rule);
@@ -1274,43 +1262,43 @@ struct ContextFormat1
   {
     TRACE_WOULD_APPLY (this);
 
     const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph},
       NULL
     };
-    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+    return_trace (rule_set.would_apply (c, lookup_context));
   }
 
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED))
-      return TRACE_RETURN (false);
+      return_trace (false);
 
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph},
       NULL
     };
-    return TRACE_RETURN (rule_set.apply (c, lookup_context));
+    return_trace (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<RuleSet>
@@ -1366,44 +1354,44 @@ struct ContextFormat2
 
     const ClassDef &class_def = this+classDef;
     unsigned int index = class_def.get_class (c->glyphs[0]);
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_class},
       &class_def
     };
-    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+    return_trace (rule_set.would_apply (c, lookup_context));
   }
 
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const ClassDef &class_def = this+classDef;
     index = class_def.get_class (c->buffer->cur().codepoint);
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_class},
       &class_def
     };
-    return TRACE_RETURN (rule_set.apply (c, lookup_context));
+    return_trace (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
+    return_trace (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetTo<ClassDef>
@@ -1457,49 +1445,49 @@ struct ContextFormat3
   {
     TRACE_WOULD_APPLY (this);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
     };
-    return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
+    return_trace (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline const Coverage &get_coverage (void) const
   {
     return this+coverageZ[0];
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
     };
-    return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
+    return_trace (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!c->check_struct (this)) return TRACE_RETURN (false);
+    if (!c->check_struct (this)) return_trace (false);
     unsigned int count = glyphCount;
-    if (!count) return TRACE_RETURN (false); /* We want to access coverageZ[0] freely. */
-    if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return TRACE_RETURN (false);
+    if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */
+    if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!coverageZ[i].sanitize (c, this)) return TRACE_RETURN (false);
+      if (!coverageZ[i].sanitize (c, this)) return_trace (false);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count);
-    return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
+    return_trace (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 3 */
   USHORT	glyphCount;		/* Number of glyphs in the input glyph
 					 * sequence */
   USHORT	lookupCount;		/* Number of LookupRecords */
   OffsetTo<Coverage>
@@ -1512,22 +1500,22 @@ struct ContextFormat3
 };
 
 struct Context
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
+    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->dispatch (u.format1));
-    case 2: return TRACE_RETURN (c->dispatch (u.format2));
-    case 3: return TRACE_RETURN (c->dispatch (u.format3));
-    default:return TRACE_RETURN (c->default_return_value ());
+    case 1: return_trace (c->dispatch (u.format1));
+    case 2: return_trace (c->dispatch (u.format2));
+    case 3: return_trace (c->dispatch (u.format3));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   ContextFormat1	format1;
   ContextFormat2	format2;
@@ -1682,46 +1670,46 @@ struct ChainRule
   }
 
   inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return TRACE_RETURN (chain_context_would_apply_lookup (c,
-							   backtrack.len, backtrack.array,
-							   input.len, input.array,
-							   lookahead.len, lookahead.array, lookup.len,
-							   lookup.array, lookup_context));
+    return_trace (chain_context_would_apply_lookup (c,
+						    backtrack.len, backtrack.array,
+						    input.len, input.array,
+						    lookahead.len, lookahead.array, lookup.len,
+						    lookup.array, lookup_context));
   }
 
   inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return TRACE_RETURN (chain_context_apply_lookup (c,
-						     backtrack.len, backtrack.array,
-						     input.len, input.array,
-						     lookahead.len, lookahead.array, lookup.len,
-						     lookup.array, lookup_context));
+    return_trace (chain_context_apply_lookup (c,
+					      backtrack.len, backtrack.array,
+					      input.len, input.array,
+					      lookahead.len, lookahead.array, lookup.len,
+					      lookup.array, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
+    if (!backtrack.sanitize (c)) return_trace (false);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    if (!input.sanitize (c)) return TRACE_RETURN (false);
+    if (!input.sanitize (c)) return_trace (false);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
-    if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
+    if (!lookahead.sanitize (c)) return_trace (false);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return TRACE_RETURN (lookup.sanitize (c));
+    return_trace (lookup.sanitize (c));
   }
 
   protected:
   ArrayOf<USHORT>
 		backtrack;		/* Array of backtracking values
 					 * (to be matched before the input
 					 * sequence) */
   HeadlessArrayOf<USHORT>
@@ -1756,36 +1744,36 @@ struct ChainRuleSet
   }
 
   inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).would_apply (c, lookup_context))
-        return TRACE_RETURN (true);
+        return_trace (true);
 
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).apply (c, lookup_context))
-        return TRACE_RETURN (true);
+        return_trace (true);
 
-    return TRACE_RETURN (false);
+    return_trace (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (rule.sanitize (c, this));
+    return_trace (rule.sanitize (c, this));
   }
 
   protected:
   OffsetArrayOf<ChainRule>
 		rule;			/* Array of ChainRule tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, rule);
@@ -1830,42 +1818,42 @@ struct ChainContextFormat1
   {
     TRACE_WOULD_APPLY (this);
 
     const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph},
       {NULL, NULL, NULL}
     };
-    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+    return_trace (rule_set.would_apply (c, lookup_context));
   }
 
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+    if (likely (index == NOT_COVERED)) return_trace (false);
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph},
       {NULL, NULL, NULL}
     };
-    return TRACE_RETURN (rule_set.apply (c, lookup_context));