Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 18 Aug 2012 19:10:45 -0400
changeset 102733 8c85c83068e79540ea4dfa9774d0f24380a74462
parent 102712 c06577220b1bf30d4963c8d7bd011801c85ca728 (current diff)
parent 102732 f1c9b2f9e8bd6148206ead3f09d233f2225fdb22 (diff)
child 102747 35b8d6ef5d46373206497cc7e576d9db2dfa12cd
push id23305
push userryanvm@gmail.com
push dateSat, 18 Aug 2012 23:10:45 +0000
treeherdermozilla-central@8c85c83068e7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.0a1
first release with
nightly linux32
8c85c83068e7 / 17.0a1 / 20120819030601 / files
nightly linux64
8c85c83068e7 / 17.0a1 / 20120819030601 / files
nightly mac
8c85c83068e7 / 17.0a1 / 20120819030601 / files
nightly win32
8c85c83068e7 / 17.0a1 / 20120819030601 / files
nightly win64
8c85c83068e7 / 17.0a1 / 20120819030601 / 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 the last PGO-green inbound changeset to m-c.
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/test/mochitest/test_bug751809.html
gfx/harfbuzz/src/hb-fallback-shape-private.hh
gfx/harfbuzz/src/hb-graphite2-private.hh
gfx/harfbuzz/src/hb-ot-shape.h
gfx/harfbuzz/src/hb-uniscribe-private.hh
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -353,16 +353,19 @@ telemetryNoButtonLabel = No
 telemetryNoButtonAccessKey = N
 
 # Webapps notification popup
 webapps.install = Install
 webapps.install.accesskey = I
 #LOCALIZATION NOTE (webapps.requestInstall) %1$S is the web app name, %2$S is the site from which the web app is installed
 webapps.requestInstall = Do you want to install "%1$S" from this site (%2$S)?
 webapps.install.success = Application Installed
+# LOCALIZATION NOTE (webapps.uninstall.notification): %S will be replaced with the name of the uninstalled web app
+webapps.uninstall.notification = %S has been uninstalled from your computer.
+webapps.uninstall.label = Uninstall App
 
 # Telemetry opt-out prompt for Aurora and Nightly
 # LOCALIZATION NOTE (telemetryOptOutPrompt): %1$S and %3$S will be replaced by
 # brandFullName, and %2$S by the value of the toolkit.telemetry.server_owner preference.
 telemetryOptOutPrompt = %1$S sends information about performance, hardware, usage and customizations back to %2$S to help improve %3$S.
 
 # LOCALIZATION NOTE (fullscreen.entered): displayed when we enter HTML5 fullscreen mode, %S is the domain name of the focused website (e.g. mozilla.com).
 fullscreen.entered=%S is now fullscreen.
--- a/build/autoconf/config.status.m4
+++ b/build/autoconf/config.status.m4
@@ -69,28 +69,30 @@ test "x$exec_prefix" = xNONE && exec_pre
 trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
 
 : ${CONFIG_STATUS=./config.status}
 
 dnl We're going to need [ ] for python syntax.
 changequote(<<<, >>>)dnl
 echo creating $CONFIG_STATUS
 
+extra_python_path=${COMM_BUILD:+"'mozilla', "}
+
 cat > $CONFIG_STATUS <<EOF
 #!${PYTHON}
 # coding=$encoding
 
 import os, sys
 dnl topsrcdir is the top source directory in native form, as opposed to a
 dnl form suitable for make.
 topsrcdir = '''${WIN_TOP_SRC:-$srcdir}'''
 if not os.path.isabs(topsrcdir):
     topsrcdir = os.path.normpath(os.path.join(os.path.dirname(<<<__file__>>>), topsrcdir))
 dnl Don't rely on virtualenv here. Standalone js doesn't use it.
-sys.path.append(os.path.join(topsrcdir, ${COMM_BUILD:+'mozilla',} 'build'))
+sys.path.append(os.path.join(topsrcdir, ${extra_python_path}'build'))
 from ConfigStatus import config_status
 
 args = {
     'topsrcdir': topsrcdir,
     'topobjdir': os.path.dirname(<<<__file__>>>),
 
 dnl All defines and substs are stored with an additional space at the beginning
 dnl and at the end of the string, to avoid any problem with values starting or
--- a/configure.in
+++ b/configure.in
@@ -7676,17 +7676,17 @@ MOZ_ARG_DISABLE_BOOL(md,
      _cpp_md_flag=1
    fi
   dnl Default is to use -xM if using Sun Studio on Solaris
    if test "$SOLARIS_SUNPRO_CC"; then
      _cpp_md_flag=1
    fi])
 if test "$_cpp_md_flag"; then
   COMPILER_DEPEND=1
-  _DEPEND_CFLAGS='$(filter-out %/.pp,-MD -MF $(MDDEPDIR)/$(@F).pp)'
+  _DEPEND_CFLAGS='$(filter-out %/.pp,-MMD -MF $(MDDEPDIR)/$(@F).pp)'
   dnl Sun Studio on Solaris use -xM instead of -MD, see config/rules.mk
   if test "$SOLARIS_SUNPRO_CC"; then
     _DEPEND_CFLAGS=
   fi
 else
   COMPILER_DEPEND=
   dnl Don't override this for MSVC
   if test -z "$_WIN32_MSVC"; then
--- a/dom/base/DOMError.cpp
+++ b/dom/base/DOMError.cpp
@@ -1,21 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMError.h"
 
-#include "mozilla/Util.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMException.h"
 
-using mozilla::ArrayLength;
 using mozilla::dom::DOMError;
 
 namespace {
 
 struct NameMap
 {
   PRUint16 code;
   const char* name;
@@ -31,57 +29,16 @@ DOMError::CreateForNSResult(nsresult aRv
   const char* message;
   aRv = NS_GetNameAndMessageForDOMNSResult(aRv, &name, &message);
   if (NS_FAILED(aRv) || !name) {
     return nullptr;
   }
   return CreateWithName(NS_ConvertASCIItoUTF16(name));
 }
 
-// static
-already_AddRefed<nsIDOMDOMError>
-DOMError::CreateForDOMExceptionCode(PRUint16 aDOMExceptionCode)
-{
-  // All of these codes (and yes, some are skipped) come from the spec.
-  static const NameMap kNames[] = {
-    {  1, "IndexSizeError" },
-    {  3, "HierarchyRequestError" },
-    {  4, "WrongDocumentError" },
-    {  5, "InvalidCharacterError" },
-    {  7, "NoModificationAllowedError" },
-    {  8, "NotFoundError" },
-    {  9, "NotSupportedError" },
-    { 11, "InvalidStateError" },
-    { 12, "SyntaxError" },
-    { 13, "InvalidModificationError" },
-    { 14, "NamespaceError" },
-    { 15, "InvalidAccessError" },
-    { 17, "TypeMismatchError" },
-    { 18, "SecurityError" },
-    { 19, "NetworkError" },
-    { 20, "AbortError" },
-    { 21, "URLMismatchError" },
-    { 22, "QuotaExceededError" },
-    { 23, "TimeoutError" },
-    { 24, "InvalidNodeTypeError" },
-    { 25, "DataCloneError" }
-  };
-
-  for (size_t index = 0; index < ArrayLength(kNames); index++) {
-    if (kNames[index].code == aDOMExceptionCode) {
-      nsString name;
-      name.AssignASCII(kNames[index].name);
-      return CreateWithName(name);
-    }
-  }
-
-  NS_NOTREACHED("Unknown DOMException code!");
-  return nullptr;
-}
-
 NS_IMPL_ADDREF(DOMError)
 NS_IMPL_RELEASE(DOMError)
 
 NS_INTERFACE_MAP_BEGIN(DOMError)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMError)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDOMError)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
--- a/dom/base/DOMError.h
+++ b/dom/base/DOMError.h
@@ -22,19 +22,16 @@ class DOMError : public nsIDOMDOMError
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMDOMERROR
 
   static already_AddRefed<nsIDOMDOMError>
   CreateForNSResult(nsresult rv);
 
   static already_AddRefed<nsIDOMDOMError>
-  CreateForDOMExceptionCode(PRUint16 aDOMExceptionCode);
-
-  static already_AddRefed<nsIDOMDOMError>
   CreateWithName(const nsAString& aName)
   {
     nsCOMPtr<nsIDOMDOMError> error = new DOMError(aName);
     return error.forget();
   }
 
 protected:
   DOMError(const nsAString& aName)
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -3771,22 +3771,16 @@ void nsPluginInstanceOwner::SetFrame(nsO
       for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
         nsIScrollableFrame* sf = do_QueryFrame(f);
         if (sf) {
           sf->AddScrollPositionListener(this);
         }
       }
     }
 #endif
-    
-    nsFocusManager* fm = nsFocusManager::GetFocusManager();
-    const nsIContent* content = aFrame->GetContent();
-    if (fm && content) {
-      mContentFocused = (content == fm->GetFocusedContent());
-    }
   }
 }
 
 nsObjectFrame* nsPluginInstanceOwner::GetFrame()
 {
   return mObjectFrame;
 }
 
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -59,17 +59,16 @@ MOCHITEST_FILES = \
   crashing_subpage.html \
   test_GCrace.html \
   test_propertyAndMethod.html \
   test_bug539565-1.html \
   test_bug539565-2.html \
   test_bug771202.html \
   file_bug771202.html \
   test_bug777098.html \
-  test_bug751809.html \
   test_enumerate.html \
   test_npruntime_construct.html \
   307-xo-redirect.sjs \
   test_redirect_handling.html \
   test_zero_opacity.html \
   test_NPPVpluginWantsAllNetworkStreams.html \
   test_npruntime_npnsetexception.html \
   test_NPNVdocumentOrigin.html \
deleted file mode 100644
--- a/dom/plugins/test/mochitest/test_bug751809.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<html>
-<head>
-  <title>Bug 751809</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-
-<body>
-  <script class="testbody" type="application/javascript">
-  
-  SimpleTest.waitForExplicitFinish();
-  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-
-  const Ci = Components.interfaces;
-  const clickToPlayPref = "plugins.click_to_play";
-  const utils = window.QueryInterface(Ci.nsIInterfaceRequestor).
-                                    getInterface(Ci.nsIDOMWindowUtils);
-
-  function waitForCondition(condition, nextTest, errorMsg) {
-    var tries = 0;
-    var interval = setInterval(function() {
-      if (tries >= 30) {
-        ok(false, errorMsg);
-        moveOn();
-      }
-      if (condition()) {
-        moveOn();
-      }
-      tries++;
-    }, 100);
-    var moveOn = function() { clearInterval(interval); nextTest(); };
-  }
-  
-  function startFocusTest() {
-    var plugin = document.getElementById('plugin');
-    ok(plugin, "Got plugin element.");
-    
-    var condition = function() plugin.getBoundingClientRect().width == 400;
-    waitForCondition(condition, afterPluginInsertion, "Waited too long for plugin to show up in page");
-  }
-    
-  function afterPluginInsertion() {
-    var plugin = document.getElementById('plugin');
-    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    ok(!objLoadingContent.activated, "plugin should not be activated");
-
-    synthesizeMouseAtCenter(plugin, {}, window);
-    var condition = function() objLoadingContent.activated;
-    waitForCondition(condition, afterPluginActivation, "Waited too long for plugin to activate");
-  }
-    
-  function afterPluginActivation() { 
-    var plugin = document.getElementById('plugin');
-    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    ok(objLoadingContent.activated, "plugin should be activated now");
-    is(plugin.getMouseUpEventCount(), 0, "Plugin should not have received mouse events yet.");
-
-    synthesizeMouseAtCenter(plugin, {}, window);
-    var condition = function() plugin.getMouseUpEventCount() > 0;
-    waitForCondition(condition, afterFirstClick, "Waited too long for plugin to receive the mouse click");
-  }
-  
-  function afterFirstClick() {
-    var plugin = document.getElementById('plugin');
-    is(plugin.getMouseUpEventCount(), 1, "Plugin should have received 1 mouse up event.");
-
-    testsFinished();
-  }
-
-  function testsFinished() {
-    try {
-      SpecialPowers.clearUserPref(clickToPlayPref);
-    }   
-    catch(e) {
-      ok(false, "Couldn't reset click-to-play pref");
-    }
-    SimpleTest.finish();
-  }
-    
-  SpecialPowers.setBoolPref(clickToPlayPref, true);
-  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-  
-  document.write('<embed id="plugin" type="application/x-test" width="400" height="400" drawmode="solid" color="FF00FFFF"></embed>');
-  SimpleTest.executeSoon(startFocusTest);
-
-  </script>
-</body>
-</html>
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -158,17 +158,16 @@ static bool getEventModel(NPObject* npob
 static bool getReflector(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool isVisible(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getWindowPosition(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
-static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 
 static const NPUTF8* sPluginMethodIdentifierNames[] = {
   "npnEvaluateTest",
   "npnInvokeTest",
   "npnInvokeDefaultTest",
   "setUndefinedValueTest",
   "identifierToStringTest",
   "timerTest",
@@ -218,18 +217,17 @@ static const NPUTF8* sPluginMethodIdenti
   "getEventModel",
   "getReflector",
   "isVisible",
   "getWindowPosition",
   "constructObject",
   "setSitesWithData",
   "setSitesWithDataCapabilities",
   "getLastKeyText",
-  "getNPNVdocumentOrigin",
-  "getMouseUpEventCount"
+  "getNPNVdocumentOrigin"
 };
 static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
 static const ScriptableFunction sPluginMethodFunctions[] = {
   npnEvaluateTest,
   npnInvokeTest,
   npnInvokeDefaultTest,
   setUndefinedValueTest,
   identifierToStringTest,
@@ -280,18 +278,17 @@ static const ScriptableFunction sPluginM
   getEventModel,
   getReflector,
   isVisible,
   getWindowPosition,
   constructObject,
   setSitesWithData,
   setSitesWithDataCapabilities,
   getLastKeyText,
-  getNPNVdocumentOrigin,
-  getMouseUpEventCount
+  getNPNVdocumentOrigin
 };
 
 STATIC_ASSERT(ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
               ARRAY_LENGTH(sPluginMethodFunctions));
 
 static const NPUTF8* sPluginPropertyIdentifierNames[] = {
   "propertyAndMethod"
 };
@@ -775,17 +772,16 @@ NPP_New(NPMIMEType pluginType, NPP insta
   instanceData->focusState = ACTIVATION_STATE_UNKNOWN;
   instanceData->focusEventCount = 0;
   instanceData->eventModel = 0;
   instanceData->closeStream = false;
   instanceData->wantsAllStreams = false;
   instanceData->asyncDrawing = AD_NONE;
   instanceData->frontBuffer = NULL;
   instanceData->backBuffer = NULL;
-  instanceData->mouseUpEventCount = 0;
   instance->pdata = instanceData;
 
   TestNPObject* scriptableObject = (TestNPObject*)NPN_CreateObject(instance, &sNPClass);
   if (!scriptableObject) {
     printf("NPN_CreateObject failed to create an object, can't create a plugin instance\n");
     free(instanceData);
     return NPERR_GENERIC_ERROR;
   }
@@ -3648,20 +3644,8 @@ bool getNPNVdocumentOrigin(NPObject* npo
   NPError err = NPN_GetValue(npp, NPNVdocumentOrigin, &origin);
   if (err != NPERR_NO_ERROR) {
     return false;
   }
 
   STRINGZ_TO_NPVARIANT(origin, *result);
   return true;
 }
-
-bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
-{
-  if (argCount != 0) {
-    return false;
-  }
-  
-  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
-  InstanceData* id = static_cast<InstanceData*>(npp->pdata);
-  INT32_TO_NPVARIANT(id->mouseUpEventCount, *result);
-  return true;
-}
--- a/dom/plugins/test/testplugin/nptest.h
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -144,14 +144,13 @@ typedef struct InstanceData {
   int32_t focusEventCount;
   int32_t eventModel;
   bool closeStream;
   std::string lastKeyText;
   bool wantsAllStreams;
   AsyncDrawing asyncDrawing;
   NPAsyncSurface *frontBuffer;
   NPAsyncSurface *backBuffer;
-  int32_t mouseUpEventCount;
 } InstanceData;
 
 void notifyDidPaint(InstanceData* instanceData);
 
 #endif // nptest_h_
--- a/dom/plugins/test/testplugin/nptest_gtk2.cpp
+++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp
@@ -249,19 +249,16 @@ MotionEvent(GtkWidget* widget, GdkEventM
 
 static gboolean
 ButtonEvent(GtkWidget* widget, GdkEventButton* event,
             gpointer user_data)
 {
   InstanceData* instanceData = static_cast<InstanceData*>(user_data);
   instanceData->lastMouseX = event->x;
   instanceData->lastMouseY = event->y;
-  if (event->type == GDK_BUTTON_RELEASE) {
-    instanceData->mouseUpEventCount++;
-  }
   return TRUE;
 }
 
 static gboolean
 DeleteWidget(GtkWidget* widget, GdkEvent* event, gpointer user_data)
 {
   InstanceData* instanceData = static_cast<InstanceData*>(user_data);
   // Some plugins do not expect the plug to be removed from the socket before
@@ -416,19 +413,16 @@ pluginHandleEvent(InstanceData* instance
     instanceData->lastMouseY = motion->y;
     break;
   }
   case ButtonPress:
   case ButtonRelease: {
     XButtonEvent* button = &nsEvent->xbutton;
     instanceData->lastMouseX = button->x;
     instanceData->lastMouseY = button->y;
-    if (nsEvent->type == ButtonRelease) {
-      instanceData->mouseUpEventCount++;
-    }
     break;
   }
   default:
     break;
   }
 #endif
 
   return 0;
--- a/dom/plugins/test/testplugin/nptest_macosx.mm
+++ b/dom/plugins/test/testplugin/nptest_macosx.mm
@@ -271,19 +271,16 @@ pluginHandleEvent(InstanceData* instance
       case osEvt:
       {
         Rect globalBounds = {0};
         WindowRef nativeWindow = static_cast<WindowRef>(static_cast<NP_CGContext*>(w->window)->window);
         if (nativeWindow)
           ::GetWindowBounds(nativeWindow, kWindowStructureRgn, &globalBounds);
         instanceData->lastMouseX = carbonEvent->where.h - w->x - globalBounds.left;
         instanceData->lastMouseY = carbonEvent->where.v - w->y - globalBounds.top;
-        if (carbonEvent->what == mouseUp) {
-          instanceData->mouseUpEventCount++;
-        }
         break;
       }
       default:
         return kNPEventNotHandled;
     }
 
     return kNPEventHandled;
   }
@@ -297,19 +294,16 @@ pluginHandleEvent(InstanceData* instance
     case NPCocoaEventDrawRect:
       pluginDraw(instanceData, cocoaEvent);
       break;
     case NPCocoaEventMouseDown:
     case NPCocoaEventMouseUp:
     case NPCocoaEventMouseMoved:
       instanceData->lastMouseX = (int32_t)cocoaEvent->data.mouse.pluginX;
       instanceData->lastMouseY = (int32_t)cocoaEvent->data.mouse.pluginY;
-      if (cocoaEvent->type == NPCocoaEventMouseUp) {
-        instanceData->mouseUpEventCount++;
-      }
       break;
     case NPCocoaEventWindowFocusChanged:
       instanceData->topLevelWindowActivationState = cocoaEvent->data.focus.hasFocus ?
         ACTIVATION_STATE_ACTIVATED : ACTIVATION_STATE_DEACTIVATED;
       instanceData->topLevelWindowActivationEventCount = instanceData->topLevelWindowActivationEventCount + 1;
       break;
     case NPCocoaEventFocusChanged:
       instanceData->focusState = cocoaEvent->data.focus.hasFocus ?
--- a/dom/plugins/test/testplugin/nptest_qt.cpp
+++ b/dom/plugins/test/testplugin/nptest_qt.cpp
@@ -188,17 +188,16 @@ pluginHandleEvent(InstanceData* instance
     ////printf("ButtonPress\n");
     break;
   }
   case ButtonRelease: {
     //printf("ButtonRelease\n");
     XButtonEvent* button = &nsEvent->xbutton;
     instanceData->lastMouseX = button->x;
     instanceData->lastMouseY = button->y;
-    instanceData->mouseUpEventCount++;
     break;
   }
   default:
     break;
   }
 #endif
 
   return 0;
--- a/dom/plugins/test/testplugin/nptest_windows.cpp
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -597,19 +597,16 @@ handleEventInternal(InstanceData* instan
     case WM_MBUTTONDOWN:
     case WM_MBUTTONUP:
     case WM_RBUTTONDOWN:
     case WM_RBUTTONUP: {
       int x = instanceData->hasWidget ? 0 : instanceData->winX;
       int y = instanceData->hasWidget ? 0 : instanceData->winY;
       instanceData->lastMouseX = GET_X_LPARAM(pe->lParam) - x;
       instanceData->lastMouseY = GET_Y_LPARAM(pe->lParam) - y;
-      if ((UINT)pe->event == WM_LBUTTONUP) {
-        instanceData->mouseUpEventCount++;
-      }
       return true;
     }
 
     case WM_KEYDOWN:
       instanceData->lastKeyText.erase();
       *result = 0;
       return true;
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,9 +1,9 @@
-gfx/harfbuzz status as of 2012-08-06:
+gfx/harfbuzz status as of 2012-08-16:
 
 This directory contains the "harfbuzz-ng" source from the 'master' branch of
 git://anongit.freedesktop.org/git/harfbuzz.
 
 UPDATING:
 
 Note that hb-ot-shape-complex-indic-machine.hh and gfx/harfbuzz/src/hb-version.h
 are not present in the upstream Git repository. These are created at build time
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -82,16 +82,18 @@ HBSOURCES += \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.hh \
 	hb-ot-shape-complex-misc.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
+	hb-ot-shape-fallback-private.hh \
+	hb-ot-shape-fallback.cc \
 	hb-ot-shape-private.hh \
 	$(NULL)
 HBHEADERS += \
 	hb-ot.h \
 	hb-ot-layout.h \
 	hb-ot-tag.h \
 	$(NULL)
 endif
@@ -157,26 +159,46 @@ if HAVE_HB_OLD
 SUBDIRS += hb-old
 HBCFLAGS += -I$(srcdir)/hb-old
 HBLIBS   += hb-old/libhb-old.la
 HBSOURCES += hb-old.cc
 endif
 DIST_SUBDIRS += hb-old
 
 
+
+# Put the library together
+
+if OS_WIN32
+export_symbols = -export-symbols harfbuzz.def
+harfbuzz_def_dependency = harfbuzz.def
+endif
+
 # Use a C linker, not C++; Don't link to libstdc++
 libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
 nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES)
 libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
-libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined
 libharfbuzz_la_LIBADD = $(HBLIBS)
+libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
 pkginclude_HEADERS = $(HBHEADERS)
 nodist_pkginclude_HEADERS = hb-version.h
 
+CLEANFILES += harfbuzz.def
+harfbuzz.def: $(HBHEADERS)
+	$(AM_V_GEN) (echo EXPORTS; \
+	(cat $^ || echo 'hb_ERROR ()' ) | \
+	$(EGREP) '^hb_.* \(' | \
+	sed -e 's/ (.*//' | \
+	LANG=C sort; \
+	echo LIBRARY libharfbuzz-$(HB_VERSION_MAJOR).dll; \
+	) >"$@.tmp"
+	@ ! grep -q hb_ERROR "$@.tmp" && mv "$@.tmp" "$@" || ($(RM) "$@"; false)
+
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
 	$(NULL)
 
 EXTRA_DIST += $(GENERATORS)
 
@@ -214,16 +236,17 @@ indic_LDADD = libharfbuzz.la $(HBLIBS)
 
 test_would_substitute_SOURCES = test-would-substitute.cc
 test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
 test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
+	check-exported-symbols.sh \
 	check-includes.sh \
 	check-internal-symbols.sh \
 	check-static-inits.sh \
 	$(NULL)
 
 if HAVE_ICU
 else
 dist_check_SCRIPTS += check-libstdc++.sh
--- a/gfx/harfbuzz/src/Makefile.in
+++ b/gfx/harfbuzz/src/Makefile.in
@@ -44,16 +44,17 @@ CPPSRCS	=                        \
   hb-common.cc                   \
   hb-fallback-shape.cc           \
   hb-font.cc                     \
   hb-ot-layout.cc                \
   hb-ot-map.cc                   \
   hb-ot-shape-complex-arabic.cc  \
   hb-ot-shape-complex-indic.cc   \
   hb-ot-shape-complex-misc.cc    \
+  hb-ot-shape-fallback.cc        \
   hb-ot-shape-normalize.cc       \
   hb-ot-shape.cc                 \
   hb-ot-tag.cc                   \
   hb-set.cc                      \
   hb-shape.cc                    \
   hb-shape-plan.cc               \
   hb-shaper.cc                   \
   hb-unicode.cc                  \
@@ -66,17 +67,16 @@ EXPORTS_harfbuzz = \
   hb.h             \
   hb-blob.h        \
   hb-buffer.h      \
   hb-common.h      \
   hb-font.h        \
   hb-ot.h          \
   hb-ot-layout.h   \
   hb-ot-tag.h      \
-  hb-ot-shape.h    \
   hb-set.h         \
   hb-shape.h       \
   hb-shape-plan.h  \
   hb-unicode.h     \
   hb-version.h     \
   $(NULL)
 
 LOCAL_INCLUDES  += -I$(srcdir) 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/check-exported-symbols.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+if which nm 2>/dev/null >/dev/null; then
+	:
+else
+	echo "check-exported-symbols.sh: 'nm' not found; skipping test"
+	exit 77
+fi
+
+defs="harfbuzz.def"
+$MAKE $defs > /dev/null
+tested=false
+for def in $defs; do
+	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
+	so=.libs/lib${lib}.so
+	if test -f "$so"; then
+		echo "Checking that $so has the same symbol list as $def"
+		{
+			echo EXPORTS
+			nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>' | cut -d' ' -f3
+			stat=1
+			# cheat: copy the last line from the def file!
+			tail -n1 "$def"
+		} | diff "$def" - >&2 || stat=1
+		tested=true
+	fi
+done
+if ! $tested; then
+	echo "check-exported-symbols.sh: libharfbuzz shared library not found; skipping test"
+	exit 77
+fi
+
+exit $stat
--- a/gfx/harfbuzz/src/check-internal-symbols.sh
+++ b/gfx/harfbuzz/src/check-internal-symbols.sh
@@ -9,28 +9,22 @@ stat=0
 
 if which nm 2>/dev/null >/dev/null; then
 	:
 else
 	echo "check-internal-symbols.sh: 'nm' not found; skipping test"
 	exit 77
 fi
 
-if which c++filt 2>/dev/null >/dev/null; then
-	cplusplusfilt=c++filt
-else
-	cplusplusfilt=cat
-fi
-
 tested=false
-for suffix in so; do
-	so=.libs/libharfbuzz.$suffix
+for suffix in .so; do
+	so=`echo .libs/libharfbuzz$suffix`
 	if test -f "$so"; then
 		echo "Checking that we are not exposing internal symbols"
-		if nm $so | grep ' [TW] ' | $cplusplusfilt | grep -v ' T _fini\>\| T _init\>\| T hb_'; then
+		if nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then
 			echo "Ouch, internal symbols exposed"
 			stat=1
 		fi
 		tested=true
 	fi
 done
 if ! $tested; then
 	echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test"
--- a/gfx/harfbuzz/src/check-static-inits.sh
+++ b/gfx/harfbuzz/src/check-static-inits.sh
@@ -9,25 +9,31 @@ stat=0
 
 if which objdump 2>/dev/null >/dev/null; then
 	:
 else
 	echo "check-static-inits.sh: 'objdump' not found; skipping test"
 	exit 77
 fi
 
+OBJS=.libs/*.o
+if test "x`echo $OBJS`" = "x$OBJS" 2>/dev/null >/dev/null; then
+	echo "check-static-inits.sh: object files not found; skipping test"
+	exit 77
+fi
+
 echo "Checking that no object file has static initializers"
-for obj in .libs/*.o; do
+for obj in $OBJS; do
 	if objdump -t "$obj" | grep '[.]ctors'; then
 		echo "Ouch, $obj has static initializers"
 		stat=1
 	fi
 done
 
 echo "Checking that no object file has lazy static C++ constructors/destructors"
-for obj in .libs/*.o; do
+for obj in $OBJS; do
 	if objdump -t "$obj" | grep '__c'; then
 		echo "Ouch, $obj has lazy static C++ constructors/destructors"
 		stat=1
 	fi
 done
 
 exit $stat
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -59,24 +59,26 @@ hb_segment_properties_equal (const hb_se
 			     const hb_segment_properties_t *b)
 {
   return a->direction == b->direction &&
 	 a->script    == b->script    &&
 	 a->language  == b->language;
 }
 
 
-static inline long
+#if 0
+static inline unsigned int
 hb_segment_properties_hash (const hb_segment_properties_t *p)
 {
   /* TODO improve */
-  return (long) p->direction +
-	 (long) p->script +
-	 (long) p->language;
+  return (unsigned int) p->direction +
+	 (unsigned int) p->script +
+	 (intptr_t) (p->language);
 }
+#endif
 
 
 
 /*
  * hb_buffer_t
  */
 
 struct hb_buffer_t {
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -32,18 +32,16 @@
 #include <string.h>
 
 
 
 #ifndef HB_DEBUG_BUFFER
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
-#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default))
-
 /* Here is how the buffer works internally:
  *
  * There are two info pointers: info and out_info.  They always have
  * the same allocated size, but different lengths.
  *
  * As an optimization, both info and out_info may point to the
  * same piece of memory, which is owned by info.  This remains the
  * case as long as out_len doesn't exceed i at any time.
@@ -139,17 +137,17 @@ hb_buffer_t::get_scratch_buffer (unsigne
 
 void
 hb_buffer_t::reset (void)
 {
   if (unlikely (hb_object_is_inert (this)))
     return;
 
   hb_unicode_funcs_destroy (unicode);
-  unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
+  unicode = hb_unicode_funcs_get_default ();
 
   hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
   props = default_props;
 
   in_error = false;
   have_output = false;
   have_positions = false;
 
@@ -547,17 +545,17 @@ hb_buffer_create ()
 }
 
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
   static const hb_buffer_t _hb_buffer_nil = {
     HB_OBJECT_HEADER_STATIC,
 
-    _HB_BUFFER_UNICODE_FUNCS_DEFAULT,
+    const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     _HB_BUFFER_PROPS_DEFAULT,
 
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
   };
 
   return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
@@ -603,17 +601,18 @@ hb_buffer_get_user_data (hb_buffer_t    
 void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   if (!unicode)
-    unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
+    unicode = hb_unicode_funcs_get_default ();
+
 
   hb_unicode_funcs_reference (unicode);
   hb_unicode_funcs_destroy (buffer->unicode);
   buffer->unicode = unicode;
 }
 
 hb_unicode_funcs_t *
 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -196,16 +196,16 @@ hb_buffer_get_glyph_positions (hb_buffer
 /* Reorders a glyph buffer to have canonical in-cluster glyph order / position.
  * The resulting clusters should behave identical to pre-reordering clusters.
  * NOTE: This has nothing to do with Unicode normalization. */
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
 
 /*
  * NOT IMPLEMENTED
-void
-hb_buffer_normalize_characters (hb_buffer_t *buffer);
+ void
+ hb_buffer_normalize_characters (hb_buffer_t *buffer);
 */
 
 
 HB_END_DECLS
 
 #endif /* HB_BUFFER_H */
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -233,27 +233,22 @@ hb_language_to_string (hb_language_t lan
 {
   /* This is actually NULL-safe! */
   return language->s;
 }
 
 hb_language_t
 hb_language_get_default (void)
 {
-  static hb_language_t default_language;
+  static hb_language_t default_language = HB_LANGUAGE_INVALID;
 
-  if (!default_language) {
-    /* This block is not quite threadsafe, but is not as bad as
-     * it looks since it's idempotent.  As long as pointer ops
-     * are atomic, we are safe. */
-
-    /* I hear that setlocale() doesn't honor env vars on Windows,
-     * but for now we ignore that. */
-
-    default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
+  hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
+  if (unlikely (language == HB_LANGUAGE_INVALID)) {
+    language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
+    hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
   }
 
   return default_language;
 }
 
 
 /* hb_script_t */
 
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -38,18 +38,16 @@
 #  define HB_BEGIN_DECLS	extern "C" {
 #  define HB_END_DECLS		}
 # else /* !__cplusplus */
 #  define HB_BEGIN_DECLS
 #  define HB_END_DECLS
 # endif /* !__cplusplus */
 #endif
 
-HB_BEGIN_DECLS
-
 #if !defined (HB_DONT_DEFINE_STDINT)
 
 #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \
     defined (_sgi) || defined (__sun) || defined (sun) || \
     defined (__digital__) || defined (__HP_cc)
 #  include <inttypes.h>
 #elif defined (_AIX)
 #  include <sys/inttypes.h>
@@ -64,16 +62,18 @@ typedef unsigned __int32 uint32_t;
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
 #else
 #  include <stdint.h>
 #endif
 
 #endif
 
+HB_BEGIN_DECLS
+
 
 typedef int hb_bool_t;
 
 typedef uint32_t hb_codepoint_t;
 typedef int32_t hb_position_t;
 typedef uint32_t hb_mask_t;
 
 typedef union _hb_var_int_t {
@@ -91,17 +91,18 @@ typedef union _hb_var_int_t {
 typedef uint32_t hb_tag_t;
 
 #define HB_TAG(a,b,c,d) ((hb_tag_t)((((uint8_t)(a))<<24)|(((uint8_t)(b))<<16)|(((uint8_t)(c))<<8)|((uint8_t)(d))))
 #define HB_UNTAG(tag)   ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 
 /* len=-1 means str is NUL-terminated */
-hb_tag_t hb_tag_from_string (const char *str, int len);
+hb_tag_t
+hb_tag_from_string (const char *str, int len);
 
 
 /* hb_direction_t */
 
 typedef enum {
   HB_DIRECTION_INVALID = 0,
   HB_DIRECTION_LTR = 4,
   HB_DIRECTION_RTL,
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -136,25 +136,25 @@ void
 
 /*
  * shaper shape_plan data
  */
 
 struct hb_coretext_shaper_shape_plan_data_t {};
 
 hb_coretext_shaper_shape_plan_data_t *
-_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
-					     const hb_feature_t *user_features,
-					     unsigned int        num_user_features)
+_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
+					     const hb_feature_t *user_features HB_UNUSED,
+					     unsigned int        num_user_features HB_UNUSED)
 {
   return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data)
+_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-fallback-shape-private.hh
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright © 2011  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_FALLBACK_SHAPE_PRIVATE_HH
-#define HB_FALLBACK_SHAPE_PRIVATE_HH
-
-#include "hb-private.hh"
-
-#include "hb-shape.h"
-
-
-HB_BEGIN_DECLS
-
-
-HB_INTERNAL hb_bool_t
-_hb_fallback_shape (hb_font_t          *font,
-		    hb_buffer_t        *buffer,
-		    const hb_feature_t *features,
-		    unsigned int        num_features);
-
-
-HB_END_DECLS
-
-#endif /* HB_FALLBACK_SHAPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-fallback-shape.cc
+++ b/gfx/harfbuzz/src/hb-fallback-shape.cc
@@ -66,25 +66,25 @@ void
 
 /*
  * shaper shape_plan data
  */
 
 struct hb_fallback_shaper_shape_plan_data_t {};
 
 hb_fallback_shaper_shape_plan_data_t *
-_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
-					    const hb_feature_t *user_features,
-					    unsigned int        num_user_features)
+_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
+					    const hb_feature_t *user_features HB_UNUSED,
+					    unsigned int        num_user_features HB_UNUSED)
 {
   return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data)
+_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -89,29 +89,54 @@ struct hb_font_funcs_t {
  */
 
 struct hb_face_t {
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t immutable;
 
-  hb_reference_table_func_t  reference_table;
+  hb_reference_table_func_t  reference_table_func;
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
   unsigned int index;
-  unsigned int upem;
+  mutable unsigned int upem;
 
   struct hb_shaper_data_t shaper_data;
 
   struct plan_node_t {
     hb_shape_plan_t *shape_plan;
     plan_node_t *next;
   } *shape_plans;
+
+
+  inline hb_blob_t *reference_table (hb_tag_t tag) const
+  {
+    hb_blob_t *blob;
+
+    if (unlikely (!this || !reference_table_func))
+      return hb_blob_get_empty ();
+
+    blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data);
+    if (unlikely (!blob))
+      return hb_blob_get_empty ();
+
+    return blob;
+  }
+
+  inline unsigned int get_upem (void) const
+  {
+    if (unlikely (!upem))
+      load_upem ();
+    return upem;
+  }
+
+  private:
+  HB_INTERNAL void load_upem (void) const;
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
@@ -249,25 +274,28 @@ struct hb_font_t {
 					   glyph, point_index,
 					   x, y,
 					   klass->user_data.glyph_contour_point);
   }
 
   inline hb_bool_t get_glyph_name (hb_codepoint_t glyph,
 				   char *name, unsigned int size)
   {
+    if (size) *name = '\0';
     return klass->get.glyph_name (this, user_data,
 				  glyph,
 				  name, size,
 				  klass->user_data.glyph_name);
   }
 
   inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */
 					hb_codepoint_t *glyph)
   {
+    *glyph = 0;
+    if (len == -1) len = strlen (name);
     return klass->get.glyph_from_name (this, user_data,
 				       name, len,
 				       glyph,
 				       klass->user_data.glyph_from_name);
   }
 
 
   /* A bit higher-level, and with fallback */
@@ -372,16 +400,56 @@ struct hb_font_t {
     hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y);
 
     if (ret)
       subtract_glyph_origin_for_direction (glyph, direction, x, y);
 
     return ret;
   }
 
+  /* Generates gidDDD if glyph has no name. */
+  inline void
+  glyph_to_string (hb_codepoint_t glyph,
+		   char *s, unsigned int size)
+  {
+    if (get_glyph_name (glyph, s, size)) return;
+
+    snprintf (s, size, "gid%u", glyph);
+  }
+
+  /* Parses gidDDD and uniUUUU strings automatically. */
+  inline hb_bool_t
+  glyph_from_string (const char *s, int len, /* -1 means nul-terminated */
+		     hb_codepoint_t *glyph)
+  {
+    if (get_glyph_from_name (s, len, glyph)) return true;
+
+    if (len == -1) len = strlen (s);
+
+    /* Straight glyph index. */
+    if (hb_codepoint_parse (s, len, 10, glyph))
+      return true;
+
+    if (len > 3)
+    {
+      /* gidDDD syntax for glyph indices. */
+      if (0 == strncmp (s, "gid", 3) &&
+	  hb_codepoint_parse (s + 3, len - 3, 10, glyph))
+	return true;
+
+      /* uniUUUU and other Unicode character indices. */
+      hb_codepoint_t unichar;
+      if (0 == strncmp (s, "uni", 3) &&
+	  hb_codepoint_parse (s + 3, len - 3, 16, &unichar) &&
+	  get_glyph (unichar, 0, glyph))
+	return true;
+    }
+
+    return false;
+  }
 
   private:
   inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / hb_face_get_upem (this->face); }
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
 #include "hb-shaper-list.hh"
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -49,56 +49,56 @@ static hb_bool_t
 hb_font_get_glyph_nil (hb_font_t *font,
 		       void *font_data HB_UNUSED,
 		       hb_codepoint_t unicode,
 		       hb_codepoint_t variation_selector,
 		       hb_codepoint_t *glyph,
 		       void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph);
+    return font->parent->get_glyph (unicode, variation_selector, glyph);
 
   *glyph = 0;
   return false;
 }
 
 static hb_position_t
 hb_font_get_glyph_h_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph));
+    return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph));
 
   return font->x_scale;
 }
 
 static hb_position_t
 hb_font_get_glyph_v_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph));
+    return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph));
 
   return font->y_scale;
 }
 
 static hb_bool_t
 hb_font_get_glyph_h_origin_nil (hb_font_t *font,
 				void *font_data HB_UNUSED,
 				hb_codepoint_t glyph,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, glyph, x, y);
+    hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
   return false;
 }
@@ -107,17 +107,17 @@ static hb_bool_t
 hb_font_get_glyph_v_origin_nil (hb_font_t *font,
 				void *font_data HB_UNUSED,
 				hb_codepoint_t glyph,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, glyph, x, y);
+    hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
   return false;
 }
@@ -125,45 +125,43 @@ hb_font_get_glyph_v_origin_nil (hb_font_
 static hb_position_t
 hb_font_get_glyph_h_kerning_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t left_glyph,
 				 hb_codepoint_t right_glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph));
+    return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph));
 
   return 0;
 }
 
 static hb_position_t
 hb_font_get_glyph_v_kerning_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t top_glyph,
 				 hb_codepoint_t bottom_glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph));
+    return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph));
 
   return 0;
 }
 
 static hb_bool_t
 hb_font_get_glyph_extents_nil (hb_font_t *font,
 			       void *font_data HB_UNUSED,
 			       hb_codepoint_t glyph,
 			       hb_glyph_extents_t *extents,
 			       void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_extents (font->parent,
-					       glyph,
-					       extents);
+    hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents);
     if (ret) {
       font->parent_scale_position (&extents->x_bearing, &extents->y_bearing);
       font->parent_scale_distance (&extents->width, &extents->height);
     }
     return ret;
   }
 
   memset (extents, 0, sizeof (*extents));
@@ -175,17 +173,17 @@ hb_font_get_glyph_contour_point_nil (hb_
 				     void *font_data HB_UNUSED,
 				     hb_codepoint_t glyph,
 				     unsigned int point_index,
 				     hb_position_t *x,
 				     hb_position_t *y,
 				     void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, glyph, point_index, x, y);
+    hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
   return false;
 }
@@ -193,31 +191,31 @@ hb_font_get_glyph_contour_point_nil (hb_
 static hb_bool_t
 hb_font_get_glyph_name_nil (hb_font_t *font,
 			    void *font_data HB_UNUSED,
 			    hb_codepoint_t glyph,
 			    char *name, unsigned int size,
 			    void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return hb_font_get_glyph_name (font->parent, glyph, name, size);
+    return font->parent->get_glyph_name (glyph, name, size);
 
-  snprintf (name, size, "gid%u", glyph);
+  if (size) *name = '\0';
   return false;
 }
 
 static hb_bool_t
 hb_font_get_glyph_from_name_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 const char *name, int len, /* -1 means nul-terminated */
 				 hb_codepoint_t *glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
-    return hb_font_get_glyph_from_name (font->parent, name, len, glyph);
+    return font->parent->get_glyph_from_name (name, len, glyph);
 
   *glyph = 0;
   return false;
 }
 
 
 static const hb_font_funcs_t _hb_font_funcs_nil = {
   HB_OBJECT_HEADER_STATIC,
@@ -483,27 +481,45 @@ 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);
 }
 
+/* Generates gidDDD if glyph has no name. */
+void
+hb_font_glyph_to_string (hb_font_t *font,
+			 hb_codepoint_t glyph,
+			 char *s, unsigned int size)
+{
+  font->glyph_to_string (glyph, s, size);
+}
+
+/* Parses gidDDD and uniUUUU strings automatically. */
+hb_bool_t
+hb_font_glyph_from_string (hb_font_t *font,
+			   const char *s, int len, /* -1 means nul-terminated */
+			   hb_codepoint_t *glyph)
+{
+  return font->glyph_from_string (s, len, glyph);
+}
+
 
 /*
  * hb_face_t
  */
 
 static const hb_face_t _hb_face_nil = {
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
 
-  NULL, /* reference_table */
+  NULL, /* reference_table_func */
   NULL, /* user_data */
   NULL, /* destroy */
 
   0,    /* index */
   1000, /* upem */
 
   {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
@@ -511,29 +527,29 @@ static const hb_face_t _hb_face_nil = {
 #undef HB_SHAPER_IMPLEMENT
   },
 
   NULL, /* shape_plans */
 };
 
 
 hb_face_t *
-hb_face_create_for_tables (hb_reference_table_func_t  reference_table,
+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;
 
-  if (!reference_table || !(face = hb_object_create<hb_face_t> ())) {
+  if (!reference_table_func || !(face = hb_object_create<hb_face_t> ())) {
     if (destroy)
       destroy (user_data);
     return hb_face_get_empty ();
   }
 
-  face->reference_table = reference_table;
+  face->reference_table_func = reference_table_func;
   face->user_data = user_data;
   face->destroy = destroy;
 
   face->upem = 0;
 
   return face;
 }
 
@@ -619,19 +635,16 @@ hb_face_reference (hb_face_t *face)
   return hb_object_reference (face);
 }
 
 void
 hb_face_destroy (hb_face_t *face)
 {
   if (!hb_object_destroy (face)) return;
 
-  /* The cached shape_plans think they have a reference on us, and
-   * try to release it.  Make sure that doesn't mess up. */
-  face->header.ref_count.ref_count = HB_REFERENCE_COUNT_INVALID_VALUE;
   for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
   {
     hb_face_t::plan_node_t *next = node->next;
     hb_shape_plan_destroy (node->shape_plan);
     free (node);
     node = next;
   }
 
@@ -677,32 +690,23 @@ hb_face_is_immutable (hb_face_t *face)
   return face->immutable;
 }
 
 
 hb_blob_t *
 hb_face_reference_table (hb_face_t *face,
 			 hb_tag_t   tag)
 {
-  hb_blob_t *blob;
-
-  if (unlikely (!face || !face->reference_table))
-    return hb_blob_get_empty ();
-
-  blob = face->reference_table (face, tag, face->user_data);
-  if (unlikely (!blob))
-    return hb_blob_get_empty ();
-
-  return blob;
+  return face->reference_table (tag);
 }
 
 hb_blob_t *
 hb_face_reference_blob (hb_face_t *face)
 {
-  return hb_face_reference_table (face, HB_TAG_NONE);
+  return face->reference_table (HB_TAG_NONE);
 }
 
 void
 hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
   if (hb_object_is_inert (face))
     return;
@@ -724,23 +728,27 @@ hb_face_set_upem (hb_face_t    *face,
     return;
 
   face->upem = upem;
 }
 
 unsigned int
 hb_face_get_upem (hb_face_t *face)
 {
-  if (unlikely (!face->upem)) {
-    hb_blob_t *head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head));
-    const head *head_table = Sanitizer<head>::lock_instance (head_blob);
-    face->upem = head_table->get_upem ();
-    hb_blob_destroy (head_blob);
-  }
-  return face->upem;
+  return face->get_upem ();
+}
+
+
+void
+hb_face_t::load_upem (void) const
+{
+  hb_blob_t *head_blob = Sanitizer<head>::sanitize (reference_table (HB_OT_TAG_head));
+  const head *head_table = Sanitizer<head>::lock_instance (head_blob);
+  upem = head_table->get_upem ();
+  hb_blob_destroy (head_blob);
 }
 
 
 /*
  * hb_font_t
  */
 
 hb_font_t *
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -47,17 +47,17 @@ typedef struct hb_font_t hb_font_t;
 hb_face_t *
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index);
 
 typedef hb_blob_t * (*hb_reference_table_func_t)  (hb_face_t *face, hb_tag_t tag, void *user_data);
 
 /* calls destroy() when not needing user_data anymore */
 hb_face_t *
-hb_face_create_for_tables (hb_reference_table_func_t  reference_table,
+hb_face_create_for_tables (hb_reference_table_func_t  reference_table_func,
 			   void                      *user_data,
 			   hb_destroy_func_t          destroy);
 
 hb_face_t *
 hb_face_get_empty (void);
 
 hb_face_t *
 hb_face_reference (hb_face_t *face);
@@ -138,17 +138,18 @@ hb_font_funcs_get_user_data (hb_font_fun
 
 
 void
 hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs);
 
 hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
 
-/* funcs */
+
+/* glyph extents */
 
 typedef struct hb_glyph_extents_t
 {
   hb_position_t x_bearing;
   hb_position_t y_bearing;
   hb_position_t width;
   hb_position_t height;
 } hb_glyph_extents_t;
@@ -341,16 +342,27 @@ hb_font_get_glyph_extents_for_origin (hb
 				      hb_glyph_extents_t *extents);
 
 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);
 
+/* Generates gidDDD if glyph has no name. */
+void
+hb_font_glyph_to_string (hb_font_t *font,
+			 hb_codepoint_t glyph,
+			 char *s, unsigned int size);
+/* Parses gidDDD and uniUUUU strings automatically. */
+hb_bool_t
+hb_font_glyph_from_string (hb_font_t *font,
+			   const char *s, int len, /* -1 means nul-terminated */
+			   hb_codepoint_t *glyph);
+
 
 /*
  * hb_font_t
  */
 
 /* Fonts are very light-weight objects */
 
 hb_font_t *
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -196,17 +196,17 @@ hb_ft_get_glyph_extents (hb_font_t *font
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, 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;
+  extents->height = -ft_face->glyph->metrics.height;
   return true;
 }
 
 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,
@@ -459,16 +459,17 @@ hb_ft_font_set_funcs (hb_font_t *font)
   if (unlikely (err)) {
     hb_blob_destroy (blob);
     DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed");
     return;
   }
 
   FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
+  assert (font->y_scale >= 0);
   FT_Set_Char_Size (ft_face,
 		    font->x_scale, font->y_scale,
 		    0, 0);
 #if 0
 		    font->x_ppem * 72 * 64 / font->x_scale,
 		    font->y_ppem * 72 * 64 / font->y_scale);
 #endif
 
--- a/gfx/harfbuzz/src/hb-glib.cc
+++ b/gfx/harfbuzz/src/hb-glib.cc
@@ -245,19 +245,16 @@ hb_glib_unicode_compose (hb_unicode_func
 {
 #if GLIB_CHECK_VERSION(2,29,12)
   return g_unichar_compose (a, b, ab);
 #endif
 
   /* We don't ifdef-out the fallback code such that compiler always
    * sees it and makes sure it's compilable. */
 
-  if (!a || !b)
-    return false;
-
   gchar utf8[12];
   gchar *normalized;
   int len;
   hb_bool_t ret;
 
   len = g_unichar_to_utf8 (a, utf8);
   len += g_unichar_to_utf8 (b, utf8 + len);
   normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC);
@@ -362,27 +359,26 @@ hb_glib_unicode_decompose_compatibility 
   for (i = 0, c = utf8_decomposed; i < utf8_decomposed_len; i++, c = g_utf8_next_char (c))
     *decomposed++ = g_utf8_get_char (c);
 
   g_free (utf8_decomposed);
 
   return utf8_decomposed_len;
 }
 
-extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs;
-const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
-  HB_OBJECT_HEADER_STATIC,
-
-  NULL, /* parent */
-  true, /* immutable */
-  {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name,
-    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
-  }
-};
-
 hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void)
 {
+  static const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
+    HB_OBJECT_HEADER_STATIC,
+
+    NULL, /* parent */
+    true, /* immutable */
+    {
+#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name,
+      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_UNICODE_FUNC_IMPLEMENT
+    }
+  };
+
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
--- a/gfx/harfbuzz/src/hb-gobject-enums.cc
+++ b/gfx/harfbuzz/src/hb-gobject-enums.cc
@@ -35,17 +35,17 @@
 #undef __GNUC__
 #undef __GNUC_MINOR__
 #define __GNUC__ 2
 #define __GNUC_MINOR__ 6
 #endif
 
 #include "hb-gobject.h"
 
-/* enumerations from "../../src/hb-blob.h" */
+/* enumerations from "hb-blob.h" */
 inline static /* TODO(behdad) disable these for now until we fix them... */
 GType
 hb_memory_mode_t_hb_memory_mode_t_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
 
   if (g_once_init_enter (&g_define_type_id__volatile))
     {
@@ -59,17 +59,17 @@ hb_memory_mode_t_hb_memory_mode_t_get_ty
       GType g_define_type_id =
         g_enum_register_static (g_intern_static_string ("hb_memory_mode_t"), values);
       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
     }
 
   return g_define_type_id__volatile;
 }
 
-/* enumerations from "../../src/hb-common.h" */
+/* enumerations from "hb-common.h" */
 inline static /* TODO(behdad) disable these for now until we fix them... */
 GType
 hb_direction_t_hb_direction_t_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
 
   if (g_once_init_enter (&g_define_type_id__volatile))
     {
@@ -86,65 +86,16 @@ hb_direction_t_hb_direction_t_get_type (
       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
     }
 
   return g_define_type_id__volatile;
 }
 
 inline static /* TODO(behdad) disable these for now until we fix them... */
 GType
-hb_unicode_general_category_t_hb_unicode_general_category_t_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GEnumValue values[] = {
-        { HB_UNICODE_GENERAL_CATEGORY_CONTROL, "HB_UNICODE_GENERAL_CATEGORY_CONTROL", "control" },
-        { HB_UNICODE_GENERAL_CATEGORY_FORMAT, "HB_UNICODE_GENERAL_CATEGORY_FORMAT", "format" },
-        { HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, "HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED", "unassigned" },
-        { HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE, "HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE", "private-use" },
-        { HB_UNICODE_GENERAL_CATEGORY_SURROGATE, "HB_UNICODE_GENERAL_CATEGORY_SURROGATE", "surrogate" },
-        { HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, "HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER", "lowercase-letter" },
-        { HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER, "HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER", "modifier-letter" },
-        { HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER, "HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER", "other-letter" },
-        { HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER, "HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER", "titlecase-letter" },
-        { HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, "HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER", "uppercase-letter" },
-        { HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK, "HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK", "spacing-mark" },
-        { HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK, "HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK", "enclosing-mark" },
-        { HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK, "HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK", "non-spacing-mark" },
-        { HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER, "HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER", "decimal-number" },
-        { HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER, "HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER", "letter-number" },
-        { HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER, "HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER", "other-number" },
-        { HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION", "connect-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION", "dash-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION", "close-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION", "final-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION", "initial-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION", "other-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION", "open-punctuation" },
-        { HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL", "currency-symbol" },
-        { HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL", "modifier-symbol" },
-        { HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL", "math-symbol" },
-        { HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL", "other-symbol" },
-        { HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR, "HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR", "line-separator" },
-        { HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR, "HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR", "paragraph-separator" },
-        { HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR, "HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR", "space-separator" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("hb_unicode_general_category_t"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-
-inline static /* TODO(behdad) disable these for now until we fix them... */
-GType
 hb_script_t_hb_script_t_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
 
   if (g_once_init_enter (&g_define_type_id__volatile))
     {
       static const GEnumValue values[] = {
         { HB_SCRIPT_COMMON, "HB_SCRIPT_COMMON", "common" },
@@ -256,11 +207,137 @@ hb_script_t_hb_script_t_get_type (void)
       GType g_define_type_id =
         g_enum_register_static (g_intern_static_string ("hb_script_t"), values);
       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
     }
 
   return g_define_type_id__volatile;
 }
 
+/* enumerations from "hb-unicode.h" */
+inline static /* TODO(behdad) disable these for now until we fix them... */
+GType
+hb_unicode_general_category_t_hb_unicode_general_category_t_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { HB_UNICODE_GENERAL_CATEGORY_CONTROL, "HB_UNICODE_GENERAL_CATEGORY_CONTROL", "control" },
+        { HB_UNICODE_GENERAL_CATEGORY_FORMAT, "HB_UNICODE_GENERAL_CATEGORY_FORMAT", "format" },
+        { HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, "HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED", "unassigned" },
+        { HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE, "HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE", "private-use" },
+        { HB_UNICODE_GENERAL_CATEGORY_SURROGATE, "HB_UNICODE_GENERAL_CATEGORY_SURROGATE", "surrogate" },
+        { HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, "HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER", "lowercase-letter" },
+        { HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER, "HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER", "modifier-letter" },
+        { HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER, "HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER", "other-letter" },
+        { HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER, "HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER", "titlecase-letter" },
+        { HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, "HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER", "uppercase-letter" },
+        { HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK, "HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK", "spacing-mark" },
+        { HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK, "HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK", "enclosing-mark" },
+        { HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK, "HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK", "non-spacing-mark" },
+        { HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER, "HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER", "decimal-number" },
+        { HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER, "HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER", "letter-number" },
+        { HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER, "HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER", "other-number" },
+        { HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION", "connect-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION", "dash-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION", "close-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION", "final-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION", "initial-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION", "other-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION, "HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION", "open-punctuation" },
+        { HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL", "currency-symbol" },
+        { HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL", "modifier-symbol" },
+        { HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL", "math-symbol" },
+        { HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL, "HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL", "other-symbol" },
+        { HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR, "HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR", "line-separator" },
+        { HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR, "HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR", "paragraph-separator" },
+        { HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR, "HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR", "space-separator" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("hb_unicode_general_category_t"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+inline static /* TODO(behdad) disable these for now until we fix them... */
+GType
+hb_unicode_combining_class_t_hb_unicode_combining_class_t_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { HB_UNICODE_COMBINING_CLASS_NOT_REORDERED, "HB_UNICODE_COMBINING_CLASS_NOT_REORDERED", "not-reordered" },
+        { HB_UNICODE_COMBINING_CLASS_OVERLAY, "HB_UNICODE_COMBINING_CLASS_OVERLAY", "overlay" },
+        { HB_UNICODE_COMBINING_CLASS_NUKTA, "HB_UNICODE_COMBINING_CLASS_NUKTA", "nukta" },
+        { HB_UNICODE_COMBINING_CLASS_KANA_VOICING, "HB_UNICODE_COMBINING_CLASS_KANA_VOICING", "kana-voicing" },
+        { HB_UNICODE_COMBINING_CLASS_VIRAMA, "HB_UNICODE_COMBINING_CLASS_VIRAMA", "virama" },
+        { HB_UNICODE_COMBINING_CLASS_CCC10, "HB_UNICODE_COMBINING_CLASS_CCC10", "ccc10" },
+        { HB_UNICODE_COMBINING_CLASS_CCC11, "HB_UNICODE_COMBINING_CLASS_CCC11", "ccc11" },
+        { HB_UNICODE_COMBINING_CLASS_CCC12, "HB_UNICODE_COMBINING_CLASS_CCC12", "ccc12" },
+        { HB_UNICODE_COMBINING_CLASS_CCC13, "HB_UNICODE_COMBINING_CLASS_CCC13", "ccc13" },
+        { HB_UNICODE_COMBINING_CLASS_CCC14, "HB_UNICODE_COMBINING_CLASS_CCC14", "ccc14" },
+        { HB_UNICODE_COMBINING_CLASS_CCC15, "HB_UNICODE_COMBINING_CLASS_CCC15", "ccc15" },
+        { HB_UNICODE_COMBINING_CLASS_CCC16, "HB_UNICODE_COMBINING_CLASS_CCC16", "ccc16" },
+        { HB_UNICODE_COMBINING_CLASS_CCC17, "HB_UNICODE_COMBINING_CLASS_CCC17", "ccc17" },
+        { HB_UNICODE_COMBINING_CLASS_CCC18, "HB_UNICODE_COMBINING_CLASS_CCC18", "ccc18" },
+        { HB_UNICODE_COMBINING_CLASS_CCC19, "HB_UNICODE_COMBINING_CLASS_CCC19", "ccc19" },
+        { HB_UNICODE_COMBINING_CLASS_CCC20, "HB_UNICODE_COMBINING_CLASS_CCC20", "ccc20" },
+        { HB_UNICODE_COMBINING_CLASS_CCC21, "HB_UNICODE_COMBINING_CLASS_CCC21", "ccc21" },
+        { HB_UNICODE_COMBINING_CLASS_CCC22, "HB_UNICODE_COMBINING_CLASS_CCC22", "ccc22" },
+        { HB_UNICODE_COMBINING_CLASS_CCC23, "HB_UNICODE_COMBINING_CLASS_CCC23", "ccc23" },
+        { HB_UNICODE_COMBINING_CLASS_CCC24, "HB_UNICODE_COMBINING_CLASS_CCC24", "ccc24" },
+        { HB_UNICODE_COMBINING_CLASS_CCC25, "HB_UNICODE_COMBINING_CLASS_CCC25", "ccc25" },
+        { HB_UNICODE_COMBINING_CLASS_CCC26, "HB_UNICODE_COMBINING_CLASS_CCC26", "ccc26" },
+        { HB_UNICODE_COMBINING_CLASS_CCC27, "HB_UNICODE_COMBINING_CLASS_CCC27", "ccc27" },
+        { HB_UNICODE_COMBINING_CLASS_CCC28, "HB_UNICODE_COMBINING_CLASS_CCC28", "ccc28" },
+        { HB_UNICODE_COMBINING_CLASS_CCC29, "HB_UNICODE_COMBINING_CLASS_CCC29", "ccc29" },
+        { HB_UNICODE_COMBINING_CLASS_CCC30, "HB_UNICODE_COMBINING_CLASS_CCC30", "ccc30" },
+        { HB_UNICODE_COMBINING_CLASS_CCC31, "HB_UNICODE_COMBINING_CLASS_CCC31", "ccc31" },
+        { HB_UNICODE_COMBINING_CLASS_CCC32, "HB_UNICODE_COMBINING_CLASS_CCC32", "ccc32" },
+        { HB_UNICODE_COMBINING_CLASS_CCC33, "HB_UNICODE_COMBINING_CLASS_CCC33", "ccc33" },
+        { HB_UNICODE_COMBINING_CLASS_CCC34, "HB_UNICODE_COMBINING_CLASS_CCC34", "ccc34" },
+        { HB_UNICODE_COMBINING_CLASS_CCC35, "HB_UNICODE_COMBINING_CLASS_CCC35", "ccc35" },
+        { HB_UNICODE_COMBINING_CLASS_CCC36, "HB_UNICODE_COMBINING_CLASS_CCC36", "ccc36" },
+        { HB_UNICODE_COMBINING_CLASS_CCC84, "HB_UNICODE_COMBINING_CLASS_CCC84", "ccc84" },
+        { HB_UNICODE_COMBINING_CLASS_CCC91, "HB_UNICODE_COMBINING_CLASS_CCC91", "ccc91" },
+        { HB_UNICODE_COMBINING_CLASS_CCC103, "HB_UNICODE_COMBINING_CLASS_CCC103", "ccc103" },
+        { HB_UNICODE_COMBINING_CLASS_CCC107, "HB_UNICODE_COMBINING_CLASS_CCC107", "ccc107" },
+        { HB_UNICODE_COMBINING_CLASS_CCC118, "HB_UNICODE_COMBINING_CLASS_CCC118", "ccc118" },
+        { HB_UNICODE_COMBINING_CLASS_CCC122, "HB_UNICODE_COMBINING_CLASS_CCC122", "ccc122" },
+        { HB_UNICODE_COMBINING_CLASS_CCC129, "HB_UNICODE_COMBINING_CLASS_CCC129", "ccc129" },
+        { HB_UNICODE_COMBINING_CLASS_CCC130, "HB_UNICODE_COMBINING_CLASS_CCC130", "ccc130" },
+        { HB_UNICODE_COMBINING_CLASS_CCC133, "HB_UNICODE_COMBINING_CLASS_CCC133", "ccc133" },
+        { HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT, "HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT", "attached-below-left" },
+        { HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW, "HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW", "attached-below" },
+        { HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE, "HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE", "attached-above" },
+        { HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT, "HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT", "attached-above-right" },
+        { HB_UNICODE_COMBINING_CLASS_BELOW_LEFT, "HB_UNICODE_COMBINING_CLASS_BELOW_LEFT", "below-left" },
+        { HB_UNICODE_COMBINING_CLASS_BELOW, "HB_UNICODE_COMBINING_CLASS_BELOW", "below" },
+        { HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT, "HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT", "below-right" },
+        { HB_UNICODE_COMBINING_CLASS_LEFT, "HB_UNICODE_COMBINING_CLASS_LEFT", "left" },
+        { HB_UNICODE_COMBINING_CLASS_RIGHT, "HB_UNICODE_COMBINING_CLASS_RIGHT", "right" },
+        { HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT, "HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT", "above-left" },
+        { HB_UNICODE_COMBINING_CLASS_ABOVE, "HB_UNICODE_COMBINING_CLASS_ABOVE", "above" },
+        { HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT, "HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT", "above-right" },
+        { HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW, "HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW", "double-below" },
+        { HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE, "HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE", "double-above" },
+        { HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT, "HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT", "iota-subscript" },
+        { HB_UNICODE_COMBINING_CLASS_INVALID, "HB_UNICODE_COMBINING_CLASS_INVALID", "invalid" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("hb_unicode_combining_class_t"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
 
 /* Generated data ends here */
 
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-graphite2-private.hh
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright © 2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_GRAPHITE2_PRIVATE_HH
-#define HB_GRAPHITE2_PRIVATE_HH
-
-#include "hb-private.hh"
-
-#include "hb-graphite2.h"
-
-
-HB_INTERNAL hb_bool_t
-_hb_graphite2_shape (hb_font_t          *font,
-		     hb_buffer_t        *buffer,
-		     const hb_feature_t *features,
-		     unsigned int        num_features);
-
-
-#endif /* HB_GRAPHITE2_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -1,12 +1,12 @@
 /*
  * Copyright © 2011  Martin Hosken
  * Copyright © 2011  SIL International
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -21,237 +21,222 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
-
-#include "hb-graphite2.h"
-
-#include "hb-buffer-private.hh"
-#include "hb-font-private.hh"
-#include "hb-ot-tag.h"
+#define HB_SHAPER graphite2
+#define hb_graphite2_shaper_font_data_t gr_font
+#include "hb-shaper-impl-private.hh"
 
 #include <graphite2/Font.h>
 #include <graphite2/Segment.h>
 
+#include "hb-graphite2.h"
 
-struct hb_gr_cluster_t {
-  unsigned int base_char;
-  unsigned int num_chars;
-  unsigned int base_glyph;
-  unsigned int num_glyphs;
-};
+#include "hb-ot-tag.h"
+
+
+HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face)
+HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font)
 
 
-typedef struct hb_gr_tablelist_t {
-  hb_blob_t   *blob;
-  struct hb_gr_tablelist_t *next;
-  unsigned int tag;
-} hb_gr_tablelist_t;
+/*
+ * shaper face data
+ */
 
-static struct hb_gr_face_data_t {
-  hb_face_t         *face;
-  gr_face           *grface;
-  hb_gr_tablelist_t *tlist;
-} _hb_gr_face_data_nil = {NULL, NULL};
+typedef struct hb_graphite2_tablelist_t {
+  struct hb_graphite2_tablelist_t *next;
+  hb_blob_t *blob;
+  unsigned int tag;
+} hb_graphite2_tablelist_t;
 
-static struct hb_gr_font_data_t {
-  gr_font   *grfont;
+struct hb_graphite2_shaper_face_data_t {
+  hb_face_t *face;
   gr_face   *grface;
-} _hb_gr_font_data_nil = {NULL, NULL};
+  hb_graphite2_tablelist_t *tlist;
+};
 
-
-static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *len)
+static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len)
 {
-  hb_gr_tablelist_t *pl = NULL, *p;
-  hb_gr_face_data_t *face = (hb_gr_face_data_t *) data;
-  hb_gr_tablelist_t *tlist = face->tlist;
+  hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data;
+  hb_graphite2_tablelist_t *tlist = face_data->tlist;
+
+  hb_blob_t *blob = NULL;
 
-  for (p = tlist; p; p = p->next)
-    if (p->tag == tag ) {
-      unsigned int tlen;
-      const char *d = hb_blob_get_data (p->blob, &tlen);
-      *len = tlen;
-      return d;
-    } else
-      pl = p;
+  for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next)
+    if (p->tag == tag) {
+      blob = p->blob;
+      break;
+    }
 
-  if (!face->face)
-    return NULL;
-  hb_blob_t *blob = hb_face_reference_table (face->face, tag);
+  if (unlikely (!blob))
+  {
+    blob = face_data->face->reference_table (tag);
 
-  if (!pl || pl->blob)
-  {
-    p = (hb_gr_tablelist_t *) malloc (sizeof (hb_gr_tablelist_t));
-    if (!p) {
+    hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t));
+    if (unlikely (!p)) {
       hb_blob_destroy (blob);
       return NULL;
     }
-    p->next = NULL;
-    if (pl)
-      pl->next = p;
-    else
-      face->tlist = p;
-    pl = p;
+    p->blob = blob;
+    p->tag = tag;
+
+    /* TODO Not thread-safe, but fairly harmless.
+     * We can do the double-chcked pointer cmpexch thing here. */
+    p->next = face_data->tlist;
+    face_data->tlist = p;
   }
-  pl->blob = blob;
-  pl->tag = tag;
 
   unsigned int tlen;
   const char *d = hb_blob_get_data (blob, &tlen);
   *len = tlen;
   return d;
 }
 
-static float hb_gr_get_advance (const void *hb_font, unsigned short gid)
-{
-  return ((hb_font_t *) font)->get_glyph_h_advance (gid);
-}
-
-static void _hb_gr_face_data_destroy (void *data)
-{
-  hb_gr_face_data_t *f = (hb_gr_face_data_t *) data;
-  hb_gr_tablelist_t *tlist = f->tlist;
-  while (tlist)
-  {
-    hb_gr_tablelist_t *old = tlist;
-    hb_blob_destroy (tlist->blob);
-    tlist = tlist->next;
-    free (old);
-  }
-  gr_face_destroy (f->grface);
-}
-
-static void _hb_gr_font_data_destroy (void *data)
+hb_graphite2_shaper_face_data_t *
+_hb_graphite2_shaper_face_data_create (hb_face_t *face)
 {
-  hb_gr_font_data_t *f = (hb_gr_font_data_t *) data;
-
-  gr_font_destroy (f->grfont);
-  free (f);
-}
-
-static hb_user_data_key_t hb_gr_data_key;
-
-static hb_gr_face_data_t *
-_hb_gr_face_get_data (hb_face_t *face)
-{
-  hb_gr_face_data_t *data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
-  if (likely (data)) return data;
-
-  data = (hb_gr_face_data_t *) calloc (1, sizeof (hb_gr_face_data_t));
-  if (unlikely (!data))
-    return &_hb_gr_face_data_nil;
-
-
-  hb_blob_t *silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf);
+  hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF);
+  /* Umm, we just reference the table to check whether it exists.
+   * Maybe add better API for this? */
   if (!hb_blob_get_length (silf_blob))
   {
     hb_blob_destroy (silf_blob);
-    return &_hb_gr_face_data_nil;
+    return NULL;
   }
+  hb_blob_destroy (silf_blob);
+
+  hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t));
+  if (unlikely (!data))
+    hb_blob_destroy (silf_blob);
 
   data->face = face;
-  data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default);
-
+  data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_default);
 
-  if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data,
-					(hb_destroy_func_t) _hb_gr_face_data_destroy,
-					false)))
-  {
-    _hb_gr_face_data_destroy (data);
-    data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
-    if (data)
-      return data;
-    else
-      return &_hb_gr_face_data_nil;
+  if (unlikely (!data->grface)) {
+    free (data);
+    return NULL;
   }
 
   return data;
 }
 
-static hb_gr_font_data_t *
-_hb_gr_font_get_data (hb_font_t *font)
+void
+_hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data)
 {
-  hb_gr_font_data_t *data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
-  if (likely (data)) return data;
+  hb_graphite2_tablelist_t *tlist = data->tlist;
 
-  data = (hb_gr_font_data_t *) calloc (1, sizeof (hb_gr_font_data_t));
-  if (unlikely (!data))
-    return &_hb_gr_font_data_nil;
-
-
-  hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Silf);
-  if (!hb_blob_get_length (silf_blob))
+  while (tlist)
   {
-    hb_blob_destroy (silf_blob);
-    return &_hb_gr_font_data_nil;
+    hb_graphite2_tablelist_t *old = tlist;
+    hb_blob_destroy (tlist->blob);
+    tlist = tlist->next;
+    free (old);
   }
 
-  data->grface = _hb_gr_face_get_data (font->face)->grface;
-  data->grfont = gr_make_font_with_advance_fn (font->x_scale, font, &hb_gr_get_advance, data->grface);
-
+  gr_face_destroy (data->grface);
 
-  if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data,
-					(hb_destroy_func_t) _hb_gr_font_data_destroy,
-					false)))
-  {
-    _hb_gr_font_data_destroy (data);
-    data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
-    if (data)
-      return data;
-    else
-      return &_hb_gr_font_data_nil;
-  }
-
-  return data;
+  free (data);
 }
 
 
-hb_bool_t
-_hb_graphite_shape (hb_font_t          *font,
-		   hb_buffer_t        *buffer,
-		   const hb_feature_t *features,
-		   unsigned int        num_features)
+/*
+ * shaper font data
+ */
+
+static float hb_graphite2_get_advance (const void *hb_font, unsigned short gid)
+{
+  return ((hb_font_t *) hb_font)->get_glyph_h_advance (gid);
+}
+
+hb_graphite2_shaper_font_data_t *
+_hb_graphite2_shaper_font_data_create (hb_font_t *font)
 {
+  if (unlikely (!hb_graphite2_shaper_face_data_ensure (font->face))) return NULL;
+
+  hb_face_t *face = font->face;
+  hb_graphite2_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+
+  return gr_make_font_with_advance_fn (font->x_scale, font, &hb_graphite2_get_advance, face_data->grface);
+}
+
+void
+_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data)
+{
+  gr_font_destroy (data);
+}
+
+
+/*
+ * shaper shape_plan data
+ */
+
+struct hb_graphite2_shaper_shape_plan_data_t {};
 
-  buffer->guess_properties ();
+hb_graphite2_shaper_shape_plan_data_t *
+_hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
+					     const hb_feature_t *user_features HB_UNUSED,
+					     unsigned int        num_user_features HB_UNUSED)
+{
+  return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED)
+{
+}
+
+
+/*
+ * shaper
+ */
 
-  /* XXX We do a hell of a lot of stuff just to figure out this font
-   * is not graphite!  Shouldn't do. */
+struct hb_graphite2_cluster_t {
+  unsigned int base_char;
+  unsigned int num_chars;
+  unsigned int base_glyph;
+  unsigned int num_glyphs;
+};
 
-  hb_gr_font_data_t *data = _hb_gr_font_get_data (font);
-  if (!data->grface) return false;
+hb_bool_t
+_hb_graphite2_shape (hb_shape_plan_t    *shape_plan,
+		     hb_font_t          *font,
+		     hb_buffer_t        *buffer,
+		     const hb_feature_t *features,
+		     unsigned int        num_features)
+{
+  hb_face_t *face = font->face;
+  gr_face *grface = HB_SHAPER_DATA_GET (face)->grface;
+  gr_font *grfont = HB_SHAPER_DATA_GET (font);
 
   unsigned int charlen;
   hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen);
 
   int success = 0;
 
-  if (!charlen) return true;
-
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
   const char *lang_end = strchr (lang, '-');
   int lang_len = lang_end ? lang_end - lang : -1;
-  gr_feature_val *feats = gr_face_featureval_for_lang (data->grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
+  gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
 
   while (num_features--)
   {
-    const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag);
+    const gr_feature_ref *fref = gr_face_find_fref (grface, features->tag);
     if (fref)
       gr_fref_set_feature_value (fref, features->value, feats);
     features++;
   }
 
+  /* TODO Use scratch buffer for these. */
   hb_codepoint_t *gids = NULL, *pg;
-  hb_gr_cluster_t *clusters = NULL;
+  hb_graphite2_cluster_t *clusters = NULL;
   gr_segment *seg = NULL;
   uint32_t *text = NULL;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
   unsigned int glyphlen = 0;
   unsigned int *p;
 
@@ -261,25 +246,25 @@ hb_bool_t
   p = text;
   for (unsigned int i = 0; i < charlen; ++i)
     *p++ = bufferi++->codepoint;
   *p = 0;
 
   hb_tag_t script_tag[2];
   hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
 
-  seg = gr_make_seg (data->grfont, data->grface,
+  seg = gr_make_seg (grfont, grface,
 		     script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
 		     feats,
 		     gr_utf32, text, charlen,
 		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
   if (!seg) goto dieout;
 
   glyphlen = gr_seg_n_slots (seg);
-  clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t));
+  clusters = (hb_graphite2_cluster_t *) calloc (charlen, sizeof (hb_graphite2_cluster_t));
   if (!glyphlen || !clusters) goto dieout;
 
   gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t));
   if (!gids) goto dieout;
 
   pg = gids;
   for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
   {
@@ -291,17 +276,17 @@ hb_bool_t
     {
       clusters[ci-1].num_chars += clusters[ci].num_chars;
       clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
       ci--;
     }
 
     if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
     {
-      hb_gr_cluster_t *c = clusters + ci + 1;
+      hb_graphite2_cluster_t *c = clusters + ci + 1;
       c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
       c->num_chars = before - c->base_char;
       c->base_glyph = ic;
       c->num_glyphs = 0;
       ci++;
     }
     clusters[ci].num_glyphs++;
 
@@ -310,39 +295,43 @@ hb_bool_t
   }
   ci++;
 
   buffer->clear_output ();
   for (unsigned int i = 0; i < ci; ++i)
     buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids + clusters[i].base_glyph);
   buffer->swap_buffers ();
 
+  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+    curradvx = gr_seg_advance_X(seg);
+
   hb_glyph_position_t *pPos;
   for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
        is; pPos++, is = gr_slot_next_in_segment (is))
   {
-    pPos->x_offset = gr_slot_origin_X(is) - curradvx;
-    pPos->y_offset = gr_slot_origin_Y(is) - curradvy;
-    pPos->x_advance = gr_slot_advance_X(is, data->grface, data->grfont);
-    pPos->y_advance = gr_slot_advance_Y(is, data->grface, data->grfont);
-//    if (pPos->x_advance < 0 && gr_slot_attached_to(is))
-//      pPos->x_advance = 0;
-    curradvx += pPos->x_advance;
+    pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+    pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
+    pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+    pPos->y_advance = gr_slot_advance_Y (is, grface, grfont);
+    if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+      curradvx -= pPos->x_advance;
+    pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+    if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+      curradvx += pPos->x_advance;
+    pPos->y_offset = gr_slot_origin_Y (is) - curradvy;
     curradvy += pPos->y_advance;
   }
-  pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
+  if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+    pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
 
-  /* TODO(behdad):
-   * This shaper is badly broken with RTL text.  It returns glyphs
-   * in the logical order!
-   */
-//  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-//    hb_buffer_reverse (buffer);
+  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+    hb_buffer_reverse_clusters (buffer);
 
   success = 1;
 
 dieout:
+  if (feats) gr_featureval_destroy (feats);
   if (gids) free (gids);
   if (clusters) free (clusters);
   if (seg) gr_seg_destroy (seg);
   if (text) free (text);
   return success;
 }
--- a/gfx/harfbuzz/src/hb-graphite2.h
+++ b/gfx/harfbuzz/src/hb-graphite2.h
@@ -26,15 +26,15 @@
 #ifndef HB_GRAPHITE2_H
 #define HB_GRAPHITE2_H
 
 #include "hb.h"
 
 HB_BEGIN_DECLS
 
 
-#define HB_GRAPHITE_TAG_Silf HB_TAG('S','i','l','f')
+#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f')
 
 /* TODO add gr_font/face etc getters and other glue API */
 
 HB_END_DECLS
 
 #endif /* HB_GRAPHITE2_H */
--- a/gfx/harfbuzz/src/hb-icu.cc
+++ b/gfx/harfbuzz/src/hb-icu.cc
@@ -28,21 +28,20 @@
  */
 
 #include "hb-private.hh"
 
 #include "hb-icu.h"
 
 #include "hb-unicode-private.hh"
 
-#include <unicode/uversion.h>
 #include <unicode/uchar.h>
 #include <unicode/unorm.h>
 #include <unicode/ustring.h>
-
+#include <unicode/uversion.h>
 
 
 hb_script_t
 hb_icu_script_to_script (UScriptCode script)
 {
   if (unlikely (script == USCRIPT_INVALID_CODE))
     return HB_SCRIPT_INVALID;
 
@@ -159,28 +158,41 @@ hb_icu_unicode_script (hb_unicode_funcs_
   UScriptCode scriptCode = uscript_getScript(unicode, &status);
 
   if (unlikely (U_FAILURE (status)))
     return HB_SCRIPT_UNKNOWN;
 
   return hb_icu_script_to_script (scriptCode);
 }
 
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+static const UNormalizer2 *normalizer;
+#endif
+
 static hb_bool_t
 hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			hb_codepoint_t      a,
 			hb_codepoint_t      b,
 			hb_codepoint_t     *ab,
 			void               *user_data HB_UNUSED)
 {
-  if (!a || !b)
-    return false;
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+  {
+    UChar32 ret = unorm2_composePair (normalizer, a, b);
+    if (ret < 0) return false;
+    *ab = ret;
+    return true;
+  }
+#endif
+
+  /* We don't ifdef-out the fallback code such that compiler always
+   * sees it and makes sure it's compilable. */
 
   UChar utf16[4], normalized[5];
-  int len;
+  unsigned int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
   len = 0;
   err = false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err);
   if (err) return false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err);
@@ -202,18 +214,44 @@ hb_icu_unicode_compose (hb_unicode_funcs
 
 static hb_bool_t
 hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			  hb_codepoint_t      ab,
 			  hb_codepoint_t     *a,
 			  hb_codepoint_t     *b,
 			  void               *user_data HB_UNUSED)
 {
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+  {
+    UChar decomposed[4];
+    int len;
+    UErrorCode icu_err = U_ZERO_ERROR;
+    len = unorm2_getRawDecomposition (normalizer, ab, decomposed,
+				      ARRAY_LENGTH (decomposed), &icu_err);
+    if (U_FAILURE (icu_err) || len < 0) return false;
+
+    len = u_countChar32 (decomposed, len);
+    if (len == 1) {
+      U16_GET_UNSAFE (decomposed, 0, *a);
+      *b = 0;
+      return *a != ab;
+    } else if (len == 2) {
+      len =0;
+      U16_NEXT_UNSAFE (decomposed, len, *a);
+      U16_NEXT_UNSAFE (decomposed, len, *b);
+    }
+    return true;
+  }
+#endif
+
+  /* We don't ifdef-out the fallback code such that compiler always
+   * sees it and makes sure it's compilable. */
+
   UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
-  int len;
+  unsigned int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
   /* This function is a monster! Maybe it wasn't a good idea adding a
    * pairwise decompose API... */
   /* Watchout for the dragons.  Err, watchout for macros changing len. */
 
   len = 0;
@@ -273,17 +311,17 @@ hb_icu_unicode_decompose (hb_unicode_fun
 
 static unsigned int
 hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 					hb_codepoint_t      u,
 					hb_codepoint_t     *decomposed,
 					void               *user_data HB_UNUSED)
 {
   UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
-  int len;
+  unsigned int len;
   int32_t utf32_len;
   hb_bool_t err;
   UErrorCode icu_err;
 
   /* Copy @u into a UTF-16 array to be passed to ICU. */
   len = 0;
   err = FALSE;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), u, err);
@@ -301,28 +339,34 @@ hb_icu_unicode_decompose_compatibility (
   u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err);
   if (icu_err)
     return 0;
 
   return utf32_len;
 }
 
 
-extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs;
-const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
-  HB_OBJECT_HEADER_STATIC,
-
-  NULL, /* parent */
-  true, /* immutable */
-  {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
-    HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
-  }
-};
-
 hb_unicode_funcs_t *
 hb_icu_get_unicode_funcs (void)
 {
+  static const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
+    HB_OBJECT_HEADER_STATIC,
+
+    NULL, /* parent */
+    true, /* immutable */
+    {
+#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
+      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_UNICODE_FUNC_IMPLEMENT
+    }
+  };
+
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+  if (!hb_atomic_ptr_get (&normalizer)) {
+    UErrorCode icu_err = U_ZERO_ERROR;
+    /* We ignore failure in getNFCInstace(). */
+    hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err));
+  }
+#endif
   return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
 }
 
 
--- a/gfx/harfbuzz/src/hb-icu.h
+++ b/gfx/harfbuzz/src/hb-icu.h
@@ -28,17 +28,16 @@
 
 #ifndef HB_ICU_H
 #define HB_ICU_H
 
 #include "hb.h"
 
 #include <unicode/uscript.h>
 
-
 HB_BEGIN_DECLS
 
 
 hb_script_t
 hb_icu_script_to_script (UScriptCode script);
 
 UScriptCode
 hb_icu_script_from_script (hb_script_t script);
--- a/gfx/harfbuzz/src/hb-mutex-private.hh
+++ b/gfx/harfbuzz/src/hb-mutex-private.hh
@@ -39,16 +39,17 @@
 
 /* We need external help for these */
 
 #if 0
 
 
 #elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
 
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 typedef CRITICAL_SECTION hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	{ NULL, 0, 0, NULL, NULL, 0 }
 #define hb_mutex_impl_init(M)	InitializeCriticalSection (M)
 #define hb_mutex_impl_lock(M)	EnterCriticalSection (M)
 #define hb_mutex_impl_unlock(M)	LeaveCriticalSection (M)
 #define hb_mutex_impl_finish(M)	DeleteCriticalSection (M)
 
--- a/gfx/harfbuzz/src/hb-old.cc
+++ b/gfx/harfbuzz/src/hb-old.cc
@@ -147,17 +147,17 @@ hb_old_getGlyphMetrics (HB_Font old_font
 
   hb_glyph_extents_t extents;
 
   font->get_glyph_extents (glyph, &extents);
 
   metrics->x       = extents.x_bearing;
   metrics->y       = extents.y_bearing;
   metrics->width   = extents.width;
-  metrics->height  = -extents.height;
+  metrics->height  = extents.height;
   metrics->xOffset = font->get_glyph_h_advance (glyph);
   metrics->yOffset = 0;
 }
 
 static HB_Fixed
 hb_old_getFontMetric (HB_Font old_font,
 		      HB_FontMetric metric)
 {
@@ -183,17 +183,17 @@ static const HB_FontClass hb_old_font_cl
 };
 
 
 
 static HB_Error
 table_func (void *font, HB_Tag tag, HB_Byte *buffer, HB_UInt *length)
 {
   hb_face_t *face = (hb_face_t *) font;
-  hb_blob_t *blob = hb_face_reference_table (face, (hb_tag_t) tag);
+  hb_blob_t *blob = face->reference_table ((hb_tag_t) tag);
   unsigned int capacity = *length;
   *length = hb_blob_get_length (blob);
   memcpy (buffer, hb_blob_get_data (blob, NULL), MIN (capacity, *length));
   hb_blob_destroy (blob);
  return HB_Err_Ok;
 }
 
 
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -309,17 +309,16 @@ struct Lookup
     }
     return flag;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
     if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
-    if (!subTable.len) TRACE_RETURN (false);
     if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
       if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
     return TRACE_RETURN (true);
   }
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -468,17 +468,24 @@ struct Ligature
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
         return;
     c->glyphs->add (ligGlyph);
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    return c->len == 1 || (c->len == 2 && component.len == 2 && component[1] == c->second);
+    if (c->len != component.len)
+      return false;
+
+    for (unsigned int i = 1; i < c->len; i++)
+      if (likely (c->glyphs[i] != component[i]))
+	return false;
+
+    return true;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int count = component.len;
     if (unlikely (count < 1)) return TRACE_RETURN (false);
 
@@ -701,17 +708,17 @@ struct LigatureSubstFormat1
 
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    return (this+ligatureSet[(this+coverage) (c->first)]).would_apply (c);
+    return (this+ligatureSet[(this+coverage) (c->glyphs[0])]).would_apply (c);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
@@ -1059,18 +1066,26 @@ struct SubstLookupSubTable
     default:			return Null(Coverage);
     }
   }
 
   inline bool would_apply (hb_would_apply_context_t *c,
 			   unsigned int lookup_type) const
   {
     TRACE_WOULD_APPLY ();
-    if (get_coverage (lookup_type).get_coverage (c->first) == NOT_COVERED) return false;
-    if (c->len == 1) return true; /* Done! */
+    if (get_coverage (lookup_type).get_coverage (c->glyphs[0]) == NOT_COVERED) return false;
+    if (c->len == 1) {
+      switch (lookup_type) {
+      case Single:
+      case Multiple:
+      case Alternate:
+      case ReverseChainSingle:
+        return true;
+      }
+    }
 
     /* Only need to look further for lookups that support substitutions
      * of input longer than 1. */
     switch (lookup_type) {
     case Ligature:		return u.ligature.would_apply (c);
     case Context:		return u.context.would_apply (c);
     case ChainContext:		return u.chainContext.would_apply (c);
     case Extension:		return u.extension.would_apply (c);
@@ -1165,17 +1180,18 @@ struct SubstLookup : Lookup
         c->add_coverage (glyphs);
         last = c;
       }
     }
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    if (!c->digest.may_have (c->first)) return false;
+    if (unlikely (!c->len)) return false;
+    if (!c->digest.may_have (c->glyphs[0])) return false;
     unsigned int lookup_type = get_type ();
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       if (get_subtable (i).would_apply (c, lookup_type))
 	return true;
     return false;
   }
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -38,25 +38,16 @@
 #ifndef HB_DEBUG_CLOSURE
 #define HB_DEBUG_CLOSURE (HB_DEBUG+0)
 #endif
 
 #define TRACE_CLOSURE() \
 	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
 
-/* TODO Add TRACE_RETURN annotation to gsub. */
-#ifndef HB_DEBUG_WOULD_APPLY
-#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
-#endif
-
-#define TRACE_WOULD_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "first %u second %u", c->first, c->second);
-
-
 struct hb_closure_context_t
 {
   hb_face_t *face;
   hb_set_t *glyphs;
   unsigned int nesting_level_left;
   unsigned int debug_depth;
 
 
@@ -66,33 +57,41 @@ struct hb_closure_context_t
 			  face (face_),
 			  glyphs (glyphs_),
 			  nesting_level_left (nesting_level_left_),
 			  debug_depth (0) {}
 };
 
 
 
+/* TODO Add TRACE_RETURN annotation to gsub. */
+#ifndef HB_DEBUG_WOULD_APPLY
+#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
+#endif
+
+#define TRACE_WOULD_APPLY() \
+	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "%d glyphs", c->len);
+
 
 struct hb_would_apply_context_t
 {
   hb_face_t *face;
-  hb_codepoint_t first;
-  hb_codepoint_t second;
+  const hb_codepoint_t *glyphs;
   unsigned int len;
   const hb_set_digest_t digest;
   unsigned int debug_depth;
 
   hb_would_apply_context_t (hb_face_t *face_,
-			    hb_codepoint_t first_,
-			    hb_codepoint_t second_,
+			    const hb_codepoint_t *glyphs_,
+			    unsigned int len_,
 			    const hb_set_digest_t *digest_
 			    ) :
 			      face (face_),
-			      first (first_), second (second_), len (second == (hb_codepoint_t) -1 ? 1 : 2),
+			      glyphs (glyphs_),
+			      len (len_),
 			      digest (*digest_),
 			      debug_depth (0) {};
 };
 
 
 #ifndef HB_DEBUG_APPLY
 #define HB_DEBUG_APPLY (HB_DEBUG+0)
 #endif
@@ -402,17 +401,17 @@ static inline bool would_match_input (hb
 				      const USHORT input[], /* Array of input values--start with second glyph */
 				      match_func_t match_func,
 				      const void *match_data)
 {
   if (count != c->len)
     return false;
 
   for (unsigned int i = 1; i < count; i++)
-    if (likely (!match_func (c->second, input[i - 1], match_data)))
+    if (likely (!match_func (c->glyphs[i], input[i - 1], match_data)))
       return false;
 
   return true;
 }
 static inline bool match_input (hb_apply_context_t *c,
 				unsigned int count, /* Including the first glyph (not matched) */
 				const USHORT input[], /* Array of input values--start with second glyph */
 				match_func_t match_func,
@@ -752,17 +751,17 @@ struct ContextFormat1
 	rule_set.closure (c, lookup_context);
       }
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY ();
 
-    const RuleSet &rule_set = this+ruleSet[(this+coverage) (c->first)];
+    const RuleSet &rule_set = this+ruleSet[(this+coverage) (c->glyphs[0])];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph, NULL},
       NULL
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -825,17 +824,17 @@ struct ContextFormat2
       }
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY ();
 
     const ClassDef &class_def = this+classDef;
-    unsigned int index = class_def (c->first);
+    unsigned int index = class_def (c->glyphs[0]);
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_class, NULL},
       &class_def
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
@@ -924,17 +923,16 @@ struct ContextFormat3
     };
     return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
-    if (unlikely (!glyphCount)) return TRACE_RETURN (false);
     if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
       if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
     LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
     return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
   protected:
@@ -1248,17 +1246,17 @@ struct ChainContextFormat1
 	rule_set.closure (c, lookup_context);
       }
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY ();
 
-    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage) (c->first)];
+    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage) (c->glyphs[0])];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph, NULL},
       {NULL, NULL, NULL}
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -1324,17 +1322,17 @@ struct ChainContextFormat2
   }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY ();
 
     const ClassDef &input_class_def = this+inputClassDef;
 
-    unsigned int index = input_class_def (c->first);
+    unsigned int index = input_class_def (c->glyphs[0]);
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_class, NULL},
       {NULL, &input_class_def, NULL}
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
@@ -1463,17 +1461,16 @@ struct ChainContextFormat3
 						     lookup.len, lookup.array, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!input.sanitize (c, this)) return TRACE_RETURN (false);
-    if (unlikely (!input.len)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return TRACE_RETURN (lookup.sanitize (c));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 3 */
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -34,19 +34,19 @@
 #include "hb-ot-layout.h"
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-set-private.hh"
 
 
 /* buffer var allocations, used during the GSUB/GPOS processing */
-#define glyph_props()		var1.u16[1] /* GDEF glyph properties */
-#define syllable()		var2.u8[0] /* GSUB/GPOS shaping boundaries */
-#define lig_props()		var2.u8[1] /* GSUB/GPOS ligature tracking */
+#define glyph_props()		var1.u16[0] /* GDEF glyph properties */
+#define syllable()		var1.u8[2] /* GSUB/GPOS shaping boundaries */
+#define lig_props()		var1.u8[3] /* GSUB/GPOS ligature tracking */
 
 #define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot)
 
 /*
  * GDEF
  */
 
 typedef enum {
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -44,23 +44,23 @@ HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
   if (unlikely (!layout))
     return NULL;
 
-  layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
+  layout->gdef_blob = Sanitizer<GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
   layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
 
-  layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB));
+  layout->gsub_blob = Sanitizer<GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
   layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
 
-  layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
+  layout->gpos_blob = Sanitizer<GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
   layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
 
   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
 
   layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
   layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
 
@@ -410,19 +410,18 @@ hb_ot_layout_would_substitute_lookup (hb
 }
 
 hb_bool_t
 hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
 					   unsigned int          lookup_index)
 {
-  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
-  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
+  hb_would_apply_context_t c (face, glyphs, glyphs_length, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
   return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
 }
 
 void
 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
   GSUB::substitute_start (font, buffer);
 }
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -164,17 +164,16 @@ hb_ot_layout_feature_get_lookup_indexes 
 
 /*
  * GSUB
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face);
 
-/* Supports length 1 or 2 right now. */
 hb_bool_t
 hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
 				      unsigned int          lookup_index);
 
 void
 hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
@@ -909,19 +909,19 @@ static const uint16_t shaping_table[][4]
   {0x06D2, 0x06D2, 0xFBAF, 0xFBAE}, /* U+06D2 ARABIC LETTER YEH BARREE */
   {0x06D3, 0x06D3, 0xFBB1, 0xFBB0}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */
 };
 
 #define SHAPING_TABLE_FIRST	0x0621
 #define SHAPING_TABLE_LAST	0x06D3
 
 
-static const struct {
+static const struct ligature_set_t {
  uint16_t first;
- struct {
+ struct ligature_pairs_t {
    uint16_t second;
    uint16_t ligature;
  } ligatures[4];
 } ligature_table[] =
 {
   { 0xFEDF, {
     { 0xFE88, 0xFEF9 }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */
     { 0xFE82, 0xFEF5 }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -1,10 +1,10 @@
 /*
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -25,17 +25,17 @@
  */
 
 #include "hb-ot-shape-complex-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */
+#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 
 /*
  * Bits used in the joining tables
  */
 enum {
   JOINING_TYPE_U		= 0,
   JOINING_TYPE_R		= 1,
@@ -94,17 +94,17 @@ static uint16_t get_ligature (hb_codepoi
   for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++)
     if (ligature_table[i].first == first)
       for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++)
 	if (ligature_table[i].ligatures[j].second == second)
 	  return ligature_table[i].ligatures[j].ligature;
   return 0;
 }
 
-static const hb_tag_t arabic_syriac_features[] =
+static const hb_tag_t arabic_features[] =
 {
   HB_TAG('i','n','i','t'),
   HB_TAG('m','e','d','i'),
   HB_TAG('f','i','n','a'),
   HB_TAG('i','s','o','l'),
   /* Syriac */
   HB_TAG('m','e','d','2'),
   HB_TAG('f','i','n','2'),
@@ -122,19 +122,17 @@ enum {
 
   /* Syriac */
   MED2,
   FIN2,
   FIN3,
 
   NONE,
 
-  COMMON_NUM_FEATURES = 4,
-  SYRIAC_NUM_FEATURES = 7,
-  TOTAL_NUM_FEATURES = NONE
+  ARABIC_NUM_FEATURES = NONE
 };
 
 static const struct arabic_state_table_entry {
 	uint8_t prev_action;
 	uint8_t curr_action;
 	uint16_t next_state;
 } arabic_state_table[][NUM_STATE_MACHINE_COLS] =
 {
@@ -179,36 +177,80 @@ collect_features_arabic (hb_ot_shape_pla
    * TODO: Add test cases for these two.
    */
 
   map->add_bool_feature (HB_TAG('c','c','m','p'));
   map->add_bool_feature (HB_TAG('l','o','c','l'));
 
   map->add_gsub_pause (NULL);
 
-  unsigned int num_features = plan->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
-  for (unsigned int i = 0; i < num_features; i++)
-    map->add_bool_feature (arabic_syriac_features[i], false);
+  for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
+    map->add_bool_feature (arabic_features[i], false);
 
   map->add_gsub_pause (NULL);
 
   map->add_bool_feature (HB_TAG('r','l','i','g'));
   map->add_gsub_pause (NULL);
 
   map->add_bool_feature (HB_TAG('c','a','l','t'));
   map->add_gsub_pause (NULL);
 
   /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */
   map->add_bool_feature (HB_TAG('c','s','w','h'));
 }
 
+struct arabic_shape_plan_t
+{
+  ASSERT_POD ();
+
+  bool do_fallback;
+  /* The "+ 1" in the next array is to accommodate for the "NONE" command,
+   * which is not an OpenType feature, but this simplifies the code by not
+   * having to do a "if (... < NONE) ..." and just rely on the fact that
+   * mask_array[NONE] == 0. */
+  hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
+};
+
+static void *
+data_create_arabic (const hb_ot_shape_plan_t *plan)
+{
+  arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t));
+  if (unlikely (!arabic_plan))
+    return NULL;
+
+  hb_mask_t total_masks = 0;
+  for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
+    arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]);
+    total_masks |= arabic_plan->mask_array[i];
+  }
+
+  /* Pitfalls:
+   * - This path fires if user force-set init/medi/fina/isol off,
+   * - If font does not declare script 'arab', well, what to do?
+   *   Most probably it's safe to assume that init/medi/fina/isol
+   *   still mean Arabic shaping, although they do not have to.
+   */
+  arabic_plan->do_fallback = 0 == total_masks;
+
+  return arabic_plan;
+}
+
+static void
+data_destroy_arabic (void *data)
+{
+  free (data);
+}
 
 static void
 arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
 {
+  /* Only Arabic has presentation forms encoded in Unicode. */
+  if (buffer->props.script != HB_SCRIPT_ARABIC)
+    return;
+
   unsigned int count = buffer->len;
   hb_codepoint_t glyph;
 
   /* Shape to presentation forms */
   for (unsigned int i = 0; i < count; i++) {
     hb_codepoint_t u = buffer->info[i].codepoint;
     hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action());
     if (shaped != u && font->get_glyph (shaped, 0, &glyph))
@@ -231,19 +273,17 @@ arabic_fallback_shape (hb_font_t *font, 
      * But who cares, we're in fallback! */
   }
   for (; buffer->idx < count;)
       buffer->next_glyph ();
   buffer->swap_buffers ();
 }
 
 static void
-setup_masks_arabic (const hb_ot_shape_plan_t *plan,
-		    hb_buffer_t              *buffer,
-		    hb_font_t                *font)
+arabic_joining (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   unsigned int prev = 0, state = 0;
 
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
   for (unsigned int i = 0; i < count; i++)
   {
@@ -260,45 +300,53 @@ setup_masks_arabic (const hb_ot_shape_pl
       buffer->info[prev].arabic_shaping_action() = entry->prev_action;
 
     buffer->info[i].arabic_shaping_action() = entry->curr_action;
 
     prev = i;
     state = entry->next_state;
   }
 
-  hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0};
-  hb_mask_t total_masks = 0;
-  unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
-  for (unsigned int i = 0; i < num_masks; i++) {
-    mask_array[i] = plan->map.get_1_mask (arabic_syriac_features[i]);
-    total_masks |= mask_array[i];
-  }
+  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
+}
 
-  if (total_masks) {
-    /* Has OpenType tables */
-    for (unsigned int i = 0; i < count; i++)
-      buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()];
-  } else if (buffer->props.script == HB_SCRIPT_ARABIC) {
-    /* Fallback Arabic shaping to Presentation Forms */
-    /* Pitfalls:
-     * - This path fires if user force-set init/medi/fina/isol off,
-     * - If font does not declare script 'arab', well, what to do?
-     *   Most probably it's safe to assume that init/medi/fina/isol
-     *   still mean Arabic shaping, although they do not have to.
-     */
+static void
+preprocess_text_arabic (const hb_ot_shape_plan_t *plan,
+			hb_buffer_t              *buffer,
+			hb_font_t                *font)
+{
+  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+
+  if (unlikely (arabic_plan->do_fallback))
+  {
+    arabic_joining (buffer);
     arabic_fallback_shape (font, buffer);
   }
+}
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
+static void
+setup_masks_arabic (const hb_ot_shape_plan_t *plan,
+		    hb_buffer_t              *buffer,
+		    hb_font_t                *font)
+{
+  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+
+  if (likely (!arabic_plan->do_fallback))
+  {
+    arabic_joining (buffer);
+    unsigned int count = buffer->len;
+    for (unsigned int i = 0; i < count; i++)
+      buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
+  }
 }
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
 {
   "arabic",
   collect_features_arabic,
   NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
+  data_create_arabic,
+  data_destroy_arabic,
+  preprocess_text_arabic,
   NULL, /* normalization_preference */
   setup_masks_arabic,
   true, /* zero_width_attached_marks */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -30,18 +30,18 @@
 #include "hb-private.hh"
 
 
 #include "hb-ot-shape-complex-private.hh"
 #include "hb-ot-shape-private.hh" /* XXX Remove */
 
 
 /* buffer var allocations */
-#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
-#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
+#define indic_category() complex_var_u8_0() /* indic_category_t */
+#define indic_position() complex_var_u8_1() /* indic_matra_category_t */
 
 
 #define INDIC_TABLE_ELEMENT_TYPE uint8_t
 
 /* Cateories used in the OpenType spec:
  * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
  */
 /* Note: This enum is duplicated in the -machine.rl source file.
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -52,17 +52,17 @@ indic_options_init (void)
   u.opts.initialized = 1;
 
   char *c = getenv ("HB_OT_INDIC_OPTIONS");
   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
 
   return u;
 }
 
-inline indic_options_t
+static inline indic_options_t
 indic_options (void)
 {
   static indic_options_union_t options;
 
   if (unlikely (!options.i)) {
     /* This is idempotent and threadsafe. */
     options = indic_options_init ();
   }
@@ -143,18 +143,18 @@ indic_features[] =
    * Basic features.
    * These features are applied in order, one at a time, after initial_reordering.
    */
   {HB_TAG('n','u','k','t'), true},
   {HB_TAG('a','k','h','n'), true},
   {HB_TAG('r','p','h','f'), false},
   {HB_TAG('r','k','r','f'), true},
   {HB_TAG('p','r','e','f'), false},
+  {HB_TAG('h','a','l','f'), false},
   {HB_TAG('b','l','w','f'), false},
-  {HB_TAG('h','a','l','f'), false},
   {HB_TAG('a','b','v','f'), false},
   {HB_TAG('p','s','t','f'), false},
   {HB_TAG('c','f','a','r'), false},
   {HB_TAG('c','j','c','t'), true},
   {HB_TAG('v','a','t','u'), true},
   /*
    * Other features.
    * These features are applied all at once, after final_reordering.
@@ -175,18 +175,18 @@ indic_features[] =
  * Must be in the same order as the indic_features array.
  */
 enum {
   _NUKT,
   _AKHN,
   RPHF,
   _RKRF,
   PREF,
+  HALF,
   BLWF,
-  HALF,
   ABVF,
   PSTF,
   CFAR,
   _CJCT,
   _VATU,
 
   INIT,
   _PRES,
@@ -1129,12 +1129,13 @@ final_reordering (const hb_ot_shape_plan
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
 {
   "indic",
   collect_features_indic,
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
+  NULL, /* preprocess_text */
   NULL, /* normalization_preference */
   setup_masks_indic,
   false, /* zero_width_attached_marks */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-misc.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-misc.cc
@@ -1,10 +1,10 @@
 /*
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -85,28 +85,29 @@ normalization_preference_default (const 
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
 {
   "default",
   collect_features_default,
   NULL, /* override_features */
   NULL, /* data_create */
   NULL, /* data_destroy */
+  NULL, /* preprocess_text */
   normalization_preference_default,
   NULL, /* setup_masks */
   true, /* zero_width_attached_marks */
 };
 
 
 /* Thai / Lao shaper */
 
 static void
-setup_masks_thai (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		  hb_buffer_t              *buffer,
-		  hb_font_t                *font HB_UNUSED)
+preprocess_text_thai (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		      hb_buffer_t              *buffer,
+		      hb_font_t                *font HB_UNUSED)
 {
   /* The following is NOT specified in the MS OT Thai spec, however, it seems
    * to be what Uniscribe and other engines implement.  According to Eric Muller:
    *
    * When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the
    * NIKHAHIT backwards over any tone mark (0E48-0E4B).
    *
    * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
@@ -195,12 +196,13 @@ setup_masks_thai (const hb_ot_shape_plan
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
 {
   "thai",
   NULL, /* collect_features */
   NULL, /* override_features */
   NULL, /* data_create */
   NULL, /* data_destroy */
+  preprocess_text_thai,
   NULL, /* normalization_preference */
-  setup_masks_thai,
+  NULL, /* setup_masks */
   true, /* zero_width_attached_marks */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -30,19 +30,18 @@
 #include "hb-private.hh"
 
 #include "hb-ot-shape-private.hh"
 #include "hb-ot-shape-normalize-private.hh"
 
 
 
 /* buffer var allocations, used by complex shapers */
-#define complex_var_persistent_u8_0()	var2.u8[2]
-#define complex_var_persistent_u8_1()	var2.u8[3]
-#define complex_var_temporary_u8()	var2.u8[0]
+#define complex_var_u8_0()	var2.u8[2]
+#define complex_var_u8_1()	var2.u8[3]
 
 
 
 /* Master OT shaper list */
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
@@ -52,52 +51,61 @@
 
 struct hb_ot_complex_shaper_t
 {
   char name[8];
 
   /* collect_features()
    * Called during shape_plan().
    * Shapers should use plan->map to add their features and callbacks.
-   * May be NULL.
    */
   void (*collect_features) (hb_ot_shape_planner_t *plan);
 
   /* override_features()
    * Called during shape_plan().
    * Shapers should use plan->map to override features and add callbacks after
    * common features are added.
-   * May be NULL.
    */
   void (*override_features) (hb_ot_shape_planner_t *plan);
 
 
   /* data_create()
    * Called at the end of shape_plan().
    * Whatever shapers return will be accessible through plan->data later.
    * If NULL is returned, means a plan failure.
-   * May be NULL. */
+   */
   void *(*data_create) (const hb_ot_shape_plan_t *plan);
 
   /* data_destroy()
    * Called when the shape_plan is being destroyed.
    * plan->data is passed here for destruction.
    * If NULL is returned, means a plan failure.
    * May be NULL. */
   void (*data_destroy) (void *data);
 
+
+  /* preprocess_text()
+   * Called during shape().
+   * Shapers can use to modify text before shaping starts.
+   */
+  void (*preprocess_text) (const hb_ot_shape_plan_t *plan,
+			   hb_buffer_t              *buffer,
+			   hb_font_t                *font);
+
+
   /* normalization_preference()
    * Called during shape().
    */
   hb_ot_shape_normalization_mode_t
   (*normalization_preference) (const hb_ot_shape_plan_t *plan);
 
   /* setup_masks()
    * Called during shape().
    * Shapers should use map to get feature masks and set on buffer.
+   * Shapers may NOT modify characters.
    */
   void (*setup_masks) (const hb_ot_shape_plan_t *plan,
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font);
 
   bool zero_width_attached_marks;
 };
 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_FALLBACK_PRIVATE_HH
+#define HB_OT_SHAPE_FALLBACK_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-private.hh"
+
+
+HB_INTERNAL void _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
+						 hb_font_t *font,
+						 hb_buffer_t  *buffer);
+
+#endif /* HB_OT_SHAPE_FALLBACK_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -0,0 +1,326 @@
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-fallback-private.hh"
+
+static void
+zero_mark_advances (hb_buffer_t *buffer,
+		    unsigned int start,
+		    unsigned int end)
+{
+  for (unsigned int i = start; i < end; i++)
+    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    {
+      buffer->pos[i].x_advance = 0;
+      buffer->pos[i].y_advance = 0;
+    }
+}
+
+static unsigned int
+recategorize_combining_class (unsigned int modified_combining_class)
+{
+  if (modified_combining_class >= 200)
+    return modified_combining_class;
+
+  /* This should be kept in sync with modified combining class mapping
+   * from hb-unicode.cc. */
+  switch (modified_combining_class)
+  {
+
+    /* Hebrew */
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */
+    case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */
+    case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */
+    case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */
+    case HB_MODIFIED_COMBINING_CLASS_CCC14: /* hiriq */
+    case HB_MODIFIED_COMBINING_CLASS_CCC15: /* tsere */
+    case HB_MODIFIED_COMBINING_CLASS_CCC16: /* segol */
+    case HB_MODIFIED_COMBINING_CLASS_CCC17: /* patah */
+    case HB_MODIFIED_COMBINING_CLASS_CCC18: /* qamats */
+    case HB_MODIFIED_COMBINING_CLASS_CCC20: /* qubuts */
+    case HB_MODIFIED_COMBINING_CLASS_CCC22: /* meteg */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC23: /* rafe */
+      return HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC24: /* shin dot */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC25: /* sin dot */
+    case HB_MODIFIED_COMBINING_CLASS_CCC19: /* holam */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC26: /* point varika */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC21: /* dagesh */
+      break;
+
+
+    /* Arabic and Syriac */
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC27: /* fathatan */
+    case HB_MODIFIED_COMBINING_CLASS_CCC28: /* dammatan */
+    case HB_MODIFIED_COMBINING_CLASS_CCC30: /* fatha */
+    case HB_MODIFIED_COMBINING_CLASS_CCC31: /* damma */
+    case HB_MODIFIED_COMBINING_CLASS_CCC33: /* shadda */
+    case HB_MODIFIED_COMBINING_CLASS_CCC34: /* sukun */
+    case HB_MODIFIED_COMBINING_CLASS_CCC35: /* superscript alef */
+    case HB_MODIFIED_COMBINING_CLASS_CCC36: /* superscript alaph */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */
+    case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+
+    /* Thai */
+
+    /* Note: to be useful we also need to position U+0E3A that has ccc=9 (virama).
+     * But viramas can be both above and below based on the codepoint / script. */
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC103: /* sara u / sara uu */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC107: /* mai */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+
+    /* Lao */
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+
+    /* Tibetan */
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC129: /* sign aa */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC130: /* sign i*/
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC132: /* sign u */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+  }
+
+  return modified_combining_class;
+}
+
+static inline void
+position_mark (const hb_ot_shape_plan_t *plan,
+	       hb_font_t *font,
+	       hb_buffer_t  *buffer,
+	       hb_glyph_extents_t &base_extents,
+	       unsigned int i,
+	       unsigned int combining_class)
+{
+  hb_glyph_extents_t mark_extents;
+  if (!font->get_glyph_extents (buffer->info[i].codepoint,
+				&mark_extents))
+    return;
+
+  hb_position_t y_gap = font->y_scale / 16;
+
+  hb_glyph_position_t &pos = buffer->pos[i];
+  pos.x_offset = pos.y_offset = 0;
+
+
+  /* We dont position LEFT and RIGHT marks. */
+
+  /* X positioning */
+  switch (combining_class)
+  {
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
+      if (buffer->props.direction == HB_DIRECTION_LTR) {
+	pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing;
+        break;
+      } else if (buffer->props.direction == HB_DIRECTION_RTL) {
+	pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing;
+        break;
+      }
+      /* Fall through */
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE:
+      /* Center align. */
+      pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing;
+      break;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT:
+      /* Left align. */
+      pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing;
+      break;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT:
+      /* Right align. */
+      pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width - mark_extents.x_bearing;
+      break;
+  }
+
+  /* Y positioning */
+  switch (combining_class)
+  {
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT:
+      /* Add gap, fall-through. */
+      base_extents.height -= y_gap;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
+      pos.y_offset += base_extents.y_bearing + base_extents.height - mark_extents.y_bearing;
+      base_extents.height += mark_extents.height;
+      break;
+
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT:
+      /* Add gap, fall-through. */
+      base_extents.y_bearing += y_gap;
+      base_extents.height -= y_gap;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT:
+      pos.y_offset += base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height);
+      base_extents.y_bearing -= mark_extents.height;
+      base_extents.height += mark_extents.height;
+      break;
+  }
+}
+
+static inline void
+position_around_base (const hb_ot_shape_plan_t *plan,
+		      hb_font_t *font,
+		      hb_buffer_t  *buffer,
+		      unsigned int base,
+		      unsigned int end)
+{
+  hb_glyph_extents_t base_extents;
+  if (!font->get_glyph_extents (buffer->info[base].codepoint,
+				&base_extents))
+  {
+    /* If extents don't work, zero marks and go home. */
+    zero_mark_advances (buffer, base + 1, end);
+    return;
+  }
+  base_extents.x_bearing += buffer->pos[base].x_offset;
+  base_extents.y_bearing += buffer->pos[base].y_offset;
+
+  /* XXX Handle ligature component positioning... */
+  HB_UNUSED bool is_ligature = is_a_ligature (buffer->info[base]);
+
+  hb_position_t x_offset = 0, y_offset = 0;
+  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
+    x_offset -= buffer->pos[base].x_advance;
+    y_offset -= buffer->pos[base].y_advance;
+  }
+  unsigned int last_combining_class = 255;
+  hb_glyph_extents_t cluster_extents;
+  for (unsigned int i = base + 1; i < end; i++)
+    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    {
+      unsigned int this_combining_class = recategorize_combining_class (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]));
+      if (this_combining_class != last_combining_class)
+        cluster_extents = base_extents;
+
+      position_mark (plan, font, buffer, base_extents, i, this_combining_class);
+
+      buffer->pos[i].x_advance = 0;
+      buffer->pos[i].y_advance = 0;
+      buffer->pos[i].x_offset += x_offset;
+      buffer->pos[i].y_offset += y_offset;
+
+      /* combine cluster extents. */
+
+      last_combining_class = this_combining_class;
+    } else {
+      if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
+	x_offset -= buffer->pos[i].x_advance;
+	y_offset -= buffer->pos[i].y_advance;
+      } else {
+	x_offset += buffer->pos[i].x_advance;
+	y_offset += buffer->pos[i].y_advance;
+      }
+    }
+
+
+}
+
+static inline void
+position_cluster (const hb_ot_shape_plan_t *plan,
+		  hb_font_t *font,
+		  hb_buffer_t  *buffer,
+		  unsigned int start,
+		  unsigned int end)
+{
+  if (end - start < 2)
+    return;
+
+  /* Find the base glyph */
+  for (unsigned int i = start; i < end; i++)
+    if (is_a_ligature (buffer->info[i]) ||
+        !(FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) &
+	  (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
+	   FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
+	   FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
+    {
+      position_around_base (plan, font, buffer, i, end);
+      break;
+    }
+}
+
+void
+_hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
+				hb_font_t *font,
+				hb_buffer_t  *buffer)
+{
+  unsigned int start = 0;
+  unsigned int last_cluster = buffer->info[0].cluster;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 1; i < count; i++)
+    if (buffer->info[i].cluster != last_cluster) {
+      position_cluster (plan, font, buffer, start, i);
+      start = i;
+      last_cluster = buffer->info[i].cluster;
+    }
+  position_cluster (plan, font, buffer, start, count);
+}
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
@@ -27,16 +27,18 @@
 #ifndef HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
 #define HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-font.h"
 #include "hb-buffer.h"
 
+/* buffer var allocations, used during the normalization process */
+#define glyph_index()	var1.u32
 
 enum hb_ot_shape_normalization_mode_t {
   HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL, /* including base-to-base composition */
 
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -76,131 +76,374 @@
  *     We don't try compatibility decomposition recursively during the canonical
  *     decomposition phase.  This has minimal impact.  There are only a handful
  *     of Greek letter that have canonical decompositions that include characters
  *     with compatibility decomposition.  Those can be found using this command:
  *
  *     egrep  "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt
  */
 
-static void
-output_glyph (hb_buffer_t *buffer, hb_codepoint_t glyph)
+static hb_bool_t
+decompose_func (hb_unicode_funcs_t *unicode,
+		hb_codepoint_t  ab,
+		hb_codepoint_t *a,
+		hb_codepoint_t *b)
+{
+  /* XXX FIXME, move these to complex shapers and propagage to normalizer.*/
+  switch (ab) {
+    case 0x0AC9  : return false;
+
+    case 0x0931  : return false;
+    case 0x0B94  : return false;
+
+    /* These ones have Unicode decompositions, but we do it
+     * this way to be close to what Uniscribe does. */
+    case 0x0DDA  : *a = 0x0DD9; *b= 0x0DDA; return true;
+    case 0x0DDC  : *a = 0x0DD9; *b= 0x0DDC; return true;
+    case 0x0DDD  : *a = 0x0DD9; *b= 0x0DDD; return true;
+    case 0x0DDE  : *a = 0x0DD9; *b= 0x0DDE; return true;
+
+    case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
+    case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
+    case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
+    case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
+    case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
+    case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
+    case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
+    case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
+    case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
+    case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
+    case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
+    case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
+#if 0
+    case 0x0B57  : *a = 0xno decomp, -> RIGHT; return true;
+    case 0x1C29  : *a = 0xno decomp, -> LEFT; return true;
+    case 0xA9C0  : *a = 0xno decomp, -> RIGHT; return true;
+    case 0x111BF  : *a = 0xno decomp, -> ABOVE; return true;
+#endif
+  }
+  return unicode->decompose (ab, a, b);
+}
+
+static hb_bool_t
+compose_func (hb_unicode_funcs_t *unicode,
+	      hb_codepoint_t  a,
+	      hb_codepoint_t  b,
+	      hb_codepoint_t *ab)
 {
-  buffer->output_glyph (glyph);
+  /* XXX, this belongs to indic normalizer. */
+  if ((FLAG (unicode->general_category (a)) &
+       (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
+	FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
+	FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
+    return false;
+  /* XXX, add composition-exclusion exceptions to Indic shaper. */
+  if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
+
+  /* XXX, these belong to the hebew / default shaper. */
+  /* Hebrew presentation-form shaping.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
+  // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
+  // note that some letters do not have a dagesh presForm encoded
+  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
+    0xFB30, // ALEF
+    0xFB31, // BET
+    0xFB32, // GIMEL
+    0xFB33, // DALET
+    0xFB34, // HE
+    0xFB35, // VAV
+    0xFB36, // ZAYIN
+    0, // HET
+    0xFB38, // TET
+    0xFB39, // YOD
+    0xFB3A, // FINAL KAF
+    0xFB3B, // KAF
+    0xFB3C, // LAMED
+    0, // FINAL MEM
+    0xFB3E, // MEM
+    0, // FINAL NUN
+    0xFB40, // NUN
+    0xFB41, // SAMEKH
+    0, // AYIN
+    0xFB43, // FINAL PE
+    0xFB44, // PE
+    0, // FINAL TSADI
+    0xFB46, // TSADI
+    0xFB47, // QOF
+    0xFB48, // RESH
+    0xFB49, // SHIN
+    0xFB4A // TAV
+  };
+
+  hb_bool_t found = unicode->compose (a, b, ab);
+
+  if (!found && (b & ~0x7F) == 0x0580) {
+      // special-case Hebrew presentation forms that are excluded from
+      // standard normalization, but wanted for old fonts
+      switch (b) {
+      case 0x05B4: // HIRIQ
+	  if (a == 0x05D9) { // YOD
+	      *ab = 0xFB1D;
+	      found = true;
+	  }
+	  break;
+      case 0x05B7: // patah
+	  if (a == 0x05F2) { // YIDDISH YOD YOD
+	      *ab = 0xFB1F;
+	      found = true;
+	  } else if (a == 0x05D0) { // ALEF
+	      *ab = 0xFB2E;
+	      found = true;
+	  }
+	  break;
+      case 0x05B8: // QAMATS
+	  if (a == 0x05D0) { // ALEF
+	      *ab = 0xFB2F;
+	      found = true;
+	  }
+	  break;
+      case 0x05B9: // HOLAM
+	  if (a == 0x05D5) { // VAV
+	      *ab = 0xFB4B;
+	      found = true;
+	  }
+	  break;
+      case 0x05BC: // DAGESH
+	  if (a >= 0x05D0 && a <= 0x05EA) {
+	      *ab = sDageshForms[a - 0x05D0];
+	      found = (*ab != 0);
+	  } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
+	      *ab = 0xFB2C;
+	      found = true;
+	  } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      case 0x05BF: // RAFE
+	  switch (a) {
+	  case 0x05D1: // BET
+	      *ab = 0xFB4C;
+	      found = true;
+	      break;
+	  case 0x05DB: // KAF
+	      *ab = 0xFB4D;
+	      found = true;
+	      break;
+	  case 0x05E4: // PE
+	      *ab = 0xFB4E;
+	      found = true;
+	      break;
+	  }
+	  break;
+      case 0x05C1: // SHIN DOT
+	  if (a == 0x05E9) { // SHIN
+	      *ab = 0xFB2A;
+	      found = true;
+	  } else if (a == 0xFB49) { // SHIN WITH DAGESH
+	      *ab = 0xFB2C;
+	      found = true;
+	  }
+	  break;
+      case 0x05C2: // SIN DOT
+	  if (a == 0x05E9) { // SHIN
+	      *ab = 0xFB2B;
+	      found = true;
+	  } else if (a == 0xFB49) { // SHIN WITH DAGESH
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      }
+  }
+
+  return found;
+}
+
+
+static inline void
+set_glyph (hb_glyph_info_t &info, hb_font_t *font)
+{
+  font->get_glyph (info.codepoint, 0, &info.glyph_index());
+}
+
+static inline void
+output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph)
+{
+  buffer->cur().glyph_index() = glyph;
+  buffer->output_glyph (unichar);
   _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode);
 }
 
-static bool
-decompose (hb_font_t *font, hb_buffer_t *buffer,
-	   bool shortest,
-	   hb_codepoint_t ab)
+static inline void
+next_char (hb_buffer_t *buffer, hb_codepoint_t glyph)
+{
+  buffer->cur().glyph_index() = glyph;
+  buffer->next_glyph ();
+}
+
+static inline void
+skip_char (hb_buffer_t *buffer)
 {
-  hb_codepoint_t a, b, glyph;
+  buffer->skip_glyph ();
+}
 
-  if (!buffer->unicode->decompose (ab, &a, &b) ||
-      (b && !font->get_glyph (b, 0, &glyph)))
-    return false;
+/* Returns 0 if didn't decompose, number of resulting characters otherwise. */
+static inline unsigned int
+decompose (hb_font_t *font, hb_buffer_t *buffer, bool shortest, hb_codepoint_t ab)
+{
+  hb_codepoint_t a, b, a_glyph, b_glyph;
 
-  bool has_a = font->get_glyph (a, 0, &glyph);
+  if (!decompose_func (buffer->unicode, ab, &a, &b) ||
+      (b && !font->get_glyph (b, 0, &b_glyph)))
+    return 0;
+
+  bool has_a = font->get_glyph (a, 0, &a_glyph);
   if (shortest && has_a) {
     /* Output a and b */
-    output_glyph (buffer, a);
-    if (b)
-      output_glyph (buffer, b);
-    return true;
+    output_char (buffer, a, a_glyph);
+    if (likely (b)) {
+      output_char (buffer, b, b_glyph);
+      return 2;
+    }
+    return 1;
   }
 
-  if (decompose (font, buffer, shortest, a)) {
-    if (b)
-      output_glyph (buffer, b);
-    return true;
+  unsigned int ret;
+  if ((ret = decompose (font, buffer, shortest, a))) {
+    if (b) {
+      output_char (buffer, b, b_glyph);
+      return ret + 1;
+    }
+    return ret;
   }
 
   if (has_a) {
-    output_glyph (buffer, a);
-    if (b)
-      output_glyph (buffer, b);
-    return true;
+    output_char (buffer, a, a_glyph);
+    if (likely (b)) {
+      output_char (buffer, b, b_glyph);
+      return 2;
+    }
+    return 1;
   }
 
-  return false;
+  return 0;
 }
 
-static bool
-decompose_compatibility (hb_font_t *font, hb_buffer_t *buffer,
-			 hb_codepoint_t u)
+/* Returns 0 if didn't decompose, number of resulting characters otherwise. */
+static inline bool
+decompose_compatibility (hb_font_t *font, hb_buffer_t *buffer, hb_codepoint_t u)
 {
   unsigned int len, i;
   hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
+  hb_codepoint_t glyphs[HB_UNICODE_MAX_DECOMPOSITION_LEN];
 
   len = buffer->unicode->decompose_compatibility (u, decomposed);
   if (!len)
-    return false;
+    return 0;
 
-  hb_codepoint_t glyph;
   for (i = 0; i < len; i++)
-    if (!font->get_glyph (decomposed[i], 0, &glyph))
-      return false;
+    if (!font->get_glyph (decomposed[i], 0, &glyphs[i]))
+      return 0;
 
   for (i = 0; i < len; i++)
-    output_glyph (buffer, decomposed[i]);
+    output_char (buffer, decomposed[i], glyphs[i]);
 
-  return true;
+  return len;
 }
 
-static void
-decompose_current_character (hb_font_t *font, hb_buffer_t *buffer,
-			     bool shortest)
+/* Returns true if recomposition may be benefitial. */
+static inline bool
+decompose_current_character (hb_font_t *font, hb_buffer_t *buffer, bool shortest)
 {
   hb_codepoint_t glyph;
+  unsigned int len = 1;
 
   /* Kind of a cute waterfall here... */
   if (shortest && font->get_glyph (buffer->cur().codepoint, 0, &glyph))
-    buffer->next_glyph ();
-  else if (decompose (font, buffer, shortest, buffer->cur().codepoint))
-    buffer->skip_glyph ();
+    next_char (buffer, glyph);
+  else if ((len = decompose (font, buffer, shortest, buffer->cur().codepoint)))
+    skip_char (buffer);
   else if (!shortest && font->get_glyph (buffer->cur().codepoint, 0, &glyph))
-    buffer->next_glyph ();
-  else if (decompose_compatibility (font, buffer, buffer->cur().codepoint))
-    buffer->skip_glyph ();
+    next_char (buffer, glyph);
+  else if ((len = decompose_compatibility (font, buffer, buffer->cur().codepoint)))
+    skip_char (buffer);
   else
-    buffer->next_glyph ();
+    next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
+
+  /*
+   * A recomposition would only be useful if we decomposed into at least three
+   * characters...
+   */
+  return len > 2;
 }
 
-static void
-decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
-			      unsigned int end)
+static inline void
+handle_variation_selector_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end)
+{
+  for (; buffer->idx < end - 1;) {
+    if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
+      /* The next two lines are some ugly lines... But work. */
+      font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index());
+      buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
+    } else {
+      set_glyph (buffer->cur(), font);
+      buffer->next_glyph ();
+    }
+  }
+  if (likely (buffer->idx < end)) {
+    set_glyph (buffer->cur(), font);
+    buffer->next_glyph ();
+  }
+}
+
+/* Returns true if recomposition may be benefitial. */
+static inline bool
+decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end)
 {
   /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
   for (unsigned int i = buffer->idx; i < end; i++)
     if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
-      while (buffer->idx < end)
-	buffer->next_glyph ();
-      return;
+      handle_variation_selector_cluster (font, buffer, end);
+      return false;
     }
 
   while (buffer->idx < end)
     decompose_current_character (font, buffer, false);
+  /* We can be smarter here and only return true if there are at least two ccc!=0 marks.
+   * But does not matter. */
+  return true;
 }
 
+static inline bool
+decompose_cluster (hb_font_t *font, hb_buffer_t *buffer, bool recompose, unsigned int end)
+{
+  if (likely (buffer->idx + 1 == end))
+    return decompose_current_character (font, buffer, recompose);
+  else
+    return decompose_multi_char_cluster (font, buffer, end);
+}
+
+
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   unsigned int a = _hb_glyph_info_get_modified_combining_class (pa);
   unsigned int b = _hb_glyph_info_get_modified_combining_class (pb);
 
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
+
 void
 _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
 			hb_ot_shape_normalization_mode_t mode)
 {
   bool recompose = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
-  bool has_multichar_clusters = false;
+  bool can_use_recompose = false;
   unsigned int count;
 
   /* We do a fairly straightforward yet custom normalization process in three
    * separate rounds: decompose, reorder, recompose (if desired).  Currently
    * this makes two buffer swaps.  We can make it faster by moving the last
    * two rounds into the inner loop for the first round, but it's more readable
    * this way. */
 
@@ -211,27 +454,22 @@ void
   count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
       if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
-    if (buffer->idx + 1 == end)
-      decompose_current_character (font, buffer, recompose);
-    else {
-      decompose_multi_char_cluster (font, buffer, end);
-      has_multichar_clusters = true;
-    }
+    can_use_recompose = decompose_cluster (font, buffer, recompose, end) || can_use_recompose;
   }
   buffer->swap_buffers ();
 
 
-  if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !has_multichar_clusters)
+  if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !can_use_recompose)
     return; /* Done! */
 
 
   /* Second round, reorder (inplace) */
 
   count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
@@ -276,29 +514,31 @@ void
 	 * compose a CCC=0 character with it's preceding starter. */
 	(mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL ||
 	 _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) &&
 	/* If there's anything between the starter and this char, they should have CCC
 	 * smaller than this character's. */
 	(starter == buffer->out_len - 1 ||
 	 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
 	/* And compose. */
-	buffer->unicode->compose (buffer->out_info[starter].codepoint,
-				  buffer->cur().codepoint,
-				  &composed) &&
+	compose_func (buffer->unicode,
+		      buffer->out_info[starter].codepoint,
+		      buffer->cur().codepoint,
+		      &composed) &&
 	/* And the font has glyph for the composite. */
 	font->get_glyph (composed, 0, &glyph))
     {
       /* Composes. */
       buffer->next_glyph (); /* Copy to out-buffer. */
       if (unlikely (buffer->in_error))
         return;
       buffer->merge_out_clusters (starter, buffer->out_len);
-      buffer->out_len--; /* Remove the second composble. */
+      buffer->out_len--; /* Remove the second composable. */
       buffer->out_info[starter].codepoint = composed; /* Modify starter and carry on. */
+      set_glyph (buffer->out_info[starter], font);
       _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
 
       continue;
     }
 
     /* Blocked, or doesn't compose. */
     buffer->next_glyph ();
 
--- a/gfx/harfbuzz/src/hb-ot-shape-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh
@@ -29,18 +29,18 @@
 
 #include "hb-private.hh"
 
 #include "hb-ot-map-private.hh"
 
 
 
 /* buffer var allocations, used during the entire shaping process */
-#define unicode_props0()	var1.u8[0]
-#define unicode_props1()	var1.u8[1]
+#define unicode_props0()	var2.u8[0]
+#define unicode_props1()	var2.u8[1]
 
 
 
 struct hb_ot_shape_plan_t
 {
   hb_segment_properties_t props;
   const struct hb_ot_complex_shaper_t *shaper;
   hb_ot_map_t map;
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2010,2011  Google, Inc.
+ * Copyright © 2010,2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -27,46 +27,48 @@
  */
 
 #define HB_SHAPER ot
 #define hb_ot_shaper_face_data_t hb_ot_layout_t
 #define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-ot-shape-private.hh"
+#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-fallback-private.hh"
 #include "hb-ot-shape-normalize-private.hh"
-#include "hb-ot-shape-complex-private.hh"
 
 #include "hb-ot-layout-private.hh"
 #include "hb-set-private.hh"
 
 
-hb_tag_t common_features[] = {
+static hb_tag_t common_features[] = {
   HB_TAG('c','c','m','p'),
   HB_TAG('l','i','g','a'),
   HB_TAG('l','o','c','l'),
   HB_TAG('m','a','r','k'),
   HB_TAG('m','k','m','k'),
   HB_TAG('r','l','i','g'),
 };
 
 
-hb_tag_t horizontal_features[] = {
+static hb_tag_t horizontal_features[] = {
   HB_TAG('c','a','l','t'),
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
+  HB_TAG('r','c','l','t'),
 };
 
 /* Note:
  * Technically speaking, vrt2 and vert are mutually exclusive.
  * According to the spec, valt and vpal are also mutually exclusive.
  * But we apply them all for now.
  */
-hb_tag_t vertical_features[] = {
+static hb_tag_t vertical_features[] = {
   HB_TAG('v','a','l','t'),
   HB_TAG('v','e','r','t'),
   HB_TAG('v','k','r','n'),
   HB_TAG('v','p','a','l'),
   HB_TAG('v','r','t','2'),
 };
 
 
@@ -201,54 +203,32 @@ void
 
 
 /*
  * shaper
  */
 
 struct hb_ot_shape_context_t
 {
-  /* Input to hb_ot_shape_internal() */
   hb_ot_shape_plan_t *plan;
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t  *buffer;
   const hb_feature_t *user_features;
   unsigned int        num_user_features;
 
   /* Transient stuff */
   hb_direction_t target_direction;
-  hb_bool_t applied_position_complex;
 };
 
-static void
-hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
-{
-  hb_ot_map_t *map = &c->plan->map;
-
-  hb_mask_t global_mask = map->get_global_mask ();
-  c->buffer->reset_masks (global_mask);
-
-  if (c->plan->shaper->setup_masks)
-    c->plan->shaper->setup_masks (c->plan, c->buffer, c->font);
-
-  for (unsigned int i = 0; i < c->num_user_features; i++)
-  {
-    const hb_feature_t *feature = &c->user_features[i];
-    if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
-      unsigned int shift;
-      hb_mask_t mask = map->get_mask (feature->tag, &shift);
-      c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
-    }
-  }
-}
 
 
 /* Main shaper */
 
+
 /* Prepare */
 
 static void
 hb_set_unicode_props (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
@@ -281,135 +261,146 @@ hb_ensure_native_direction (hb_buffer_t 
     hb_buffer_reverse_clusters (buffer);
     buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
   }
 }
 
 
 /* Substitute */
 
-static void
-hb_mirror_chars (hb_ot_shape_context_t *c)
+static inline void
+hb_ot_mirror_chars (hb_ot_shape_context_t *c)
 {
-  hb_unicode_funcs_t *unicode = c->buffer->unicode;
-
   if (HB_DIRECTION_IS_FORWARD (c->target_direction))
     return;
 
+  hb_unicode_funcs_t *unicode = c->buffer->unicode;
   hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m'));
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
     hb_codepoint_t codepoint = unicode->mirroring (c->buffer->info[i].codepoint);
     if (likely (codepoint == c->buffer->info[i].codepoint))
-      c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
+      c->buffer->info[i].mask |= rtlm_mask;
     else
       c->buffer->info[i].codepoint = codepoint;
   }
 }
 
-static void
-hb_map_glyphs (hb_font_t    *font,
-	       hb_buffer_t  *buffer)
+static inline void
+hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
 {
-  hb_codepoint_t glyph;
+  hb_ot_map_t *map = &c->plan->map;
 
-  if (unlikely (!buffer->len))
-    return;
+  hb_mask_t global_mask = map->get_global_mask ();
+  c->buffer->reset_masks (global_mask);
 
-  buffer->clear_output ();
+  if (c->plan->shaper->setup_masks)
+    c->plan->shaper->setup_masks (c->plan, c->buffer, c->font);
 
-  unsigned int count = buffer->len - 1;
-  for (buffer->idx = 0; buffer->idx < count;) {
-    if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
-      font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &glyph);
-      buffer->replace_glyphs (2, 1, &glyph);
-    } else {
-      font->get_glyph (buffer->cur().codepoint, 0, &glyph);
-      buffer->replace_glyph (glyph);
+  for (unsigned int i = 0; i < c->num_user_features; i++)
+  {
+    const hb_feature_t *feature = &c->user_features[i];
+    if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
+      unsigned int shift;
+      hb_mask_t mask = map->get_mask (feature->tag, &shift);
+      c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
     }
   }
-  if (likely (buffer->idx < buffer->len)) {
-    font->get_glyph (buffer->cur().codepoint, 0, &glyph);
-    buffer->replace_glyph (glyph);
-  }
-  buffer->swap_buffers ();
+}
+
+static inline void
+hb_ot_map_glyphs_fast (hb_buffer_t  *buffer)
+{
+  /* Normalization process sets up glyph_index(), we just copy it. */
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].codepoint = buffer->info[i].glyph_index();
 }
 
-static void
-hb_substitute_default (hb_ot_shape_context_t *c)
+static inline void
+hb_ot_substitute_default (hb_ot_shape_context_t *c)
 {
-  hb_mirror_chars (c);
+  if (c->plan->shaper->preprocess_text)
+    c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
+
+  hb_ot_mirror_chars (c);
+
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, glyph_index);
 
-  hb_map_glyphs (c->font, c->buffer);
+  _hb_ot_shape_normalize (c->font, c->buffer,
+			  c->plan->shaper->normalization_preference ?
+			  c->plan->shaper->normalization_preference (c->plan) :
+			  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT);
+
+  hb_ot_shape_setup_masks (c);
+
+  hb_ot_map_glyphs_fast (c->buffer);
+
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, glyph_index);
 }
 
-static void
+static inline void
 hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
 {
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++)
     c->buffer->info[i].glyph_props() = _hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ?
 				       HB_OT_LAYOUT_GLYPH_CLASS_MARK :
 				       HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
 }
 
 
-static void
+static inline void
 hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
   hb_ot_layout_substitute_start (c->font, c->buffer);
 
   if (!hb_ot_layout_has_glyph_classes (c->face))
     hb_synthesize_glyph_classes (c);
 
   if (hb_ot_layout_has_substitution (c->face))
     c->plan->substitute (c->font, c->buffer);
 
   hb_ot_layout_substitute_finish (c->font, c->buffer);
 
   return;
 }
 
+static inline void
+hb_ot_substitute (hb_ot_shape_context_t *c)
+{
+  hb_ot_substitute_default (c);
+  hb_ot_substitute_complex (c);
+}
 
 /* Position */
 
-static void
-hb_position_default (hb_ot_shape_context_t *c)
+static inline void
+hb_ot_position_default (hb_ot_shape_context_t *c)
 {
   hb_ot_layout_position_start (c->font, c->buffer);
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
     c->font->get_glyph_advance_for_direction (c->buffer->info[i].codepoint,
 					      c->buffer->props.direction,
 					      &c->buffer->pos[i].x_advance,
 					      &c->buffer->pos[i].y_advance);
     c->font->subtract_glyph_origin_for_direction (c->buffer->info[i].codepoint,
 						  c->buffer->props.direction,
 						  &c->buffer->pos[i].x_offset,
 						  &c->buffer->pos[i].y_offset);
   }
 }
 
-static void
-hb_zero_mark_advances (hb_ot_shape_context_t *c)
-{
-  unsigned int count = c->buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    if (_hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
-    {
-      c->buffer->pos[i].x_advance = 0;
-      c->buffer->pos[i].y_advance = 0;
-    }
-}
-
-static void
+static inline bool
 hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
+  bool ret = false;
 
   if (hb_ot_layout_has_positioning (c->face))
   {
     /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */
 
     unsigned int count = c->buffer->len;
     for (unsigned int i = 0; i < count; i++) {
       c->font->add_glyph_origin_for_direction (c->buffer->info[i].codepoint,
@@ -422,33 +413,32 @@ hb_ot_position_complex (hb_ot_shape_cont
 
     for (unsigned int i = 0; i < count; i++) {
       c->font->subtract_glyph_origin_for_direction (c->buffer->info[i].codepoint,
 						    HB_DIRECTION_LTR,
 						    &c->buffer->pos[i].x_offset,
 						    &c->buffer->pos[i].y_offset);
     }
 
-    c->applied_position_complex = true;
-  } else
-    hb_zero_mark_advances (c);
+    ret = true;
+  }
 
   hb_ot_layout_position_finish (c->font, c->buffer, c->plan->shaper->zero_width_attached_marks);
 
-  return;
+  return ret;
 }
 
-static void
-hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
+static inline void
+hb_ot_position_complex_fallback (hb_ot_shape_context_t *c)
 {
-  /* TODO Mark pos */
+  _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
 }
 
-static void
-hb_truetype_kern (hb_ot_shape_context_t *c)
+static inline void
+hb_ot_truetype_kern (hb_ot_shape_context_t *c)
 {
   /* TODO Check for kern=0 */
   unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++) {
     hb_position_t x_kern, y_kern, kern1, kern2;
     c->font->get_glyph_kerning_for_direction (c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint,
 					      c->buffer->props.direction,
 					      &x_kern, &y_kern);
@@ -462,93 +452,84 @@ hb_truetype_kern (hb_ot_shape_context_t 
     kern1 = y_kern >> 1;
     kern2 = y_kern - kern1;
     c->buffer->pos[i - 1].y_advance += kern1;
     c->buffer->pos[i].y_advance += kern2;
     c->buffer->pos[i].y_offset += kern2;
   }
 }
 
-static void
+static inline void
 hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
 {
-  hb_truetype_kern (c);
+  hb_ot_truetype_kern (c);
 }
 
+static inline void
+hb_ot_position (hb_ot_shape_context_t *c)
+{
+  hb_ot_position_default (c);
+
+  hb_bool_t fallback = !hb_ot_position_complex (c);
+
+  if (fallback)
+    hb_ot_position_complex_fallback (c);
+
+  if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
+    hb_buffer_reverse (c->buffer);
+
+  if (fallback)
+    hb_position_complex_fallback_visual (c);
+}
+
+
+/* Post-process */
+
 static void
-hb_hide_zerowidth (hb_ot_shape_context_t *c)
+hb_ot_hide_zerowidth (hb_ot_shape_context_t *c)
 {
   hb_codepoint_t space;
   if (!c->font->get_glyph (' ', 0, &space))
     return; /* No point! */
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++)
     if (unlikely (!is_a_ligature (c->buffer->info[i]) &&
 		  _hb_glyph_info_is_zero_width (&c->buffer->info[i]))) {
       c->buffer->info[i].codepoint = space;
       c->buffer->pos[i].x_advance = 0;
       c->buffer->pos[i].y_advance = 0;
     }
 }
 
 
-/* Do it! */
+/* Pull it all together! */
 
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
 
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
   HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0);
   HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1);
 
   c->buffer->clear_output ();
 
   hb_set_unicode_props (c->buffer);
-
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
 
-  _hb_ot_shape_normalize (c->font, c->buffer,
-			  c->plan->shaper->normalization_preference ?
-			  c->plan->shaper->normalization_preference (c->plan) :
-			  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT);
-
-  hb_ot_shape_setup_masks (c);
-
-  /* SUBSTITUTE */
-  {
-    hb_substitute_default (c);
-
-    hb_ot_substitute_complex (c);
-  }
+  hb_ot_substitute (c);
+  hb_ot_position (c);
 
-  /* POSITION */
-  {
-    hb_position_default (c);
-
-    hb_ot_position_complex (c);
-
-    hb_bool_t position_fallback = !c->applied_position_complex;
-    if (position_fallback)
-      hb_position_complex_fallback (c);
-
-    if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
-      hb_buffer_reverse (c->buffer);
-
-    if (position_fallback)
-      hb_position_complex_fallback_visual (c);
-  }
-
-  hb_hide_zerowidth (c);
+  hb_ot_hide_zerowidth (c);
 
   HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1);
   HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0);
 
   c->buffer->props.direction = c->target_direction;
 
   c->buffer->deallocate_var_all ();
 }
@@ -563,39 +544,49 @@ hb_bool_t
 {
   hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features};
   hb_ot_shape_internal (&c);
 
   return true;
 }
 
 
+
+static inline void
+hb_ot_map_glyphs_dumb (hb_font_t    *font,
+		       hb_buffer_t  *buffer)
+{
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    font->get_glyph (buffer->cur().codepoint, 0, &buffer->cur().codepoint);
+}
+
 void
 hb_ot_shape_glyphs_closure (hb_font_t          *font,
 			    hb_buffer_t        *buffer,
 			    const hb_feature_t *features,
 			    unsigned int        num_features,
 			    hb_set_t           *glyphs)
 {
   hb_ot_shape_plan_t plan;
 
   buffer->guess_properties ();
 
   /* TODO cache / ensure correct backend, etc. */
   hb_shape_plan_t *shape_plan = hb_shape_plan_create (font->face, &buffer->props, features, num_features, NULL);
 
   /* TODO: normalization? have shapers do closure()? */
   /* TODO: Deal with mirrored chars? */
-  hb_map_glyphs (font, buffer);
+  hb_ot_map_glyphs_dumb (font, buffer);
 
   /* Seed it.  It's user's responsibility to have cleard glyphs
    * if that's what they desire. */
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    hb_set_add (glyphs, buffer->info[i].codepoint);
+    glyphs->add (buffer->info[i].codepoint);
 
   /* And find transitive closure. */
   hb_set_t copy;
   copy.init ();
 
   do {
     copy.set (glyphs);
     HB_SHAPER_DATA_GET (shape_plan)->substitute_closure (font->face, glyphs);
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-shape.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright © 2010  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_H
-#define HB_OT_SHAPE_H
-
-#include "hb-common.h"
-#include "hb-shape.h"
-
-
-HB_BEGIN_DECLS
-
-
-hb_bool_t
-hb_ot_shape (hb_font_t          *font,
-	     hb_buffer_t        *buffer,
-	     const hb_feature_t *features,
-	     unsigned int        num_features,
-	     const char * const *shaper_options);
-
-
-HB_END_DECLS
-
-#endif /* HB_OT_SHAPE_H */
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -61,24 +61,27 @@
 # define NULL ((void *) 0)
 #endif
 
 
 /* Basics */
 
 
 #undef MIN
-template <typename Type> static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
+template <typename Type>
+static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
 
 #undef MAX
-template <typename Type> static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
+template <typename Type>
+static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
 
 
 #undef  ARRAY_LENGTH
-#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
+template <typename Type, unsigned int n>
+static inline unsigned int ARRAY_LENGTH (const Type (&a)[n]) { return n; }
 
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
 #define _ASSERT_STATIC1(_line, _cond)	typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
 #define _ASSERT_STATIC0(_line, _cond)	_ASSERT_STATIC1 (_line, (_cond))
 #define ASSERT_STATIC(_cond)		_ASSERT_STATIC0 (__LINE__, (_cond))
 
@@ -783,11 +786,27 @@ hb_bubble_sort (T *array, unsigned int l
 }
 
 template <typename T> inline void
 hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
 {
   hb_bubble_sort (array, len, compar, (int *) NULL);
 }
 
+static inline hb_bool_t
+hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
+{
+  /* Pain because we don't know whether s is nul-terminated. */
+  char buf[64];
+  strncpy (buf, s, MIN (ARRAY_LENGTH (buf) - 1, len));
+  buf[MIN (ARRAY_LENGTH (buf) - 1, len)] = '\0';
+
+  char *end;
+  errno = 0;
+  unsigned long v = strtoul (buf, &end, base);
+  if (errno) return false;
+  if (*end) return false;
+  *out = v;
+  return true;
+}
 
 
 #endif /* HB_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-set-private.hh
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -49,19 +49,19 @@ struct hb_set_digest_common_bits_t
       return;
     }
 
     mask ^= (g & mask) ^ value;
     value &= mask;
   }
 
   inline void add_range (hb_codepoint_t a, hb_codepoint_t b) {
-    /* TODO Speedup. */
-    for (unsigned int i = a; i < b + 1; i++)
-      add (i);
+    /* The negation here stands for ~(x-1). */
+    mask &= -(1 << _hb_bit_storage (a ^ b));
+    value &= mask;
   }
 
   inline bool may_have (hb_codepoint_t g) const {
     return (g & mask) == value;
   }
 
   private:
   mask_t mask;
@@ -78,19 +78,23 @@ struct hb_set_digest_lowest_bits_t
     mask = 0;
   }
 
   inline void add (hb_codepoint_t g) {
     mask |= mask_for (g);
   }
 
   inline void add_range (hb_codepoint_t a, hb_codepoint_t b) {
-    /* TODO Speedup. */
-    for (unsigned int i = a; i < b + 1; i++)
-      add (i);
+    if (b - a >= sizeof (mask_t) * 8 - 1)
+      mask = (mask_t) -1;
+    else {
+      mask_t ma = mask_for (a);
+      mask_t mb = mask_for (b);
+      mask |= mb + (mb - ma) - (mb < ma);
+    }
   }
 
   inline bool may_have (hb_codepoint_t g) const {
     return !!(mask & mask_for (g));
   }
 
   private:
 
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -32,17 +32,17 @@
 
 #define HB_SHAPER_IMPLEMENT(shaper) \
 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 
 
-void
+static void
 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
 		    const hb_feature_t *user_features,
 		    unsigned int        num_user_features,
 		    const char * const *shaper_list)
 {
   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
 
 #define HB_SHAPER_PLAN(shaper) \
@@ -185,21 +185,21 @@ hb_shape_plan_execute (hb_shape_plan    
 }
 
 
 /*
  * caching
  */
 
 #if 0
-static long
+static unsigned int
 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
 {
   return hb_segment_properties_hash (&shape_plan->props) +
-	 shape_plan->default_shaper_list ? 0 : (long) shape_plan->shaper_func;
+	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
 }
 #endif
 
 /* TODO no user-feature caching for now. */
 struct hb_shape_plan_proposal_t
 {
   const hb_segment_properties_t  props;
   const char * const            *shaper_list;
--- a/gfx/harfbuzz/src/hb-shape-plan.h
+++ b/gfx/harfbuzz/src/hb-shape-plan.h
@@ -42,17 +42,17 @@ typedef struct hb_shape_plan_t hb_shape_
 
 HB_INTERNAL hb_shape_plan_t *
 hb_shape_plan_create (hb_face_t                     *face,
 		      const hb_segment_properties_t *props,
 		      const hb_feature_t            *user_features,
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list);
 
-hb_shape_plan_t *
+HB_INTERNAL hb_shape_plan_t *
 hb_shape_plan_create_cached (hb_face_t                     *face,
 			     const hb_segment_properties_t *props,
 			     const hb_feature_t            *user_features,
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list);
 
 HB_INTERNAL hb_shape_plan_t *
 hb_shape_plan_get_empty (void);
--- a/gfx/harfbuzz/src/hb-shaper-list.hh
+++ b/gfx/harfbuzz/src/hb-shaper-list.hh
@@ -24,17 +24,17 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SHAPER_LIST_HH
 #define HB_SHAPER_LIST_HH
 #endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */
 
 /* v--- Add new shapers in the right place here. */
-#ifdef HAVE_GRAPHITE
+#ifdef HAVE_GRAPHITE2
 HB_SHAPER_IMPLEMENT (graphite2)
 #endif
 #ifdef HAVE_UNISCRIBE
 HB_SHAPER_IMPLEMENT (uniscribe)
 #endif
 #ifdef HAVE_CORETEXT
 HB_SHAPER_IMPLEMENT (coretext)
 #endif
--- a/gfx/harfbuzz/src/hb-tt-font.cc
+++ b/gfx/harfbuzz/src/hb-tt-font.cc
@@ -46,17 +46,17 @@ struct hb_tt_font_t
 
 
 static hb_tt_font_t *
 _hb_tt_font_create (hb_font_t *font)
 {
   /* TODO Remove this object altogether */
   hb_tt_font_t *tt = (hb_tt_font_t *) calloc (1, sizeof (hb_tt_font_t));
 
-  tt->hhea_blob = Sanitizer<hhea>::sanitize (hb_face_reference_table (font->face, HB_OT_TAG_hhea));
+  tt->hhea_blob = Sanitizer<hhea>::sanitize (font->face->reference_table (HB_OT_TAG_hhea));
   tt->hhea = Sanitizer<hhea>::lock_instance (tt->hhea_blob);
 
   return tt;
 }
 
 static void
 _hb_tt_font_destroy (hb_tt_font_t *tt)
 {
@@ -71,173 +71,9 @@ static inline const hhea&
 //  return likely (face->tt && face->tt->hhea) ? *face->tt->hhea : Null(hhea);
 }
 
 
 /*
  * hb_tt_font_funcs_t
  */
 
-static hb_bool_t
-hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
-		       void *font_data HB_UNUSED,
-		       hb_codepoint_t unicode,
-		       hb_codepoint_t variation_selector,
-		       hb_codepoint_t *glyph,
-		       void *user_data HB_UNUSED)
-{
-  if (font->parent)
-    return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph);
-
-  *glyph = 0;
-  return false;
-}
-
-static hb_position_t
-hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
-				 void *user_data HB_UNUSED)
-{
-  if (font->parent)
-    return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph));
-
-  return font->x_scale;
-}
-
-static hb_position_t
-hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
-				 void *user_data HB_UNUSED)
-{
-  if (font->parent)
-    return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph));
-
-  return font->y_scale;
-}
-
-static hb_bool_t
-hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
-				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph,
-				hb_position_t *x,
-				hb_position_t *y,
-				void *user_data HB_UNUSED)
-{
-  if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent,
-						glyph,
-						x, y);
-    if (ret)
-      font->parent_scale_position (x, y);
-    return ret;
-  }
-
-  *x = *y = 0;
-  return false;
-}
-
-static hb_bool_t
-hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
-				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph,
-				hb_position_t *x,
-				hb_position_t *y,
-				void *user_data HB_UNUSED)
-{
-  if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent,
-						glyph,
-						x, y);
-    if (ret)
-      font->parent_scale_position (x, y);
-    return ret;
-  }
-
-  *x = *y = 0;
-  return false;
-}
-
-static hb_position_t
-hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t left_glyph,
-				 hb_codepoint_t right_glyph,
-				 void *user_data HB_UNUSED)
-{
-  if (font->parent)
-    return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph));
-
-  return 0;
-}
-
-static hb_position_t
-hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t top_glyph,
-				 hb_codepoint_t bottom_glyph,
-				 void *user_data HB_UNUSED)
-{
-  if (font->parent)
-    return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph));
-
-  return 0;
-}
-
-static hb_bool_t
-hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
-			       void *font_data HB_UNUSED,
-			       hb_codepoint_t glyph,
-			       hb_glyph_extents_t *extents,
-			       void *user_data HB_UNUSED)
-{
-  if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_extents (font->parent,
-					       glyph,
-					       extents);
-    if (ret) {
-      font->parent_scale_position (&extents->x_bearing, &extents->y_bearing);
-      font->parent_scale_distance (&extents->width, &extents->height);
-    }
-    return ret;
-  }
-
-  memset (extents, 0, sizeof (*extents));
-  return false;
-}
-
-static hb_bool_t
-hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t glyph,
-				     unsigned int point_index,
-				     hb_position_t *x,
-				     hb_position_t *y,
-				     void *user_data HB_UNUSED)
-{
-  if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent,
-						     glyph, point_index,
-						     x, y);
-    if (ret)
-      font->parent_scale_position (x, y);
-    return ret;
-  }
-
-  *x = *y = 0;
-  return false;
-}
-
-
-static hb_font_funcs_t _hb_font_funcs_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  true, /* immutable */
-
-  {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
-    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_FONT_FUNC_IMPLEMENT
-  }
-};
 #endif
-
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -75,63 +75,23 @@ struct hb_unicode_funcs_t {
   inline return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); }
 HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
 #undef HB_UNICODE_FUNC_IMPLEMENT
 
   inline hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b,
 			    hb_codepoint_t *ab)
   {
     *ab = 0;
-    /* XXX, this belongs to indic normalizer. */
-    if ((FLAG (general_category (a)) &
-	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
-      return false;
-    /* XXX, add composition-exclusion exceptions to Indic shaper. */
-    if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
+    if (unlikely (!a || !b)) return false;
     return func.compose (this, a, b, ab, user_data.compose);
   }
 
   inline hb_bool_t decompose (hb_codepoint_t ab,
 			      hb_codepoint_t *a, hb_codepoint_t *b)
   {
-    /* XXX FIXME, move these to complex shapers and propagage to normalizer.*/
-    switch (ab) {
-      case 0x0AC9  : return false;
-
-      case 0x0931  : return false;
-      case 0x0B94  : return false;
-
-      /* These ones have Unicode decompositions, but we do it
-       * this way to be close to what Uniscribe does. */
-      case 0x0DDA  : *a = 0x0DD9; *b= 0x0DDA; return true;
-      case 0x0DDC  : *a = 0x0DD9; *b= 0x0DDC; return true;
-      case 0x0DDD  : *a = 0x0DD9; *b= 0x0DDD; return true;
-      case 0x0DDE  : *a = 0x0DD9; *b= 0x0DDE; return true;
-
-      case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
-      case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
-      case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
-      case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
-      case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
-      case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
-      case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
-      case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
-      case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
-      case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
-      case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
-      case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
-#if 0
-      case 0x0B57  : *a = 0xno decomp, -> RIGHT; return true;
-      case 0x1C29  : *a = 0xno decomp, -> LEFT; return true;
-      case 0xA9C0  : *a = 0xno decomp, -> RIGHT; return true;
-      case 0x111BF  : *a = 0xno decomp, -> ABOVE; return true;
-#endif
-    }
     *a = ab; *b = 0;
     return func.decompose (this, ab, a, b, user_data.decompose);
   }
 
   inline unsigned int decompose_compatibility (hb_codepoint_t  u,
 					       hb_codepoint_t *decomposed)
   {
     unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility);
@@ -218,22 +178,94 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
   struct {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
   } destroy;
 };
 
 
-#ifdef HAVE_GLIB
-extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs;
-#define _hb_unicode_funcs_default _hb_glib_unicode_funcs
-#elif defined(HAVE_ICU)
-extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs;
-#define _hb_unicode_funcs_default _hb_icu_unicode_funcs
-#else
-#define HB_UNICODE_FUNCS_NIL 1
 extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
-#define _hb_unicode_funcs_default _hb_unicode_funcs_nil
-#endif
+
+
+/* Modified combining marks */
+
+/* Hebrew
+ *
+ * We permute the "fixed-position" classes 10-26 into the order
+ * described in the SBL Hebrew manual:
+ *
+ * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
+ *
+ * (as recommended by:
+ *  http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html)
+ *
+ * More details here:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC10 22 /* sheva */
+#define HB_MODIFIED_COMBINING_CLASS_CCC11 15 /* hataf segol */
+#define HB_MODIFIED_COMBINING_CLASS_CCC12 16 /* hataf patah */
+#define HB_MODIFIED_COMBINING_CLASS_CCC13 17 /* hataf qamats */
+#define HB_MODIFIED_COMBINING_CLASS_CCC14 23 /* hiriq */
+#define HB_MODIFIED_COMBINING_CLASS_CCC15 18 /* tsere */
+#define HB_MODIFIED_COMBINING_CLASS_CCC16 19 /* segol */
+#define HB_MODIFIED_COMBINING_CLASS_CCC17 20 /* patah */
+#define HB_MODIFIED_COMBINING_CLASS_CCC18 21 /* qamats */
+#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam */
+#define HB_MODIFIED_COMBINING_CLASS_CCC20 24 /* qubuts */
+#define HB_MODIFIED_COMBINING_CLASS_CCC21 12 /* dagesh */
+#define HB_MODIFIED_COMBINING_CLASS_CCC22 25 /* meteg */
+#define HB_MODIFIED_COMBINING_CLASS_CCC23 13 /* rafe */
+#define HB_MODIFIED_COMBINING_CLASS_CCC24 10 /* shin dot */
+#define HB_MODIFIED_COMBINING_CLASS_CCC25 11 /* sin dot */
+#define HB_MODIFIED_COMBINING_CLASS_CCC26 26 /* point varika */
+
+/*
+ * Arabic
+ *
+ * Modify to move Shadda (ccc=33) before other marks.  See:
+ * http://unicode.org/faq/normalization.html#8
+ * http://unicode.org/faq/normalization.html#9
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC27 28 /* fathatan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC28 29 /* dammatan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC29 30 /* kasratan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC30 31 /* fatha */
+#define HB_MODIFIED_COMBINING_CLASS_CCC31 32 /* damma */
+#define HB_MODIFIED_COMBINING_CLASS_CCC32 33 /* kasra */
+#define HB_MODIFIED_COMBINING_CLASS_CCC33 27 /* shadda */
+#define HB_MODIFIED_COMBINING_CLASS_CCC34 34 /* sukun */
+#define HB_MODIFIED_COMBINING_CLASS_CCC35 35 /* superscript alef */
+
+/* Syriac */
+#define HB_MODIFIED_COMBINING_CLASS_CCC36 36 /* superscript alaph */
+
+/* Telugu
+ *
+ * Modify Telugu length marks (ccc=84, ccc=91).
+ * These are the only matras in the main Indic scripts range that have
+ * a non-zero ccc.  That makes them reorder with the Halant that is
+ * ccc=9.  Just zero them, we don't need them in our Indic shaper.
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC84 0 /* length mark */
+#define HB_MODIFIED_COMBINING_CLASS_CCC91 0 /* ai length mark */
+
+/* Thai
+ *
+ * Modify U+0E38 and U+0E39 (ccc=103) to be reordered before U+0E3A (ccc=9).
+ * Assign 3, which is unassigned otherwise.
+ * Uniscribe does this reordering too.
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */
+#define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */
+
+/* Lao */
+#define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */
+#define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */
+
+/* Tibetan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
+#define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */
+#define HB_MODIFIED_COMBINING_CLASS_CCC132 132 /* sign u */
 
 
 #endif /* HB_UNICODE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-unicode.cc
+++ b/gfx/harfbuzz/src/hb-unicode.cc
@@ -104,22 +104,54 @@ hb_unicode_decompose_compatibility_nil (
 					hb_codepoint_t      u          HB_UNUSED,
 					hb_codepoint_t     *decomposed HB_UNUSED,
 					void               *user_data  HB_UNUSED)
 {
   return 0;
 }
 
 
+#define HB_UNICODE_FUNCS_IMPLEMENT_SET \
+  HB_UNICODE_FUNCS_IMPLEMENT (glib) \
+  HB_UNICODE_FUNCS_IMPLEMENT (icu) \
+  HB_UNICODE_FUNCS_IMPLEMENT (nil) \
+  /* ^--- Add new callbacks before nil */
+
+#define hb_nil_get_unicode_funcs hb_unicode_funcs_get_empty
+
+/* Prototype them all */
+#define HB_UNICODE_FUNCS_IMPLEMENT(set) \
+extern "C" hb_unicode_funcs_t *hb_##set##_get_unicode_funcs (void);
+HB_UNICODE_FUNCS_IMPLEMENT_SET
+#undef HB_UNICODE_FUNCS_IMPLEMENT
+
+
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_default (void)
 {
-  return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default);
+#define HB_UNICODE_FUNCS_IMPLEMENT(set) \
+  return hb_##set##_get_unicode_funcs ();
+
+#ifdef HAVE_GLIB
+  HB_UNICODE_FUNCS_IMPLEMENT(glib)
+#elif defined(HAVE_ICU)
+  HB_UNICODE_FUNCS_IMPLEMENT(icu)
+#else
+#define HB_UNICODE_FUNCS_NIL 1
+  HB_UNICODE_FUNCS_IMPLEMENT(nil)
+#endif
+
+#undef HB_UNICODE_FUNCS_IMPLEMENT
 }
 
+#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
+#pragma message("Could not find any Unicode functions implementation, you have to provide your own.")
+#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS.")
+#endif
+
 hb_unicode_funcs_t *
 hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
 {
   hb_unicode_funcs_t *ufuncs;
 
   if (!(ufuncs = hb_object_create<hb_unicode_funcs_t> ()))
     return hb_unicode_funcs_get_empty ();
 
@@ -135,17 +167,16 @@ hb_unicode_funcs_create (hb_unicode_func
    * onto it and it's immutable.  We should not copy the destroy notifiers
    * though. */
   ufuncs->user_data = parent->user_data;
 
   return ufuncs;
 }
 
 
-extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
 const hb_unicode_funcs_t _hb_unicode_funcs_nil = {
   HB_OBJECT_HEADER_STATIC,
 
   NULL, /* parent */
   true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
@@ -282,121 +313,88 @@ unsigned int
 hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
 				    hb_codepoint_t      u,
 				    hb_codepoint_t     *decomposed)
 {
   return ufuncs->decompose_compatibility (u, decomposed);
 }
 
 
+/* See hb-unicode-private.hh for details. */
 const uint8_t
 _hb_modified_combining_class[256] =
 {
   0, /* HB_UNICODE_COMBINING_CLASS_NOT_REORDERED */
   1, /* HB_UNICODE_COMBINING_CLASS_OVERLAY */
   2, 3, 4, 5, 6,
   7, /* HB_UNICODE_COMBINING_CLASS_NUKTA */
   8, /* HB_UNICODE_COMBINING_CLASS_KANA_VOICING */
   9, /* HB_UNICODE_COMBINING_CLASS_VIRAMA */
 
   /* Hebrew */
-
-  /*
-   * We permute the "fixed-position" classes 10-26 into the order
-   * described in the SBL Hebrew manual:
-   *
-   * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
-   *
-   * (as recommended by:
-   *  http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html)
-   *
-   * More details here:
-   * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
-   */
-  22, /* HB_UNICODE_COMBINING_CLASS_CCC10 sheva */
-  15, /* HB_UNICODE_COMBINING_CLASS_CCC11 hataf segol */
-  16, /* HB_UNICODE_COMBINING_CLASS_CCC12 hataf patah*/
-  17, /* HB_UNICODE_COMBINING_CLASS_CCC13 hataf qamats */
-  23, /* HB_UNICODE_COMBINING_CLASS_CCC14 hiriq */
-  18, /* HB_UNICODE_COMBINING_CLASS_CCC15 tsere */
-  19, /* HB_UNICODE_COMBINING_CLASS_CCC16 segol */
-  20, /* HB_UNICODE_COMBINING_CLASS_CCC17 patah */
-  21, /* HB_UNICODE_COMBINING_CLASS_CCC18 qamats */
-  14, /* HB_UNICODE_COMBINING_CLASS_CCC19 holam */
-  24, /* HB_UNICODE_COMBINING_CLASS_CCC20 qubuts */
-  12, /* HB_UNICODE_COMBINING_CLASS_CCC21 dagesh */
-  25, /* HB_UNICODE_COMBINING_CLASS_CCC22 meteg */
-  13, /* HB_UNICODE_COMBINING_CLASS_CCC23 rafe */
-  10, /* HB_UNICODE_COMBINING_CLASS_CCC24 shin dot */
-  11, /* HB_UNICODE_COMBINING_CLASS_CCC25 sin dot */
-
-  26, /* HB_UNICODE_COMBINING_CLASS_CCC26 */
+  HB_MODIFIED_COMBINING_CLASS_CCC10,
+  HB_MODIFIED_COMBINING_CLASS_CCC11,
+  HB_MODIFIED_COMBINING_CLASS_CCC12,
+  HB_MODIFIED_COMBINING_CLASS_CCC13,
+  HB_MODIFIED_COMBINING_CLASS_CCC14,
+  HB_MODIFIED_COMBINING_CLASS_CCC15,
+  HB_MODIFIED_COMBINING_CLASS_CCC16,
+  HB_MODIFIED_COMBINING_CLASS_CCC17,
+  HB_MODIFIED_COMBINING_CLASS_CCC18,
+  HB_MODIFIED_COMBINING_CLASS_CCC19,
+  HB_MODIFIED_COMBINING_CLASS_CCC20,
+  HB_MODIFIED_COMBINING_CLASS_CCC21,
+  HB_MODIFIED_COMBINING_CLASS_CCC22,
+  HB_MODIFIED_COMBINING_CLASS_CCC23,
+  HB_MODIFIED_COMBINING_CLASS_CCC24,
+  HB_MODIFIED_COMBINING_CLASS_CCC25,
+  HB_MODIFIED_COMBINING_CLASS_CCC26,
 
   /* Arabic */
-
-  /*
-   * Modify to move Shadda (ccc=33) before other marks.  See:
-   * http://unicode.org/faq/normalization.html#8
-   * http://unicode.org/faq/normalization.html#9
-   */
-  28, /* HB_UNICODE_COMBINING_CLASS_CCC27 */
-  29, /* HB_UNICODE_COMBINING_CLASS_CCC28 */
-  30, /* HB_UNICODE_COMBINING_CLASS_CCC29 */
-  31, /* HB_UNICODE_COMBINING_CLASS_CCC30 */
-  32, /* HB_UNICODE_COMBINING_CLASS_CCC31 */
-  33, /* HB_UNICODE_COMBINING_CLASS_CCC32 */
-  27, /* HB_UNICODE_COMBINING_CLASS_CCC33 shadda */
-
-  34, /* HB_UNICODE_COMBINING_CLASS_CCC34 */
-  35, /* HB_UNICODE_COMBINING_CLASS_CCC35 */
+  HB_MODIFIED_COMBINING_CLASS_CCC27,
+  HB_MODIFIED_COMBINING_CLASS_CCC28,
+  HB_MODIFIED_COMBINING_CLASS_CCC29,
+  HB_MODIFIED_COMBINING_CLASS_CCC30,
+  HB_MODIFIED_COMBINING_CLASS_CCC31,
+  HB_MODIFIED_COMBINING_CLASS_CCC32,
+  HB_MODIFIED_COMBINING_CLASS_CCC33,
+  HB_MODIFIED_COMBINING_CLASS_CCC34,
+  HB_MODIFIED_COMBINING_CLASS_CCC35,
 
   /* Syriac */
-  36, /* HB_UNICODE_COMBINING_CLASS_CCC36 */
+  HB_MODIFIED_COMBINING_CLASS_CCC36,
 
   37, 38, 39,
   40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
   60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
   80, 81, 82, 83,
 
   /* Telugu */
-
-  /*
-   * Modify Telugu length marks (ccc=84, ccc=91).
-   * These are the only matras in the main Indic scripts range that have
-   * a non-zero ccc.  That makes them reorder with the Halant that is
-   * ccc=9.  Just zero them, we don't need them in our Indic shaper.
-   */
-  0, /* HB_UNICODE_COMBINING_CLASS_CCC84 */
+  HB_MODIFIED_COMBINING_CLASS_CCC84,
   85, 86, 87, 88, 89, 90,
-  0, /* HB_UNICODE_COMBINING_CLASS_CCC91 */
+  HB_MODIFIED_COMBINING_CLASS_CCC91,
   92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
 
   /* Thai */
-
-  /*
-   * Modify U+0E38 and U+0E39 (ccc=104) to be reordered before U+0E3A (ccc=9).
-   * Uniscribe does this too.
-   */
-  3, /* HB_UNICODE_COMBINING_CLASS_CCC103 */
-
+  HB_MODIFIED_COMBINING_CLASS_CCC103,
   104, 105, 106,
-  107, /* HB_UNICODE_COMBINING_CLASS_CCC107 */
+  HB_MODIFIED_COMBINING_CLASS_CCC107,
   108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
 
   /* Lao */
-  118, /* HB_UNICODE_COMBINING_CLASS_CCC118 */
+  HB_MODIFIED_COMBINING_CLASS_CCC118,
   119, 120, 121,
-  122, /* HB_UNICODE_COMBINING_CLASS_CCC122 */
+  HB_MODIFIED_COMBINING_CLASS_CCC122,
   123, 124, 125, 126, 127, 128,
 
   /* Tibetan */
-  129, /* HB_UNICODE_COMBINING_CLASS_CCC129 */
-  130, /* HB_UNICODE_COMBINING_CLASS_CCC130 */
+  HB_MODIFIED_COMBINING_CLASS_CCC129,
+  HB_MODIFIED_COMBINING_CLASS_CCC130,
   131,
-  132, /* HB_UNICODE_COMBINING_CLASS_CCC133 */
+  HB_MODIFIED_COMBINING_CLASS_CCC132,
   133, 134, 135, 136, 137, 138, 139,
 
 
   140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
   150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
   160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
   170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
   180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-uniscribe-private.hh
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright © 2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_UNISCRIBE_PRIVATE_HH
-#define HB_UNISCRIBE_PRIVATE_HH
-
-#include "hb-private.hh"
-
-#include "hb-uniscribe.h"
-
-
-HB_INTERNAL hb_bool_t
-_hb_uniscribe_shape (hb_font_t          *font,
-		     hb_buffer_t        *buffer,
-		     const hb_feature_t *features,
-		     unsigned int        num_features);
-
-
-#endif /* HB_UNISCRIBE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -20,16 +20,17 @@
  * 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
  */
 
 #define _WIN32_WINNT 0x0600
+#define WIN32_LEAN_AND_MEAN
 
 #define HB_SHAPER uniscribe
 #include "hb-shaper-impl-private.hh"
 
 #include <windows.h>
 #include <usp10.h>
 
 typedef ULONG WIN_ULONG;
@@ -192,25 +193,25 @@ void
 
 /*
  * shaper shape_plan data
  */
 
 struct hb_uniscribe_shaper_shape_plan_data_t {};
 
 hb_uniscribe_shaper_shape_plan_data_t *
-_hb_uniscribe_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
-					     const hb_feature_t *user_features,
-					     unsigned int        num_user_features)
+_hb_uniscribe_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
+					     const hb_feature_t *user_features HB_UNUSED,
+					     unsigned int        num_user_features HB_UNUSED)
 {
   return (hb_uniscribe_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_uniscribe_shaper_shape_plan_data_destroy (hb_uniscribe_shaper_shape_plan_data_t *data)
+_hb_uniscribe_shaper_shape_plan_data_destroy (hb_uniscribe_shaper_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
--- a/gfx/harfbuzz/src/hb-uniscribe.h
+++ b/gfx/harfbuzz/src/hb-uniscribe.h
@@ -24,17 +24,19 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_UNISCRIBE_H
 #define HB_UNISCRIBE_H
 
 #include "hb.h"
 
+#ifndef _WIN32_WINNT
 #define _WIN32_WINNT 0x0600
+#endif
 #include <windows.h>
 
 HB_BEGIN_DECLS
 
 
 LOGFONTW *
 hb_uniscribe_font_get_logfontw (hb_font_t *font);
 
--- a/gfx/harfbuzz/src/hb-warning.cc
+++ b/gfx/harfbuzz/src/hb-warning.cc
@@ -32,16 +32,8 @@
 #pragma message("Could not find any system to define atomic_int macros, library may NOT be thread-safe.")
 #endif
 #if defined(HB_MUTEX_IMPL_NIL)
 #pragma message("Could not find any system to define mutex macros, library may NOT be thread-safe.")
 #endif
 #if defined(HB_ATOMIC_INT_NIL) || defined(HB_MUTEX_IMPL_NIL)
 #pragma message("To suppress these warnings, define HB_NO_MT.")
 #endif
-
-
-#include "hb-unicode-private.hh"
-
-#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
-#pragma message("Could not find any Unicode functions implementation, you have to provide your own.")
-#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS.")
-#endif
--- a/gfx/harfbuzz/src/test-would-substitute.cc
+++ b/gfx/harfbuzz/src/test-would-substitute.cc
@@ -83,12 +83,21 @@ main (int argc, char **argv)
     blob = hb_blob_create (font_data, len, mm, user_data, destroy);
   }
 
   /* Create the face */
   hb_face_t *face = hb_face_create (blob, 0 /* first face */);
   hb_blob_destroy (blob);
   blob = NULL;
 
+  hb_font_t *font = hb_font_create (face);
+#ifdef HAVE_FREETYPE
+  hb_ft_font_set_funcs (font);
+#endif
+
   unsigned int len = argc - 3;
-  hb_codepoint_t glyphs[2] = {strtol (argv[3], NULL, 0), argc > 4 ? strtol (argv[4], NULL, 0) : (hb_codepoint_t) -1};
+  hb_codepoint_t glyphs[2];
+  if (!hb_font_glyph_from_string (font, argv[3], -1, &glyphs[0]) ||
+      (argc > 4 &&
+       !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1])))
+    return 2;
   return !hb_ot_layout_would_substitute_lookup (face, glyphs, len, strtol (argv[2], NULL, 0));
 }
--- a/js/src/build/autoconf/config.status.m4
+++ b/js/src/build/autoconf/config.status.m4
@@ -69,28 +69,30 @@ test "x$exec_prefix" = xNONE && exec_pre
 trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
 
 : ${CONFIG_STATUS=./config.status}
 
 dnl We're going to need [ ] for python syntax.
 changequote(<<<, >>>)dnl
 echo creating $CONFIG_STATUS
 
+extra_python_path=${COMM_BUILD:+"'mozilla', "}
+
 cat > $CONFIG_STATUS <<EOF
 #!${PYTHON}
 # coding=$encoding
 
 import os, sys
 dnl topsrcdir is the top source directory in native form, as opposed to a
 dnl form suitable for make.
 topsrcdir = '''${WIN_TOP_SRC:-$srcdir}'''
 if not os.path.isabs(topsrcdir):
     topsrcdir = os.path.normpath(os.path.join(os.path.dirname(<<<__file__>>>), topsrcdir))
 dnl Don't rely on virtualenv here. Standalone js doesn't use it.
-sys.path.append(os.path.join(topsrcdir, ${COMM_BUILD:+'mozilla',} 'build'))
+sys.path.append(os.path.join(topsrcdir, ${extra_python_path}'build'))
 from ConfigStatus import config_status
 
 args = {
     'topsrcdir': topsrcdir,
     'topobjdir': os.path.dirname(<<<__file__>>>),
 
 dnl All defines and substs are stored with an additional space at the beginning
 dnl and at the end of the string, to avoid any problem with values starting or
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -4085,17 +4085,17 @@ MOZ_ARG_DISABLE_BOOL(md,
      _cpp_md_flag=1
    fi
   dnl Default is to use -xM if using Sun Studio on Solaris
    if test "$SOLARIS_SUNPRO_CC"; then
      _cpp_md_flag=1
    fi])
 if test "$_cpp_md_flag"; then
   COMPILER_DEPEND=1
-  _DEPEND_CFLAGS='$(filter-out %/.pp,-MD -MF $(MDDEPDIR)/$(@F).pp)'
+  _DEPEND_CFLAGS='$(filter-out %/.pp,-MMD -MF $(MDDEPDIR)/$(@F).pp)'
   dnl Sun Studio on Solaris use -xM instead of -MD, see config/rules.mk
   if test "$SOLARIS_SUNPRO_CC"; then
     _DEPEND_CFLAGS=
   fi
 else
   COMPILER_DEPEND=
   dnl Don't override this for MSVC
   if test -z "$_WIN32_MSVC"; then
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug730085.js
@@ -0,0 +1,6 @@
+load(libdir + "asserts.js");
+var calledToString = false;
+assertThrowsInstanceOf(function () { Object.prototype.hasOwnProperty.call(null,
+                      {toString: function () { calledToString = true; }}); },
+                       TypeError);
+assertEq(calledToString, true);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -739,54 +739,55 @@ obj_unwatch(JSContext *cx, unsigned argc
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
 /*
  * Prototype and property query methods, to complement the 'in' and
  * 'instanceof' operators.
  */
 
-/* Proposed ECMA 15.2.4.5. */
+/* ECMA 15.2.4.5. */
 static JSBool
 obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
+    /* Step 1. */
+    RootedId id(cx);
+    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
+        return false;
+
+    /* Step 2. */
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
-    return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupGeneric, argc, args.rval().address());
+    return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupGeneric, obj, id, args.rval());
 }
 
 JSBool
-js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, unsigned argc,
-                        Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedId id(cx);
-    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
-        return JS_FALSE;
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
+js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, HandleObject obj,
+                        HandleId id, MutableHandleValue rval)
+{
+    /* Non-standard code for proxies. */
     RootedObject obj2(cx);
     RootedShape prop(cx);
     if (obj->isProxy()) {
         bool has;
         if (!Proxy::hasOwn(cx, obj, id, &has))
             return false;
-        args.rval().setBoolean(has);
+        rval.setBoolean(has);
         return true;
     }
+
+    /* Step 3. */
     if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
-        return JS_FALSE;
-    args.rval().setBoolean(!!prop);
-    return JS_TRUE;
+        return false;
+    /* Step 4,5. */
+    rval.setBoolean(!!prop);
+    return true;
 }
 
 JSBool
 js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, HandleObject obj, HandleId id,
                   MutableHandleObject objp, MutableHandleShape propp)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
     if (lookup) {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1056,18 +1056,18 @@ js_LeaveSharpObject(JSContext *cx, JSIdA
 /*
  * Mark objects stored in map if GC happens between js_EnterSharpObject
  * and js_LeaveSharpObject. GC calls this when map->depth > 0.
  */
 extern void
 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map);
 
 extern JSBool
-js_HasOwnPropertyHelper(JSContext *cx, js::LookupGenericOp lookup, unsigned argc,
-                        js::Value *vp);
+js_HasOwnPropertyHelper(JSContext *cx, js::LookupGenericOp lookup, js::HandleObject obj,
+                        js::HandleId id, js::MutableHandleValue rval);
 
 extern JSBool
 js_HasOwnProperty(JSContext *cx, js::LookupGenericOp lookup, js::HandleObject obj, js::HandleId id,
                   js::MutableHandleObject objp, js::MutableHandleShape propp);
 
 extern JSBool
 js_PropertyIsEnumerable(JSContext *cx, js::HandleObject obj, js::HandleId id, js::Value *vp);
 
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -5880,35 +5880,39 @@ xml_elements(JSContext *cx, unsigned arg
 
     return xml_elements_helper(cx, obj, xml, nameqn, vp);
 }
 
 /* XML and XMLList */
 static JSBool
 xml_hasOwnProperty(JSContext *cx, unsigned argc, jsval *vp)
 {
-    jsval name;
-    JSBool found;
-
-    JSObject *obj = ToObject(cx, HandleValue::fromMarkedLocation(&vp[1]));
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
-        return JS_FALSE;
+        return false;
     if (!obj->isXML()) {
         ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass);
-        return JS_FALSE;
-    }
-
-    name = argc != 0 ? vp[2] : JSVAL_VOID;
+        return false;
+    }
+
+    Value name = args.length() != 0 ? args[0] : UndefinedValue();
+    JSBool found;
     if (!HasProperty(cx, obj, name, &found))
-        return JS_FALSE;
+        return false;
     if (found) {
-        *vp = JSVAL_TRUE;
-        return JS_TRUE;
-    }
-    return js_HasOwnPropertyHelper(cx, baseops::LookupProperty, argc, vp);
+        args.rval().setBoolean(true);
+        return true;
+    }
+
+    RootedId id(cx);
+    if (!ValueToId(cx, name, id.address()))
+        return false;
+    return js_HasOwnPropertyHelper(cx, baseops::LookupProperty, obj, id, args.rval());
 }
 
 /* XML and XMLList */
 static JSBool
 xml_hasComplexContent(JSContext *cx, unsigned argc, jsval *vp)
 {
     JSXML *kid;
     JSObject *kidobj;
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -315,18 +315,18 @@ mjit::Compiler::compileGetChar(FrameEntr
 
     Address lengthAndFlagsAddr(strReg, JSString::offsetOfLengthAndFlags());
 
     /* Load lengthAndFlags in reg1 and reg2 */
     masm.loadPtr(lengthAndFlagsAddr, reg1);
     masm.move(reg1, reg2);
 
     /* Slow path if string is a rope */
-    masm.andPtr(ImmPtr((void *)JSString::ROPE_BIT), reg1);
-    Jump isRope = masm.branchTestPtr(Assembler::NonZero, reg1);
+    masm.andPtr(ImmPtr((void *)JSString::FLAGS_MASK), reg1);
+    Jump isRope = masm.branchTestPtr(Assembler::Zero, reg1);
     stubcc.linkExit(isRope, Uses(3));
 
     /* Slow path if out-of-range. */
     masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), reg2);
     Jump outOfRange = masm.branchPtr(Assembler::AboveOrEqual, argReg, reg2);
     stubcc.linkExit(outOfRange, Uses(3));
 
     /* Load char code in reg2. */
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -2513,26 +2513,26 @@ mjit::Compiler::jsop_stricteq(JSOp op)
             frame.pinReg(reg2);
         }
 
         JS_ASSERT(reg1 != resultReg);
         JS_ASSERT(reg1 != tmpReg);
         JS_ASSERT(reg2 != resultReg);
         JS_ASSERT(reg2 != tmpReg);
 
-        /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */
-        Imm32 atomMask(JSString::ATOM_MASK);
+        /* JSString::isAtom === (lengthAndFlags & ATOM_BIT) */
+        Imm32 atomBit(JSString::ATOM_BIT);
 
         masm.load32(Address(reg1, JSString::offsetOfLengthAndFlags()), tmpReg);
-        Jump op1NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
+        Jump op1NotAtomized = masm.branchTest32(Assembler::Zero, tmpReg, atomBit);
         stubcc.linkExit(op1NotAtomized, Uses(2));
 
         if (!op2->isConstant()) {
             masm.load32(Address(reg2, JSString::offsetOfLengthAndFlags()), tmpReg);
-            Jump op2NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
+            Jump op2NotAtomized = masm.branchTest32(Assembler::Zero, tmpReg, atomBit);
             stubcc.linkExit(op2NotAtomized, Uses(2));
         }
 
         masm.set32(cond, reg1, reg2, resultReg);
 
         frame.unpinReg(reg1);
         if (op2->isConstant())
             frame.freeReg(reg2);
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -240,26 +240,26 @@ class EqualityCompiler : public BaseComp
 
         if (!rvr.isType(JSVAL_TYPE_STRING)) {
             Jump rhsFail = masm.testString(Assembler::NotEqual, rvr.typeReg());
             linkToStub(rhsFail);
         }
 
         RegisterID tmp = ic.tempReg;
 
-        /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */
-        Imm32 atomMask(JSString::ATOM_MASK);
+        /* JSString::isAtom === (lengthAndFlags & ATOM_BIT) */
+        Imm32 atomBit(JSString::ATOM_BIT);
 
         masm.load32(Address(lvr.dataReg(), JSString::offsetOfLengthAndFlags()), tmp);
-        Jump lhsNotAtomized = masm.branchTest32(Assembler::NonZero, tmp, atomMask);
+        Jump lhsNotAtomized = masm.branchTest32(Assembler::Zero, tmp, atomBit);
         linkToStub(lhsNotAtomized);
 
         if (!rvr.isConstant()) {
             masm.load32(Address(rvr.dataReg(), JSString::offsetOfLengthAndFlags()), tmp);
-            Jump rhsNotAtomized = masm.branchTest32(Assembler::NonZero, tmp, atomMask);
+            Jump rhsNotAtomized = masm.branchTest32(Assembler::Zero, tmp, atomBit);
             linkToStub(rhsNotAtomized);
         }
 
         if (rvr.isConstant()) {
             JSString *str = rvr.value().toString();
             JS_ASSERT(str->isAtom());
             Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), ImmPtr(str));
             linkTrue(test);
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -98,17 +98,17 @@ JSString::validateLength(JSContext *cx, 
     }
 
     return true;
 }
 
 JS_ALWAYS_INLINE void
 JSRope::init(JSString *left, JSString *right, size_t length)
 {
-    d.lengthAndFlags = buildLengthAndFlags(length, ROPE_BIT);
+    d.lengthAndFlags = buildLengthAndFlags(length, ROPE_FLAGS);
     d.u1.left = left;
     d.s.u2.right = right;
     JSString::writeBarrierPost(d.u1.left, &d.u1.left);
     JSString::writeBarrierPost(d.s.u2.right, &d.s.u2.right);
 }
 
 JS_ALWAYS_INLINE JSRope *
 JSRope::new_(JSContext *cx, js::HandleString left, js::HandleString right, size_t length)
@@ -208,17 +208,17 @@ JSFixedString::new_(JSContext *cx, const
         return NULL;
     str->init(chars, length);
     return str;
 }
 
 JS_ALWAYS_INLINE JSAtom *
 JSFixedString::morphAtomizedStringIntoAtom()
 {
-    d.lengthAndFlags = buildLengthAndFlags(length(), ATOM_FLAGS);
+    d.lengthAndFlags = buildLengthAndFlags(length(), ATOM_BIT);
     return &asAtom();
 }
 
 JS_ALWAYS_INLINE JSInlineString *
 JSInlineString::new_(JSContext *cx)
 {
     return (JSInlineString *)js_NewGCString(cx);
 }
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -173,46 +173,55 @@ class JSString : public js::gc::Cell
      *
      * The string type encoding can be summarized as follows. The "instance
      * encoding" entry for a type specifies the flag bits used to create a
      * string instance of that type. Abstract types have no instances and thus
      * have no such entry. The "subtype predicate" entry for a type specifies
      * the predicate used to query whether a JSString instance is subtype
      * (reflexively) of that type.
      *
-     *   string       instance   subtype
-     *   type         encoding   predicate
-     *
-     *   Rope         0001       xxx1
-     *   Linear       -          xxx0
-     *   Dependent    0010       0010
+     *   Rope         0000       0000
+     *   Linear       -         !0000
+     *   HasBase      -          xxx1
+     *   Dependent    0001       0001
      *   Flat         -          isLinear && !isDependent
-     *   Extensible   0100       0100
-     *   Fixed        0110       isFlat && !isExtensible
-     *   Inline       0110       isFixed && (u1.chars == inlineStorage || isShort)
-     *   Short        0110       header in FINALIZE_SHORT_STRING arena
-     *   External     0110       header in FINALIZE_EXTERNAL_STRING arena
-     *   Undepended   1010       1010
-     *   Atom         1000       x000
+     *   Undepended   0011       0011
+     *   Extensible   0010       0010
+     *   Fixed        0100       isFlat && !isExtensible
+     *   Inline       0100       isFixed && (u1.chars == inlineStorage || isShort || isInt32)
+     *   Short        0100       header in FINALIZE_SHORT_STRING arena
+     *   External     0100       header in FINALIZE_EXTERNAL_STRING arena
+     *   Int32        0110       x110 (NYI, Bug 654190)
+     *   Atom         1000       1xxx
      *   InlineAtom   1000       1000 && is Inline
      *   ShortAtom    1000       1000 && is Short
+     *   Int32Atom    1110       1110 (NYI, Bug 654190)
+     *
+     *  "HasBase" here refers to the two string types that have a 'base' field:
+     *  JSDependentString and JSUndependedString.
+     *  A JSUndependedString is a JSDependentString which has been 'fixed' (by ensureFixed)
+     *  to be null-terminated.  In such cases, the string must keep marking its base since
+     *  there may be any number of *other* JSDependentStrings transitively depending on it.
+     *
      */
 
     static const size_t LENGTH_SHIFT          = 4;
     static const size_t FLAGS_MASK            = JS_BITMASK(LENGTH_SHIFT);
 
-    static const size_t ROPE_BIT              = JS_BIT(0);
+    static const size_t ROPE_FLAGS            = 0;
+    static const size_t DEPENDENT_FLAGS       = JS_BIT(0);
+    static const size_t UNDEPENDED_FLAGS      = JS_BIT(0) | JS_BIT(1);
+    static const size_t EXTENSIBLE_FLAGS      = JS_BIT(1);
+    static const size_t FIXED_FLAGS           = JS_BIT(2);
 
-    static const size_t DEPENDENT_FLAGS       = JS_BIT(1);
-    static const size_t EXTENSIBLE_FLAGS      = JS_BIT(2);
-    static const size_t FIXED_FLAGS           = JS_BIT(1) | JS_BIT(2);
-    static const size_t UNDEPENDED_FLAGS      = JS_BIT(1) | JS_BIT(3);
+    static const size_t INT32_MASK            = JS_BITMASK(3);
+    static const size_t INT32_FLAGS           = JS_BIT(1) | JS_BIT(2);
 
-    static const size_t ATOM_MASK             = JS_BITMASK(3);
-    static const size_t ATOM_FLAGS            = JS_BIT(3);
+    static const size_t HAS_BASE_BIT          = JS_BIT(0);
+    static const size_t ATOM_BIT              = JS_BIT(3);
 
     static const size_t MAX_LENGTH            = JS_BIT(32 - LENGTH_SHIFT) - 1;
 
     size_t buildLengthAndFlags(size_t length, size_t flags) {
         JS_ASSERT(length <= MAX_LENGTH);
         JS_ASSERT(flags <= FLAGS_MASK);
         return (length << LENGTH_SHIFT) | flags;
     }
@@ -263,30 +272,28 @@ class JSString : public js::gc::Cell
     inline JSLinearString *ensureLinear(JSContext *cx);
     inline JSFlatString *ensureFlat(JSContext *cx);
     inline JSFixedString *ensureFixed(JSContext *cx);
 
     /* Type query and debug-checked casts */
 
     JS_ALWAYS_INLINE
     bool isRope() const {
-        bool rope = d.lengthAndFlags & ROPE_BIT;
-        JS_ASSERT_IF(rope, (d.lengthAndFlags & FLAGS_MASK) == ROPE_BIT);
-        return rope;
+        return (d.lengthAndFlags & FLAGS_MASK) == ROPE_FLAGS;
     }
 
     JS_ALWAYS_INLINE
     JSRope &asRope() const {
         JS_ASSERT(isRope());
         return *(JSRope *)this;
     }
 
     JS_ALWAYS_INLINE
     bool isLinear() const {
-        return !(d.lengthAndFlags & ROPE_BIT);
+        return !isRope();
     }
 
     JS_ALWAYS_INLINE
     JSLinearString &asLinear() const {
         JS_ASSERT(JSString::isLinear());
         return *(JSLinearString *)this;
     }
 
@@ -344,30 +351,30 @@ class JSString : public js::gc::Cell
 
     JS_ALWAYS_INLINE
     bool isUndepended() const {
         return (d.lengthAndFlags & FLAGS_MASK) == UNDEPENDED_FLAGS;
     }
 
     JS_ALWAYS_INLINE
     bool isAtom() const {
-        return !(d.lengthAndFlags & ATOM_MASK);
+        return (d.lengthAndFlags & ATOM_BIT);
     }
 
     JS_ALWAYS_INLINE
     JSAtom &asAtom() const {
         JS_ASSERT(isAtom());
         return *(JSAtom *)this;
     }
 
     /* Only called by the GC for dependent or undepended strings. */
 
     inline bool hasBase() const {
-        JS_STATIC_ASSERT((DEPENDENT_FLAGS | JS_BIT(3)) == UNDEPENDED_FLAGS);
-        return (d.lengthAndFlags & JS_BITMASK(3)) == DEPENDENT_FLAGS;
+        JS_STATIC_ASSERT((DEPENDENT_FLAGS | JS_BIT(1)) == UNDEPENDED_FLAGS);
+        return (d.lengthAndFlags & HAS_BASE_BIT);
     }
 
     inline JSLinearString *base() const;
 
     inline void markBase(JSTracer *trc);
 
     /* Only called by the GC for strings with the FINALIZE_STRING kind. */
 
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -120,13 +120,13 @@ random-if(bug685516) == background-size-
 fails == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html
 
 # -moz-default-background-color and -moz-default-color (bug 591341)
 == background-moz-default-background-color.html background-moz-default-background-color-ref.html
 
 random-if(bug685516) == fixed-bg-with-transform-outside-viewport-1.html fixed-bg-with-transform-outside-viewport-ref.html
 
 random-if(bug685516) HTTP == root-background-1.html root-background-ref.html
-HTTP != root-background-1.html about:blank
+random-if(bug685516) HTTP != root-background-1.html about:blank
 
 random-if(bug685516) == really-big-background.html really-big-background-ref.html
 
 random-if(bug685516) == background-repeat-1-ref.html background-repeat-1.html
--- a/layout/reftests/mathml/mtable-align-whitespace-ref.html
+++ b/layout/reftests/mathml/mtable-align-whitespace-ref.html
@@ -4,17 +4,17 @@
 </head>
 
 <body>
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -27,17 +27,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -50,17 +50,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -73,17 +73,17 @@
     </math> 
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -95,17 +95,17 @@
       
     </math>   
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
    <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -118,17 +118,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -140,17 +140,17 @@
       
     </math>  
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -163,17 +163,17 @@
     </math> 
  
 <br><br><br>
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -187,17 +187,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -211,17 +211,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -235,17 +235,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -259,17 +259,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -283,17 +283,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -306,17 +306,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center -3">  
+        <mtable frame="solid" align="center -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
--- a/layout/reftests/mathml/mtable-align-whitespace.html
+++ b/layout/reftests/mathml/mtable-align-whitespace.html
@@ -4,17 +4,17 @@
 </head>
 
 <body>
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center-3">  
+        <mtable frame="solid" align="center-3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -27,17 +27,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center-3  ">  
+        <mtable frame="solid" align="center-3  ">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -50,17 +50,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="  center-3">  
+        <mtable frame="solid" align="  center-3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -73,17 +73,17 @@
     </math> 
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="  center-3  ">  
+        <mtable frame="solid" align="  center-3  ">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -95,17 +95,17 @@
       
     </math>   
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
    <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="  center   -3">  
+        <mtable frame="solid" align="  center   -3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -118,17 +118,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center  -3  ">  
+        <mtable frame="solid" align="center  -3  ">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -140,17 +140,17 @@
       
     </math>  
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="  center  -3  ">  
+        <mtable frame="solid" align="  center  -3  ">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -164,17 +164,17 @@
 
 
 <br><br><br>
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="&#xA&#xD;&#x20;center&#xA&#xD;&#x20;-3&#xA&#xD;&#x9;">  
+        <mtable frame="solid" align="&#xA&#xD;&#x20;center&#xA&#xD;&#x20;-3&#xA&#xD;&#x9;">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -188,17 +188,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="&#xA&#xD;&#x9;&#x20;center&#xA&#xD;&#x9;&#x20;-3&#xA&#xD;&#x9;&#x20;">  
+        <mtable frame="solid" align="&#xA&#xD;&#x9;&#x20;center&#xA&#xD;&#x9;&#x20;-3&#xA&#xD;&#x9;&#x20;">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -211,17 +211,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center-3&#xA&#xD;&#x9;&#x20;">  
+        <mtable frame="solid" align="center-3&#xA&#xD;&#x9;&#x20;">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -234,17 +234,17 @@
     </math>  
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="&#xA&#xD;&#x9;&#x20;center-3">  
+        <mtable frame="solid" align="&#xA&#xD;&#x9;&#x20;center-3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -258,17 +258,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="&#xA&#xD;&#x9;&#x20;center-3&#xA&#xD;&#x9;&#x20;">  
+        <mtable frame="solid" align="&#xA&#xD;&#x9;&#x20;center-3&#xA&#xD;&#x9;&#x20;">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -282,17 +282,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="&#xA&#xD;&#x9;&#x20;center&#xA&#xD;&#x9;&#x20;-3">  
+        <mtable frame="solid" align="&#xA&#xD;&#x9;&#x20;center&#xA&#xD;&#x9;&#x20;-3">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
@@ -306,17 +306,17 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
 
     <math>  
           
         <mi>X</mi>  
         <mo>=</mo>  
-        <mtable frame="solid" rowlines="solid" align="center&#xA&#xD;&#x9;&#x20;-3&#xA&#xD;&#x9;&#x20;">  
+        <mtable frame="solid" align="center&#xA&#xD;&#x9;&#x20;-3&#xA&#xD;&#x9;&#x20;">  
             <mtr>  
                  <mtd><mi>A</mi></mtd>  
                  <mtd><mi>B</mi></mtd>  
             </mtr>  
             <mtr>  
                  <mtd><mi>C</mi></mtd>  
                  <mtd><mi>D</mi></mtd>  
             </mtr>  
--- a/toolkit/components/osfile/osfile_shared_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_shared_allthreads.jsm
@@ -1,20 +1,16 @@
 /* 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/. */
 
 {
   if (typeof Components != "undefined") {
     var EXPORTED_SYMBOLS = ["OS"];
-    Components.utils.import("resource://gre/modules/ctypes.jsm");
-    Components.classes["@mozilla.org/net/osfileconstantsservice;1"].
-      getService(Components.interfaces.nsIOSFileConstantsService).init();
   }
-
   (function(exports) {
      "use strict";
      /*
       * This block defines |OS.Shared.Type|. However, |OS| can exist already
       * (in particular, if this code is executed in a worker thread, it is
       * defined).
       */
      if (!exports.OS) {
@@ -22,16 +18,25 @@
      }
      if (!exports.OS.Shared) {
        exports.OS.Shared = {};
      }
      if (exports.OS.Shared.Type) {
        return; // Avoid double-initialization
      }
 
+     // Import components after having initialized |exports.OS|, to ensure
+     // that everybody uses the same definition of |OS|.
+     if (typeof Components != "undefined") {
+       Components.utils.import("resource://gre/modules/ctypes.jsm");
+       Components.classes["@mozilla.org/net/osfileconstantsservice;1"].
+         getService(Components.interfaces.nsIOSFileConstantsService).init();
+     }
+
+
      let LOG;
      if (typeof console != "undefined" && console.log) {
        LOG = console.log.bind(console, "OS");
      } else {
        LOG = function() {
          let text = "OS";
          for (let i = 0; i < arguments.length; ++i) {
            text += (" " + arguments[i]);
@@ -418,18 +423,25 @@
       * @param {bool} signed |true| if this is a type of signed integers,
       * |false| otherwise.
       *
       * @constructor
       */
      function IntType(name, implementation, signed) {
        Type.call(this, name, implementation);
        this.importFromC = projector(implementation, signed);
+       this.project = this.importFromC;
      };
      IntType.prototype = Object.create(Type.prototype);
+     IntType.prototype.toMsg = function toMsg(value) {
+       if (typeof value == "number") {
+         return value;
+       }
+       return this.project(value);
+     };
 
      /**
       * A C char (one byte)
       */
      Types.char =
        new Type("char",
                 ctypes.char);
 
--- a/toolkit/components/osfile/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/osfile_unix_front.jsm
@@ -628,16 +628,36 @@
        get path() {
          delete this.path;
          let path = OS.Unix.Path.join(this._parent, this.name);
          Object.defineProperty(this, "path", {value: path});
          return path;
        }
      };
 
+     /**
+      * Return a version of an instance of
+      * File.DirectoryIterator.Entry that can be sent from a worker
+      * thread to the main thread. Note that deserialization is
+      * asymmetric and returns an object with a different
+      * implementation.
+      */
+     File.DirectoryIterator.Entry.toMsg = function toMsg(value) {
+       if (!value instanceof File.DirectoryIterator.Entry) {
+         throw new TypeError("parameter of " +
+           "File.DirectoryIterator.Entry.toMsg must be a " +
+           "File.DirectoryIterator.Entry");
+       }
+       let serialized = {};
+       for (let key in File.DirectoryIterator.Entry.prototype) {
+         serialized[key] = value[key];
+       }
+       return serialized;
+     };
+
      let gStatData = new OS.Shared.Type.stat.implementation();
      let gStatDataPtr = gStatData.address();
      let MODE_MASK = 4095 /*= 07777*/;
      File.Info = function Info(stat) {
        this._st_mode = stat.st_mode;
        this._st_uid = stat.st_uid;
        this._st_gid = stat.st_gid;
        this._st_atime = stat.st_atime;
@@ -662,26 +682,17 @@
         * The size of the file, in bytes.
         *
         * Note that the result may be |NaN| if the size of the file cannot be
         * represented in JavaScript.
         *
         * @type {number}
         */
        get size() {
-         delete this.size;
-         let size;
-         try {
-           size = OS.Shared.projectValue(this._st_size);
-         } catch(x) {
-           LOG("get size error", x);
-           size = NaN;
-         }
-         Object.defineProperty(this, "size", { value: size });
-         return size;
+         return exports.OS.Shared.Type.size_t.importFromC(this._st_size);
        },
        /**
         * The date of creation of this file
         *
         * @type {Date}
         */
        get creationDate() {
          delete this.creationDate;
@@ -711,33 +722,49 @@
          let date = new Date(this._st_mtime * 1000);
          Object.defineProperty(this, "lastModificationDate", {value: date});
          return date;
        },
        /**
         * Return the Unix owner of this file.
         */
        get unixOwner() {
-         return this._st_uid;
+         return exports.OS.Shared.Type.uid_t.importFromC(this._st_uid);
        },
        /**
         * Return the Unix group of this file.
         */
        get unixGroup() {
-         return this._st_gid;
+         return exports.OS.Shared.Type.gid_t.importFromC(this._st_gid);
        },
        /**
         * Return the Unix mode of this file.
         */
        get unixMode() {
-         return this._st_mode & MODE_MASK;
+         return exports.OS.Shared.Type.mode_t.importFromC(this._st_mode & MODE_MASK);
        }
      };
 
      /**
+      * Return a version of an instance of File.Info that can be sent
+      * from a worker thread to the main thread. Note that deserialization
+      * is asymmetric and returns an object with a different implementation.
+      */
+     File.Info.toMsg = function toMsg(stat) {
+       if (!stat instanceof File.Info) {
+         throw new TypeError("parameter of File.Info.toMsg must be a File.Info");
+       }
+       let serialized = {};
+       for (let key in File.Info.prototype) {
+         serialized[key] = stat[key];
+       }
+       return serialized;
+     };
+
+     /**
       * Fetch the information on a file.
       *
       * @param {string} path The full name of the file to open.
       * @param {*=} options Additional options. In this implementation:
       *
       * - {bool} unixNoFollowingLinks If set and |true|, if |path|
       * represents a symbolic link, the call will return the information
       * of the link itself, rather than that of the target file.
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -576,55 +576,76 @@
         */
        get name() {
          return this._name;
        },
        /**
         * The creation time of this file.
         * @type {Date}
         */
-       get winCreationTime() {
+       get winCreationDate() {
          let date = FILETIME_to_Date(this._ftCreationTime);
-         delete this.winCreationTime;
-         Object.defineProperty(this, "winCreationTime", {value: date});
+         delete this.winCreationDate;
+         Object.defineProperty(this, "winCreationDate", {value: date});
          return date;
        },
        /**
         * The last modification time of this file.
         * @type {Date}
         */
-       get winLastWriteTime() {
+       get winLastWriteDate() {
          let date = FILETIME_to_Date(this._ftLastWriteTime);
-         delete this.winLastWriteTime;
-         Object.defineProperty(this, "winLastWriteTime", {value: date});
+         delete this.winLastWriteDate;
+         Object.defineProperty(this, "winLastWriteDate", {value: date});
          return date;
        },
        /**
         * The last access time of this file.
         * @type {Date}
         */
-       get winLastAccessTime() {
+       get winLastAccessDate() {
          let date = FILETIME_to_Date(this._ftLastAccessTime);
-         delete this.winLastAccessTime;
-         Object.defineProperty(this, "winLastAccessTime", {value: date});
+         delete this.winLastAccessDate;
+         Object.defineProperty(this, "winLastAccessDate", {value: date});
          return date;
        },
        /**
         * The full path to the entry.
         * @type {string}
         */
        get path() {
          delete this.path;
          let path = OS.Win.Path.join(this._parent, this.name);
          Object.defineProperty(this, "path", {value: path});
          return path;
        }
      };
 
      /**
+      * Return a version of an instance of
+      * File.DirectoryIterator.Entry that can be sent from a worker
+      * thread to the main thread. Note that deserialization is
+      * asymmetric and returns an object with a different
+      * implementation.
+      */
+     File.DirectoryIterator.Entry.toMsg = function toMsg(value) {
+       if (!value instanceof File.DirectoryIterator.Entry) {
+         throw new TypeError("parameter of " +
+           "File.DirectoryIterator.Entry.toMsg must be a " +
+           "File.DirectoryIterator.Entry");
+       }
+       let serialized = {};
+       for (let key in File.DirectoryIterator.Entry.prototype) {
+         serialized[key] = value[key];
+       }
+       return serialized;
+     };
+
+
+     /**
       * Information on a file.
       *
       * To obtain the latest information on a file, use |File.stat|
       * (for an unopened file) or |File.prototype.stat| (for an
       * already opened file).
       *
       * @constructor
       */
@@ -653,24 +674,18 @@
         * The size of the file, in bytes.
         *
         * Note that the result may be |NaN| if the size of the file cannot be
         * represented in JavaScript.
         *
         * @type {number}
         */
        get size() {
-         try {
-           return OS.Shared.projectValue(
-             ctypes.uint64_t("" +
-             this._nFileSizeHigh +
-             this._nFileSizeLow));
-         } catch (x) {
-           return NaN;
-         }
+         let value = ctypes.UInt64.join(this._nFileSizeHigh, this._nFileSizeLow);
+         return exports.OS.Shared.Type.uint64_t.importFromC(value);
        },
        /**
         * The date of creation of this file
         *
         * @type {Date}
         */
        get creationDate() {
          delete this.creationDate;
@@ -704,16 +719,33 @@
          delete this.lastModification;
          let date = FILETIME_to_Date(this._ftLastWriteTime);
          Object.defineProperty(this, "lastModificationDate", { value: date });
          return date;
        }
      };
 
      /**
+      * Return a version of an instance of File.Info that can be sent
+      * from a worker thread to the main thread. Note that deserialization
+      * is asymmetric and returns an object with a different implementation.
+      */
+     File.Info.toMsg = function toMsg(stat) {
+       if (!stat instanceof File.Info) {
+         throw new TypeError("parameter of File.Info.toMsg must be a File.Info");
+       }
+       let serialized = {};
+       for (let key in File.Info.prototype) {
+         serialized[key] = stat[key];
+       }
+       return serialized;
+     };
+
+
+     /**
       * Fetch the information on a file.
       *
       * Performance note: if you have opened the file already,
       * method |File.prototype.stat| is generally much faster
       * than method |File.stat|.
       *
       * Platform-specific note: under Windows, if the file is
       * already opened without sharing of the read capability,
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -322,25 +322,25 @@ function test_iter_dir()
     }
     if (file) {
       file.close();
     }
     ok(success, "test_iter_dir: Entry " + entry.path + " exists");
 
     if (OS.Win) {
       let year = new Date().getFullYear();
-      let creation = entry.winCreationTime;
+      let creation = entry.winCreationDate;
       ok(creation, "test_iter_dir: Windows creation date exists: " + creation);
       ok(creation.getFullYear() >= year -  1 && creation.getFullYear() <= year, "test_iter_dir: consistent creation date");
 
-      let lastWrite = entry.winLastWriteTime;
+      let lastWrite = entry.winLastWriteDate;
       ok(lastWrite, "test_iter_dir: Windows lastWrite date exists: " + lastWrite);
       ok(lastWrite.getFullYear() >= year - 1 && lastWrite.getFullYear() <= year, "test_iter_dir: consistent lastWrite date");
 
-      let lastAccess = entry.winLastAccessTime;
+      let lastAccess = entry.winLastAccessDate;
       ok(lastAccess, "test_iter_dir: Windows lastAccess date exists: " + lastAccess);
       ok(lastAccess.getFullYear() >= year - 1 && lastAccess.getFullYear() <= year, "test_iter_dir: consistent lastAccess date");
     }
 
   }
   ok(encountered_tmp_file, "test_iter_dir: We have found the temporary file");
 
   ok(true, "test_iter_dir: Cleaning up");
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -5829,23 +5829,34 @@ function AddonWrapper(aAddon) {
 
     return null;
   }, this);
 
   this.__defineGetter__("optionsType", function AddonWrapper_optionsTypeGetter() {
     if (!this.isActive)
       return null;
 
-    if (aAddon.optionsType)
-      return aAddon.optionsType;
-
-    if (this.hasResource("options.xul"))
+    let hasOptionsXUL = this.hasResource("options.xul");
+    let hasOptionsURL = !!this.optionsURL;
+
+    if (aAddon.optionsType) {
+      switch (parseInt(aAddon.optionsType, 10)) {
+      case AddonManager.OPTIONS_TYPE_DIALOG:
+      case AddonManager.OPTIONS_TYPE_TAB:
+        return hasOptionsURL ? aAddon.optionsType : null;
+      case AddonManager.OPTIONS_TYPE_INLINE:
+        return (hasOptionsXUL || hasOptionsURL) ? aAddon.optionsType : null;
+      }
+      return null;
+    }
+
+    if (hasOptionsXUL)
       return AddonManager.OPTIONS_TYPE_INLINE;
 
-    if (this.optionsURL)
+    if (hasOptionsURL)
       return AddonManager.OPTIONS_TYPE_DIALOG;
 
     return null;
   }, this);
 
   this.__defineGetter__("iconURL", function AddonWrapper_iconURLGetter() {
     return this.icons[32];
   }, this);
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2657,22 +2657,20 @@ var gDetailView = {
       if (first && window.getComputedStyle(gridRows[i], null).getPropertyValue("display") != "none") {
         gridRows[i].setAttribute("first-row", true);
         first = false;
       } else {
         gridRows[i].removeAttribute("first-row");
       }
     }
 
-    this.fillSettingsRows(aScrollToPreferences);
-
-    this.updateState();
-
-    gViewController.updateCommands();
-    gViewController.notifyViewChanged();
+    this.fillSettingsRows(aScrollToPreferences, (function updateView_fillSettingsRows() {
+      this.updateState();
+      gViewController.notifyViewChanged();
+    }).bind(this));
   },
 
   show: function gDetailView_show(aAddonId, aRequest) {
     let index = aAddonId.indexOf("/preferences");
     let scrollToPreferences = false;
     if (index >= 0) {
       aAddonId = aAddonId.substring(0, index);
       scrollToPreferences = true;
@@ -2814,20 +2812,23 @@ var gDetailView = {
 
   emptySettingsRows: function gDetailView_emptySettingsRows() {
     var lastRow = document.getElementById("detail-downloads");
     var rows = lastRow.parentNode;
     while (lastRow.nextSibling)
       rows.removeChild(rows.lastChild);
   },
 
-  fillSettingsRows: function gDetailView_fillSettingsRows(aScrollToPreferences) {
+  fillSettingsRows: function gDetailView_fillSettingsRows(aScrollToPreferences, aCallback) {
     this.emptySettingsRows();
-    if (this._addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE)
+    if (this._addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE) {
+      if (aCallback)
+        aCallback();
       return;
+    }
 
     // This function removes and returns the text content of aNode without
     // removing any child elements. Removing the text nodes ensures any XBL
     // bindings apply properly.
     function stripTextNodes(aNode) {
       var text = '';
       for (var i = 0; i < aNode.childNodes.length; i++) {
         if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
@@ -2837,59 +2838,75 @@ var gDetailView = {
           text += stripTextNodes(aNode.childNodes[i]);
         }
       }
       return text;
     }
 
     var rows = document.getElementById("detail-downloads").parentNode;
 
-    var xhr = new XMLHttpRequest();
-    xhr.open("GET", this._addon.optionsURL, false);
-    xhr.send();
-
-    var xml = xhr.responseXML;
-    var settings = xml.querySelectorAll(":root > setting");
-
-    var firstSetting = null;
-    for (let setting of settings) {
-
-      var desc = stripTextNodes(setting).trim();
-      if (!setting.hasAttribute("desc"))
-        setting.setAttribute("desc", desc);
-
-      var type = setting.getAttribute("type");
-      if (type == "file" || type == "directory")
-        setting.setAttribute("fullpath", "true");
-
-      rows.appendChild(setting);
-      var visible = window.getComputedStyle(setting, null).getPropertyValue("display") != "none";
-      if (!firstSetting && visible) {
-        setting.setAttribute("first-row", true);
-        firstSetting = setting;
-      }
-    }
-
-    // Ensure the page has loaded and force the XBL bindings to be synchronously applied,
-    // then notify observers.
-    if (gViewController.viewPort.selectedPanel.hasAttribute("loading")) {
-      gDetailView.node.addEventListener("ViewChanged", function viewChangedEventListener() {
-        gDetailView.node.removeEventListener("ViewChanged", viewChangedEventListener, false);
-        if (firstSetting)
-          firstSetting.clientTop;
-        Services.obs.notifyObservers(document, "addon-options-displayed", gDetailView._addon.id);
-        if (aScrollToPreferences)
-          gDetailView.scrollToPreferencesRows();
-      }, false);
-    } else {
-      if (firstSetting)
-        firstSetting.clientTop;
-      Services.obs.notifyObservers(document, "addon-options-displayed", this._addon.id);
-      if (aScrollToPreferences)
-        gDetailView.scrollToPreferencesRows();
+    try {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", this._addon.optionsURL, true);
+      xhr.responseType = "xml";
+      xhr.onload = (function fillSettingsRows_onload() {
+        var xml = xhr.responseXML;
+        var settings = xml.querySelectorAll(":root > setting");
+
+        var firstSetting = null;
+        for (let setting of settings) {
+
+          var desc = stripTextNodes(setting).trim();
+          if (!setting.hasAttribute("desc"))
+            setting.setAttribute("desc", desc);
+
+          var type = setting.getAttribute("type");
+          if (type == "file" || type == "directory")
+            setting.setAttribute("fullpath", "true");
+
+          rows.appendChild(setting);
+          var visible = window.getComputedStyle(setting, null).getPropertyValue("display") != "none";
+          if (!firstSetting && visible) {
+            setting.setAttribute("first-row", true);
+            firstSetting = setting;
+          }
+        }
+
+        // Ensure the page has loaded and force the XBL bindings to be synchronously applied,
+        // then notify observers.
+        if (gViewController.viewPort.selectedPanel.hasAttribute("loading")) {
+          gDetailView.node.addEventListener("ViewChanged", function viewChangedEventListener() {
+            gDetailView.node.removeEventListener("ViewChanged", viewChangedEventListener, false);
+            if (firstSetting)
+              firstSetting.clientTop;
+            Services.obs.notifyObservers(document, "addon-options-displayed", gDetailView._addon.id);
+            if (aScrollToPreferences)
+              gDetailView.scrollToPreferencesRows();
+          }, false);
+        } else {
+          if (firstSetting)
+            firstSetting.clientTop;
+          Services.obs.notifyObservers(document, "addon-options-displayed", this._addon.id);
+          if (aScrollToPreferences)
+            gDetailView.scrollToPreferencesRows();
+        }
+        if (aCallback)
+          aCallback();
+      }).bind(this);
+      xhr.onerror = function fillSettingsRows_onerror(aEvent) {
+        Cu.reportError("Error " + aEvent.target.status +
+                       " occurred while receiving " + this._addon.optionsURL);
+        if (aCallback)
+          aCallback();
+      };
+      xhr.send();
+    } catch(e) {
+      Cu.reportError(e);
+      if (aCallback)
+        aCallback();
     }
   },
 
   scrollToPreferencesRows: function gDetailView_scrollToPreferencesRows() {
     // We find this row, rather than remembering it from above,
     // in case it has been changed by the observers.
     let firstRow = gDetailView.node.querySelector('setting[first-row="true"]');
     if (firstRow) {
--- a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
@@ -10,16 +10,17 @@ var gProvider;
 
 const SETTINGS_ROWS = 8;
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
 MockFilePicker.init();
 
 var observer = {
   lastDisplayed: null,
+  callback: null,
   checkDisplayed: function(aExpected) {
     is(this.lastDisplayed, aExpected, "'addon-options-displayed' notification should have fired");
     this.lastDisplayed = null;
   },
   checkNotDisplayed: function() {
     is(this.lastDisplayed, null, "'addon-options-displayed' notification should not have fired");
   },
   lastHidden: null,
@@ -36,16 +37,22 @@ var observer = {
       // Test if the binding has applied before the observers are notified. We test the second setting here,
       // because the code operates on the first setting and we want to check it applies to all.
       var setting = aSubject.querySelector("rows > setting[first-row] ~ setting");
       var input = gManagerWindow.document.getAnonymousElementByAttribute(setting, "class", "preferences-title");
       isnot(input, null, "XBL binding should be applied");
 
       // Add some extra height to the scrolling pane to ensure that it needs to scroll when appropriate.
       gManagerWindow.document.getElementById("detail-controls").style.marginBottom = "1000px";
+
+      if (this.callback) {
+        var tempCallback = this.callback;
+        this.callback = null;
+        tempCallback();
+      }
     } else if (aTopic == "addon-options-hidden") {
       this.lastHidden = aData;
     }
   }
 };
 
 function installAddon(aCallback) {
   AddonManager.getInstallForURL(TESTROOT + "addons/browser_inlinesettings1.xpi",
@@ -499,27 +506,29 @@ add_test(function() {
 
       button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
       EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
 
       wait_for_view_load(gManagerWindow, function() {
         var grid = gManagerWindow.document.getElementById("detail-grid");
         var settings = grid.querySelectorAll("rows > setting");
         is(settings.length, 0, "Grid should not have settings children");
-        
+
         // enable
         var button = gManagerWindow.document.getElementById("detail-enable-btn");
         EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
 
-        observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
+        observer.callback = function() {
+          observer.checkDisplayed("inlinesettings1@tests.mozilla.org");
 
-        settings = grid.querySelectorAll("rows > setting");
-        is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
+          settings = grid.querySelectorAll("rows > setting");
+          is(settings.length, SETTINGS_ROWS, "Grid should have settings children");
 
-        gCategoryUtilities.openType("extension", run_next_test);
+          gCategoryUtilities.openType("extension", run_next_test);
+        };
       });
     });
   });
 });
 
 
 // Addon with options.xul that requires a restart to disable,
 // disabling and enabling should not hide and show settings UI.
--- a/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js
@@ -278,16 +278,63 @@ function run_test() {
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Test Addon 21"
   }, profileDir);
 
+  writeInstallRDFForExtension({
+    id: "addon22@tests.mozilla.org",
+    version: "1.0",
+    optionsType: "2",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 22"
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "addon23@tests.mozilla.org",
+    version: "1.0",
+    optionsType: "2",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 23"
+  }, profileDir, null, "options.xul");
+
+  writeInstallRDFForExtension({
+    id: "addon24@tests.mozilla.org",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 24"
+  }, profileDir, null, "options.xul");
+
+  writeInstallRDFForExtension({
+    id: "addon25@tests.mozilla.org",
+    version: "1.0",
+    optionsType: "3",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 25"
+  }, profileDir);
+
   do_test_pending();
   startupManager();
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
@@ -300,20 +347,24 @@ function run_test() {
                                "addon13@tests.mozilla.org",
                                "addon14@tests.mozilla.org",
                                "addon15@tests.mozilla.org",
                                "addon16@tests.mozilla.org",
                                "addon17@tests.mozilla.org",
                                "addon18@tests.mozilla.org",
                                "addon19@tests.mozilla.org",
                                "addon20@tests.mozilla.org",
-                               "addon21@tests.mozilla.org"],
+                               "addon21@tests.mozilla.org",
+                               "addon22@tests.mozilla.org",
+                               "addon23@tests.mozilla.org",
+                               "addon24@tests.mozilla.org",
+                               "addon25@tests.mozilla.org"],
                                function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
                                          a11, a12, a13, a14, a15, a16, a17, a18, a19, a20,
-                                         a21]) {
+                                         a21, a22, a23, a24, a25]) {
 
     do_check_neq(a1, null);
     do_check_eq(a1.id, "addon1@tests.mozilla.org");
     do_check_eq(a1.type, "extension");
     do_check_eq(a1.version, "1.0");
     do_check_eq(a1.optionsURL, "chrome://test/content/options.xul");
     do_check_eq(a1.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
     do_check_eq(a1.aboutURL, "chrome://test/content/about.xul");
@@ -468,11 +519,27 @@ function run_test() {
     do_check_neq(a21, null);
     do_check_true(a21.isActive);
     do_check_false(a21.userDisabled);
     do_check_false(a21.appDisabled);
     do_check_true(a21.isCompatible);
     do_check_eq(a21.optionsURL, "chrome://test/content/options.xul");
     do_check_eq(a21.optionsType, AddonManager.OPTIONS_TYPE_TAB);
 
+    do_check_neq(a22, null);
+    do_check_eq(a22.optionsType, null);
+    do_check_eq(a22.optionsURL, null);
+
+    do_check_neq(a23, null);
+    do_check_eq(a23.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+    do_check_neq(a23.optionsURL, null);
+
+    do_check_neq(a24, null);
+    do_check_eq(a24.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+    do_check_neq(a24.optionsURL, null);
+
+    do_check_neq(a25, null);
+    do_check_eq(a25.optionsType, null);
+    do_check_eq(a25.optionsURL, null);
+
     do_test_finished();
   });
 }
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -928,17 +928,17 @@ ifdef INSTALL_SDK # Here comes the hard 
 	(cd $(DIST)/idl && tar $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DESTDIR)$(idldir) && tar -xf -)
 # SDK directory is the libs + a bunch of symlinks
 	$(NSINSTALL) -D $(DESTDIR)$(sdkdir)/sdk/lib
 	$(NSINSTALL) -D $(DESTDIR)$(sdkdir)/sdk/bin
 	if test -f $(DIST)/include/xpcom-config.h; then \
 	  $(SYSINSTALL) $(IFLAGS1) $(DIST)/include/xpcom-config.h $(DESTDIR)$(sdkdir); \
 	fi
-	find $(DIST)/sdk -name "*.pyc" | xargs rm
+	find $(DIST)/sdk -name "*.pyc" | xargs rm -f
 	(cd $(DIST)/sdk/lib && tar $(TAR_CREATE_FLAGS) - .) | (cd $(DESTDIR)$(sdkdir)/sdk/lib && tar -xf -)
 	(cd $(DIST)/sdk/bin && tar $(TAR_CREATE_FLAGS) - .) | (cd $(DESTDIR)$(sdkdir)/sdk/bin && tar -xf -)
 	$(RM) -f $(DESTDIR)$(sdkdir)/lib $(DESTDIR)$(sdkdir)/bin $(DESTDIR)$(sdkdir)/include $(DESTDIR)$(sdkdir)/include $(DESTDIR)$(sdkdir)/sdk/idl $(DESTDIR)$(sdkdir)/idl
 	ln -s $(sdkdir)/sdk/lib $(DESTDIR)$(sdkdir)/lib
 	ln -s $(installdir) $(DESTDIR)$(sdkdir)/bin
 	ln -s $(includedir) $(DESTDIR)$(sdkdir)/include
 	ln -s $(idldir) $(DESTDIR)$(sdkdir)/idl
 endif # INSTALL_SDK
@@ -949,17 +949,17 @@ make-sdk:
 	$(RM) -rf $(DIST)/$(MOZ_APP_NAME)-sdk
 	$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/bin
 	(cd $(DIST)/sdk-stage && tar $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DIST)/$(MOZ_APP_NAME)-sdk/bin && tar -xf -)
 	$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/host/bin
 	(cd $(DIST)/host/bin && tar $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DIST)/$(MOZ_APP_NAME)-sdk/host/bin && tar -xf -)
 	$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/sdk
-	find $(DIST)/sdk -name "*.pyc" | xargs rm
+	find $(DIST)/sdk -name "*.pyc" | xargs rm -f
 	(cd $(DIST)/sdk && tar $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DIST)/$(MOZ_APP_NAME)-sdk/sdk && tar -xf -)
 	$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/include
 	(cd $(DIST)/include && tar $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DIST)/$(MOZ_APP_NAME)-sdk/include && tar -xf -)
 	$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/idl
 	(cd $(DIST)/idl && tar $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DIST)/$(MOZ_APP_NAME)-sdk/idl && tar -xf -)
--- a/toolkit/webapps/WebappsInstaller.jsm
+++ b/toolkit/webapps/WebappsInstaller.jsm
@@ -796,20 +796,23 @@ LinuxNativeApp.prototype = {
 
   _createConfigFiles: function() {
     // ${InstallDir}/webapp.json
     writeToFile(this.configJson, JSON.stringify(this.webappJson), function() {});
 
     let factory = Cc["@mozilla.org/xpcom/ini-processor-factory;1"]
                     .getService(Ci.nsIINIParserFactory);
 
+    let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
+
     // ${InstallDir}/webapp.ini
     let writer = factory.createINIParser(this.webappINI).QueryInterface(Ci.nsIINIParserWriter);
     writer.setString("Webapp", "Name", this.appName);
     writer.setString("Webapp", "Profile", this.uniqueName);
+    writer.setString("Webapp", "UninstallMsg", browserBundle.formatStringFromName("webapps.uninstall.notification", [this.appName], 1));
     writer.setString("WebappRT", "InstallDir", this.runtimeFolder.path);
     writer.writeFile();
 
     // $XDG_DATA_HOME/applications/owa-<webappuniquename>.desktop
     this.desktopINI.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0755);
 
     writer = factory.createINIParser(this.desktopINI).QueryInterface(Ci.nsIINIParserWriter);
     writer.setString("Desktop Entry", "Name", this.appName);
@@ -818,16 +821,20 @@ LinuxNativeApp.prototype = {
     writer.setString("Desktop Entry", "Icon", this.iconFile.path);
     writer.setString("Desktop Entry", "Type", "Application");
     writer.setString("Desktop Entry", "Terminal", "false");
 
     let categories = this._translateCategories();
     if (categories)
       writer.setString("Desktop Entry", "Categories", categories);
 
+    writer.setString("Desktop Entry", "Actions", "Uninstall;");
+    writer.setString("Desktop Action Uninstall", "Name", browserBundle.GetStringFromName("webapps.uninstall.label"));
+    writer.setString("Desktop Action Uninstall", "Exec", this.webapprt.path + " -remove");
+
     writer.writeFile();
   },
 
   /**
    * This variable specifies if the icon retrieval process should
    * use a temporary file in the system or a binary stream. This
    * is accessed by a common function in WebappsIconHelpers.js and
    * is different for each platform.
--- a/webapprt/gtk2/webapprt.cpp
+++ b/webapprt/gtk2/webapprt.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // GTK headers
 #include <gtk/gtk.h>
 
 // Linux headers
 #include <fcntl.h>
 #include <unistd.h>
+#include <dlfcn.h>
 
 // Mozilla headers
 #include "nsIFile.h"
 #include "nsINIParser.h"
 #include "nsXPCOMGlue.h"
 #include "nsXPCOMPrivate.h"              // for MAXPATHLEN and XPCOM_DLL
 #include "nsXULAppAPI.h"
 #include "BinaryPath.h"
@@ -219,17 +220,17 @@ void CopyAndRelaunch(const char* firefox
     return;
   }
 
   execv(curExePath, *pargv);
 
   ErrorDialog("Couldn't execute the new webapprt-stub executable");
 }
 
-void RemoveApplication(const char* curExeDir, const char* profile)  {
+void RemoveApplication(nsINIParser& parser, const char* curExeDir, const char* profile)  {
   if (!isProfileOverridden) {
     // Remove the desktop entry file.
     char desktopEntryFilePath[MAXPATHLEN];
 
     char* dataDir = getenv("XDG_DATA_HOME");
 
     if (dataDir && *dataDir) {
       snprintf(desktopEntryFilePath, MAXPATHLEN, "%s/applications/owa-%s.desktop", dataDir, profile);
@@ -252,16 +253,57 @@ void RemoveApplication(const char* curEx
 
   char webAppJsonPath[MAXPATHLEN];
   snprintf(webAppJsonPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_JSON);
   unlink(webAppJsonPath);
 
   char iconPath[MAXPATHLEN];
   snprintf(iconPath, MAXPATHLEN, "%s/icon.png", curExeDir);
   unlink(iconPath);
+
+  char appName[MAXPATHLEN];
+  if (NS_FAILED(parser.GetString("Webapp", "Name", appName, MAXPATHLEN))) {
+    strcpy(appName, profile);
+  }
+
+  char uninstallMsg[MAXPATHLEN];
+  if (NS_SUCCEEDED(parser.GetString("Webapp", "UninstallMsg", uninstallMsg, MAXPATHLEN))) {
+    /**
+     * The only difference between libnotify.so.4 and libnotify.so.1 for these symbols
+     * is that notify_notification_new takes three arguments in libnotify.so.4 and
+     * four in libnotify.so.1.
+     * Passing the fourth argument as NULL is binary compatible.
+     */
+    typedef void  (*notify_init_t)(char*);
+    typedef void* (*notify_notification_new_t)(char*, char*, char*, char*);
+    typedef void  (*notify_notification_show_t)(void*, char*);
+
+    void *handle = dlopen("libnotify.so.4", RTLD_LAZY);
+    if (!handle) {
+      handle = dlopen("libnotify.so.1", RTLD_LAZY);
+      if (!handle)
+        return;
+    }
+
+    notify_init_t nn_init = (notify_init_t)dlsym(handle, "notify_init");
+    notify_notification_new_t nn_new = (notify_notification_new_t)dlsym(handle, "notify_notification_new");
+    notify_notification_show_t nn_show = (notify_notification_show_t)dlsym(handle, "notify_notification_show");
+    if (!nn_init || !nn_new || !nn_show) {
+      dlclose(handle);
+      return;
+    }
+
+    nn_init(appName);
+
+    void* n = nn_new(uninstallMsg, NULL, "dialog-information", NULL);
+
+    nn_show(n, NULL);
+
+    dlclose(handle);
+  }
 }
 
 int main(int argc, char *argv[])
 {
   pargc = &argc;
   pargv = &argv;
 
   // Get current executable path
@@ -314,17 +356,17 @@ int main(int argc, char *argv[])
 
   // Get profile dir from webapp.ini
   if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) {
     ErrorDialog("Couldn't retrieve profile from web app INI file");
     return 255;
   }
 
   if (removeApp) {
-    RemoveApplication(curExeDir, profile);
+    RemoveApplication(parser, curExeDir, profile);
     return 0;
   }
 
   // Get the location of Firefox from our webapp.ini
   if (NS_FAILED(parser.GetString("WebappRT", "InstallDir", firefoxDir, MAXPATHLEN))) {
     ErrorDialog("Couldn't find your Firefox install directory.");
     return 255;
   }
--- a/widget/gonk/OrientationObserver.cpp
+++ b/widget/gonk/OrientationObserver.cpp
@@ -178,16 +178,28 @@ OrientationObserver::OrientationObserver
 
 OrientationObserver::~OrientationObserver()
 {
   if (mAutoOrientationEnabled) {
     DisableAutoOrientation();
   }
 }
 
+/* static */ void
+OrientationObserver::ShutDown()
+{
+  if (!sOrientationSensorObserver) {
+    return;
+  }
+
+  if (sOrientationSensorObserver->mAutoOrientationEnabled) {
+    sOrientationSensorObserver->DisableAutoOrientation();
+  }
+}
+
 void
 OrientationObserver::Notify(const hal::SensorData& aSensorData)
 {
   // Sensor will call us on the main thread.
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSensorData.sensor() == hal::SensorType::SENSOR_ORIENTATION);
 
   InfallibleTArray<float> values = aSensorData.values();
--- a/widget/gonk/OrientationObserver.h
+++ b/widget/gonk/OrientationObserver.h
@@ -32,16 +32,20 @@ using mozilla::hal::ISensorObserver;
 using mozilla::hal::SensorData;
 using mozilla::dom::ScreenOrientation;
 
 class OrientationObserver : public ISensorObserver {
 public:
   OrientationObserver();
   ~OrientationObserver();
 
+  // Call DisableAutoOrientation on the existing OrientatiOnobserver singleton,
+  // if it exists.  If no OrientationObserver exists, do nothing.
+  static void ShutDown();
+
   // Notification from sensor.
   void Notify(const SensorData& aSensorData);
 
   // Methods to enable/disable automatic orientation.
   void EnableAutoOrientation();
   void DisableAutoOrientation();
 
   // Methods called by methods in hal_impl namespace.
--- a/widget/gonk/nsAppShell.cpp
+++ b/widget/gonk/nsAppShell.cpp
@@ -524,17 +524,17 @@ nsAppShell::Init()
     // Delay initializing input devices until the screen has been
     // initialized (and we know the resolution).
     return rv;
 }
 
 NS_IMETHODIMP
 nsAppShell::Exit()
 {
-  OrientationObserver::GetInstance()->DisableAutoOrientation();
+  OrientationObserver::ShutDown();
   return nsBaseAppShell::Exit();
 }
 
 void
 nsAppShell::InitInputDevices()
 {
     mEventHub = new EventHub();
     mReaderPolicy = new GeckoInputReaderPolicy();