Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 12 Sep 2015 08:56:45 -0700
changeset 294712 cffdd225055ecd92fc9005a5851103bd844e1dd6
parent 294578 6ca9518d7cf9426d3e5e0d55f3aebe97f310185a (current diff)
parent 294711 3f4912c6cf479fba90859f6cb9be99c8fd702819 (diff)
child 294749 454b06628c64dd754a2d754380ef3d88d62f7fc7
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -1,18 +1,18 @@
 [
 {
-"clang_version": "r183744"
+"clang_version": "r241406"
 }, 
 {
-"size": 70350828, 
-"digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6", 
+"size": 100307285, 
+"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", 
 "algorithm": "sha512", 
-"unpack": true,
-"filename": "clang.tar.bz2"
+"filename": "clang.tar.bz2",
+"unpack": true
 },
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz",
 "unpack": true
 },
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1931,17 +1931,17 @@ nsDOMWindowUtils::SendQueryContentEvent(
       break;
     case QUERY_TEXT_CONTENT:
       message = eQueryTextContent;
       break;
     case QUERY_CARET_RECT:
       message = eQueryCaretRect;
       break;
     case QUERY_TEXT_RECT:
-      message = NS_QUERY_TEXT_RECT;
+      message = eQueryTextRect;
       break;
     case QUERY_EDITOR_RECT:
       message = eQueryEditorRect;
       break;
     case QUERY_CHARACTER_AT_POINT:
       message = eQueryCharacterAtPoint;
       break;
     default:
@@ -1986,17 +1986,17 @@ nsDOMWindowUtils::SendQueryContentEvent(
 
   switch (message) {
     case eQueryTextContent:
       queryEvent.InitForQueryTextContent(aOffset, aLength, useNativeLineBreak);
       break;
     case eQueryCaretRect:
       queryEvent.InitForQueryCaretRect(aOffset, useNativeLineBreak);
       break;
-    case NS_QUERY_TEXT_RECT:
+    case eQueryTextRect:
       queryEvent.InitForQueryTextRect(aOffset, aLength, useNativeLineBreak);
       break;
     default:
       queryEvent.mUseNativeLineBreak = useNativeLineBreak;
       break;
   }
 
   nsEventStatus status;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3881,17 +3881,17 @@ PLDHashOperator RequestDiscardEnumerator
   aKey->RequestDiscard();
   return PL_DHASH_NEXT;
 }
 
 void
 nsDocument::DeleteShell()
 {
   mExternalResourceMap.HideViewers();
-  if (IsEventHandlingEnabled()) {
+  if (IsEventHandlingEnabled() && !AnimationsPaused()) {
     RevokeAnimationFrameNotifications();
   }
   if (nsPresContext* presContext = mPresShell->GetPresContext()) {
     presContext->RefreshDriver()->CancelPendingEvents(this);
   }
 
   // When our shell goes away, request that all our images be immediately
   // discarded, so we don't carry around decoded image data for a document we
@@ -4627,17 +4627,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
                nsSMILTimeContainer::PAUSE_BEGIN),
              "Clearing window pointer while animations are unpaused");
 
   if (mScriptGlobalObject && !aScriptGlobalObject) {
     // We're detaching from the window.  We need to grab a pointer to
     // our layout history state now.
     mLayoutHistoryState = GetLayoutHistoryState();
 
-    if (mPresShell && !EventHandlingSuppressed()) {
+    if (mPresShell && !EventHandlingSuppressed() && !AnimationsPaused()) {
       RevokeAnimationFrameNotifications();
     }
 
     // Also make sure to remove our onload blocker now if we haven't done it yet
     if (mOnloadBlockCount != 0) {
       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
       if (loadGroup) {
         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
@@ -10313,17 +10313,18 @@ nsIDocument::ScheduleFrameRequestCallbac
     return NS_ERROR_NOT_AVAILABLE;
   }
   int32_t newHandle = ++mFrameRequestCallbackCounter;
 
   bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty();
   DebugOnly<FrameRequest*> request =
     mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
   NS_ASSERTION(request, "This is supposed to be infallible!");
-  if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
+  if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled() &&
+      !AnimationsPaused()) {
     mPresShell->GetPresContext()->RefreshDriver()->
       ScheduleFrameRequestCallbacks(this);
   }
 
   *aHandle = newHandle;
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1444,18 +1444,18 @@ nsGlobalWindow::CleanUp()
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
       os->RemoveObserver(mObserver, "dom-storage2-changed");
     }
 
 #ifdef MOZ_B2G
-    DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
-    DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
+    DisableNetworkEvent(eNetworkUpload);
+    DisableNetworkEvent(eNetworkDownload);
 #endif // MOZ_B2G
 
     if (mIdleService) {
       mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
     }
 
     Preferences::RemoveObserver(mObserver, "intl.accept_languages");
 
@@ -14596,23 +14596,23 @@ nsGlobalWindow::EnableNetworkEvent(Event
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (!os) {
     NS_ERROR("ObserverService should be available!");
     return;
   }
 
   switch (aEventMessage) {
-    case NS_NETWORK_UPLOAD_EVENT:
+    case eNetworkUpload:
       if (!mNetworkUploadObserverEnabled) {
         mNetworkUploadObserverEnabled = true;
         os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC, false);
       }
       break;
-    case NS_NETWORK_DOWNLOAD_EVENT:
+    case eNetworkDownload:
       if (!mNetworkDownloadObserverEnabled) {
         mNetworkDownloadObserverEnabled = true;
         os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, false);
       }
       break;
     default:
       break;
   }
@@ -14624,23 +14624,23 @@ nsGlobalWindow::DisableNetworkEvent(Even
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (!os) {
     return;
   }
 
   switch (aEventMessage) {
-    case NS_NETWORK_UPLOAD_EVENT:
+    case eNetworkUpload:
       if (mNetworkUploadObserverEnabled) {
         mNetworkUploadObserverEnabled = false;
         os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC);
       }
       break;
-    case NS_NETWORK_DOWNLOAD_EVENT:
+    case eNetworkDownload:
       if (mNetworkDownloadObserverEnabled) {
         mNetworkDownloadObserverEnabled = false;
         os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC);
       }
       break;
     default:
       break;
   }
--- a/dom/base/nsQueryContentEventResult.cpp
+++ b/dom/base/nsQueryContentEventResult.cpp
@@ -53,17 +53,17 @@ nsQueryContentEventResult::GetTentativeC
   }
   *aOffset = mTentativeCaretOffset;
   return NS_OK;
 }
 
 static bool IsRectEnabled(EventMessage aEventMessage)
 {
   return aEventMessage == eQueryCaretRect ||
-         aEventMessage == NS_QUERY_TEXT_RECT ||
+         aEventMessage == eQueryTextRect ||
          aEventMessage == eQueryEditorRect ||
          aEventMessage == eQueryCharacterAtPoint;
 }
 
 NS_IMETHODIMP
 nsQueryContentEventResult::GetReversed(bool *aReversed)
 {
   NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -8,16 +8,17 @@
 
 #ifndef XP_WIN
 #include <unistd.h>
 #endif
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/BlobSet.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsIDOMDocument.h"
 #include "mozilla/dom/ProgressEvent.h"
 #include "nsIJARChannel.h"
@@ -1612,41 +1613,20 @@ nsXMLHttpRequest::Open(const nsACString&
     GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
   }
 
   Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
                         async ? 0 : 1);
 
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
-  // Disallow HTTP/1.1 TRACE method (see bug 302489)
-  // and MS IIS equivalent TRACK (see bug 381264)
-  // and CONNECT
-  if (inMethod.LowerCaseEqualsLiteral("trace") ||
-      inMethod.LowerCaseEqualsLiteral("connect") ||
-      inMethod.LowerCaseEqualsLiteral("track")) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
   nsAutoCString method;
-  // GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
-  if (inMethod.LowerCaseEqualsLiteral("get")) {
-    method.AssignLiteral("GET");
-  } else if (inMethod.LowerCaseEqualsLiteral("post")) {
-    method.AssignLiteral("POST");
-  } else if (inMethod.LowerCaseEqualsLiteral("delete")) {
-    method.AssignLiteral("DELETE");
-  } else if (inMethod.LowerCaseEqualsLiteral("head")) {
-    method.AssignLiteral("HEAD");
-  } else if (inMethod.LowerCaseEqualsLiteral("options")) {
-    method.AssignLiteral("OPTIONS");
-  } else if (inMethod.LowerCaseEqualsLiteral("put")) {
-    method.AssignLiteral("PUT");
-  } else {
-    method = inMethod; // other methods are not normalized
+  nsresult rv = FetchUtil::GetValidRequestMethod(inMethod, method);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   // sync request is not allowed using withCredential or responseType
   // in window context
   if (!async && HasOrHasHadOwner() &&
       (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
        mTimeoutMilliseconds ||
        mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
@@ -1657,17 +1637,16 @@ nsXMLHttpRequest::Open(const nsACString&
       LogMessage("TimeoutSyncXHRWarning", GetOwner());
     }
     if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     }
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
-  nsresult rv;
   nsCOMPtr<nsIURI> uri;
 
   if (mState & (XML_HTTP_REQUEST_OPENED |
                 XML_HTTP_REQUEST_HEADERS_RECEIVED |
                 XML_HTTP_REQUEST_LOADING |
                 XML_HTTP_REQUEST_SENT)) {
     // IE aborts as well
     Abort();
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -797,17 +797,17 @@ ContentEventHandler::HandleQueryContentE
 {
   switch (aEvent->mMessage) {
     case eQuerySelectedText:
       return OnQuerySelectedText(aEvent);
     case eQueryTextContent:
       return OnQueryTextContent(aEvent);
     case eQueryCaretRect:
       return OnQueryCaretRect(aEvent);
-    case NS_QUERY_TEXT_RECT:
+    case eQueryTextRect:
       return OnQueryTextRect(aEvent);
     case eQueryEditorRect:
       return OnQueryEditorRect(aEvent);
     case eQueryContentState:
       return OnQueryContentState(aEvent);
     case eQuerySelectionAsTransferable:
       return OnQuerySelectionAsTransferable(aEvent);
     case eQueryCharacterAtPoint:
@@ -1325,17 +1325,17 @@ ContentEventHandler::OnQueryCharacterAtP
     textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
   NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
   uint32_t offset;
   rv = GetFlatTextOffsetOfRange(mRootContent, contentOffsets.content,
                                 contentOffsets.offset, &offset,
                                 GetLineBreakType(aEvent));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aEvent->widget);
+  WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget);
   textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak);
   rv = OnQueryTextRect(&textRect);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
 
   // currently, we don't need to get the actual text.
   aEvent->mReply.mOffset = offset;
   aEvent->mReply.mRect = textRect.mReply.mRect;
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -43,17 +43,17 @@ public:
   nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
 
   // eQuerySelectedText event handler
   nsresult OnQuerySelectedText(WidgetQueryContentEvent* aEvent);
   // eQueryTextContent event handler
   nsresult OnQueryTextContent(WidgetQueryContentEvent* aEvent);
   // eQueryCaretRect event handler
   nsresult OnQueryCaretRect(WidgetQueryContentEvent* aEvent);
-  // NS_QUERY_TEXT_RECT event handler
+  // eQueryTextRect event handler
   nsresult OnQueryTextRect(WidgetQueryContentEvent* aEvent);
   // eQueryEditorRect event handler
   nsresult OnQueryEditorRect(WidgetQueryContentEvent* aEvent);
   // eQueryContentState event handler
   nsresult OnQueryContentState(WidgetQueryContentEvent* aEvent);
   // eQuerySelectionAsTransferable event handler
   nsresult OnQuerySelectionAsTransferable(WidgetQueryContentEvent* aEvent);
   // eQueryCharacterAtPoint event handler
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -313,38 +313,38 @@ EventListenerManager::AddEventListenerIn
       }
       // If aEventMessage is eLegacySubtreeModified, we need to listen all
       // mutations. nsContentUtils::HasMutationListeners relies on this.
       window->SetMutationListeners(
         (aEventMessage == eLegacySubtreeModified) ?
           kAllMutationBits : MutationBitForEventType(aEventMessage));
     }
   } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
-    EnableDevice(NS_DEVICE_ORIENTATION);
+    EnableDevice(eDeviceOrientation);
   } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
-    EnableDevice(NS_DEVICE_PROXIMITY);
+    EnableDevice(eDeviceProximity);
   } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
-    EnableDevice(NS_DEVICE_LIGHT);
+    EnableDevice(eDeviceLight);
   } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
-    EnableDevice(NS_DEVICE_MOTION);
+    EnableDevice(eDeviceMotion);
 #ifdef MOZ_B2G
   } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
       window->EnableTimeChangeNotifications();
     }
   } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
-      window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
+      window->EnableNetworkEvent(eNetworkUpload);
     }
   } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
-      window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
+      window->EnableNetworkEvent(eNetworkDownload);
     }
 #endif // MOZ_B2G
   } else if (aTypeAtom == nsGkAtoms::ontouchstart ||
              aTypeAtom == nsGkAtoms::ontouchend ||
              aTypeAtom == nsGkAtoms::ontouchmove ||
              aTypeAtom == nsGkAtoms::ontouchcancel) {
     mMayHaveTouchEventListener = true;
     nsPIDOMWindow* window = GetInnerWindowForTarget();
@@ -378,18 +378,18 @@ EventListenerManager::AddEventListenerIn
       nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
       NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
                        "Please do not use mouseenter/leave events in chrome. "
                        "They are slower than mouseover/out!");
 #endif
       window->SetHasMouseEnterLeaveEventListeners();
     }
 #ifdef MOZ_GAMEPAD
-  } else if (aEventMessage >= NS_GAMEPAD_START &&
-             aEventMessage <= NS_GAMEPAD_END) {
+  } else if (aEventMessage >= eGamepadEventFirst &&
+             aEventMessage <= eGamepadEventLast) {
     nsPIDOMWindow* window = GetInnerWindowForTarget();
     if (window) {
       window->SetHasGamepadEventListener();
     }
 #endif
   } else if (aTypeAtom == nsGkAtoms::onkeydown ||
              aTypeAtom == nsGkAtoms::onkeypress ||
              aTypeAtom == nsGkAtoms::onkeyup) {
@@ -414,48 +414,48 @@ EventListenerManager::AddEventListenerIn
                                                               aTypeAtom);
   }
 }
 
 bool
 EventListenerManager::IsDeviceType(EventMessage aEventMessage)
 {
   switch (aEventMessage) {
-    case NS_DEVICE_ORIENTATION:
-    case NS_DEVICE_MOTION:
-    case NS_DEVICE_LIGHT:
-    case NS_DEVICE_PROXIMITY:
-    case NS_USER_PROXIMITY:
+    case eDeviceOrientation:
+    case eDeviceMotion:
+    case eDeviceLight:
+    case eDeviceProximity:
+    case eUserProximity:
       return true;
     default:
       break;
   }
   return false;
 }
 
 void
 EventListenerManager::EnableDevice(EventMessage aEventMessage)
 {
   nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   if (!window) {
     return;
   }
 
   switch (aEventMessage) {
-    case NS_DEVICE_ORIENTATION:
+    case eDeviceOrientation:
       window->EnableDeviceSensor(SENSOR_ORIENTATION);
       break;
-    case NS_DEVICE_PROXIMITY:
-    case NS_USER_PROXIMITY:
+    case eDeviceProximity:
+    case eUserProximity:
       window->EnableDeviceSensor(SENSOR_PROXIMITY);
       break;
-    case NS_DEVICE_LIGHT:
+    case eDeviceLight:
       window->EnableDeviceSensor(SENSOR_LIGHT);
       break;
-    case NS_DEVICE_MOTION:
+    case eDeviceMotion:
       window->EnableDeviceSensor(SENSOR_ACCELERATION);
       window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
       window->EnableDeviceSensor(SENSOR_GYROSCOPE);
       break;
     default:
       NS_WARNING("Enabling an unknown device sensor.");
       break;
   }
@@ -465,29 +465,29 @@ void
 EventListenerManager::DisableDevice(EventMessage aEventMessage)
 {
   nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   if (!window) {
     return;
   }
 
   switch (aEventMessage) {
-    case NS_DEVICE_ORIENTATION:
+    case eDeviceOrientation:
       window->DisableDeviceSensor(SENSOR_ORIENTATION);
       break;
-    case NS_DEVICE_MOTION:
+    case eDeviceMotion:
       window->DisableDeviceSensor(SENSOR_ACCELERATION);
       window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
       window->DisableDeviceSensor(SENSOR_GYROSCOPE);
       break;
-    case NS_DEVICE_PROXIMITY:
-    case NS_USER_PROXIMITY:
+    case eDeviceProximity:
+    case eUserProximity:
       window->DisableDeviceSensor(SENSOR_PROXIMITY);
       break;
-    case NS_DEVICE_LIGHT:
+    case eDeviceLight:
       window->DisableDeviceSensor(SENSOR_LIGHT);
       break;
     default:
       NS_WARNING("Disabling an unknown device sensor.");
       break;
   }
 }
 
@@ -505,19 +505,19 @@ EventListenerManager::RemoveEventListene
   }
 
   Listener* listener;
 
   uint32_t count = mListeners.Length();
   uint32_t typeCount = 0;
   bool deviceType = IsDeviceType(aEventMessage);
 #ifdef MOZ_B2G
-  bool timeChangeEvent = (aEventMessage == NS_MOZ_TIME_CHANGE_EVENT);
-  bool networkEvent = (aEventMessage == NS_NETWORK_UPLOAD_EVENT ||
-                       aEventMessage == NS_NETWORK_DOWNLOAD_EVENT);
+  bool timeChangeEvent = (aEventMessage == eTimeChange);
+  bool networkEvent = (aEventMessage == eNetworkUpload ||
+                       aEventMessage == eNetworkDownload);
 #endif // MOZ_B2G
 
   for (uint32_t i = 0; i < count; ++i) {
     listener = &mListeners.ElementAt(i);
     if (EVENT_TYPE_EQUALS(listener, aEventMessage, aUserType, aTypeString,
                           aAllEvents)) {
       ++typeCount;
       if (listener->mListener == aListenerHolder &&
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -298,21 +298,21 @@ EVENT(mozfullscreenchange,
       eFullscreenChange,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(mozfullscreenerror,
       eFullscreenError,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(mozpointerlockchange,
-      NS_POINTERLOCKCHANGE,
+      ePointerLockChange,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(mozpointerlockerror,
-      NS_POINTERLOCKERROR,
+      ePointerLockError,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(pointerdown,
       ePointerDown,
       EventNameType_All,
       ePointerEventClass)
 EVENT(pointermove,
       ePointerMove,
@@ -464,21 +464,21 @@ FORWARDED_EVENT(resize,
                 EventNameType_All,
                 eBasicEventClass)
 FORWARDED_EVENT(scroll,
                 eScroll,
                 (EventNameType_HTMLXUL | EventNameType_SVGSVG),
                 eBasicEventClass)
 
 WINDOW_EVENT(afterprint,
-             NS_AFTERPRINT,
+             eAfterPrint,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 WINDOW_EVENT(beforeprint,
-             NS_BEFOREPRINT,
+             eBeforePrint,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 BEFOREUNLOAD_EVENT(beforeunload,
                    eBeforeUnload,
                    EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
                    eBasicEventClass)
 WINDOW_EVENT(hashchange,
              eHashChange,
@@ -523,47 +523,47 @@ WINDOW_EVENT(popstate,
 // WINDOW_EVENT(undo)
 WINDOW_EVENT(unload,
              eUnload,
              (EventNameType_XUL | EventNameType_SVGSVG |
               EventNameType_HTMLBodyOrFramesetOnly),
              eBasicEventClass)
 
 WINDOW_ONLY_EVENT(devicemotion,
-                  NS_DEVICE_MOTION,
+                  eDeviceMotion,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(deviceorientation,
-                  NS_DEVICE_ORIENTATION,
+                  eDeviceOrientation,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(deviceproximity,
-                  NS_DEVICE_PROXIMITY,
+                  eDeviceProximity,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(userproximity,
-                  NS_USER_PROXIMITY,
+                  eUserProximity,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(devicelight,
-                  NS_DEVICE_LIGHT,
+                  eDeviceLight,
                   EventNameType_None,
                   eBasicEventClass)
 
 #ifdef MOZ_B2G
 WINDOW_ONLY_EVENT(moztimechange,
-                  NS_MOZ_TIME_CHANGE_EVENT,
+                  eTimeChange,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(moznetworkupload,
-                  NS_NETWORK_UPLOAD_EVENT,
+                  eNetworkUpload,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(moznetworkdownload,
-                  NS_NETWORK_DOWNLOAD_EVENT,
+                  eNetworkDownload,
                   EventNameType_None,
                   eBasicEventClass)
 #endif // MOZ_B2G
 
 TOUCH_EVENT(touchstart,
             NS_TOUCH_START,
             EventNameType_All,
             eTouchEventClass)
@@ -667,29 +667,29 @@ NON_IDL_EVENT(speakerforcedchange,
               eBasicEventClass)
 
 // Events that only have on* attributes on XUL elements
 
  // "text" event is legacy event for modifying composition string in nsEditor.
  // This shouldn't be used by web/xul apps.  "compositionupdate" should be
  // used instead.
 NON_IDL_EVENT(text,
-              NS_COMPOSITION_CHANGE,
+              eCompositionChange,
               EventNameType_XUL,
               eCompositionEventClass)
 NON_IDL_EVENT(compositionstart,
-              NS_COMPOSITION_START,
+              eCompositionStart,
               EventNameType_XUL,
               eCompositionEventClass)
 NON_IDL_EVENT(compositionupdate,
-              NS_COMPOSITION_UPDATE,
+              eCompositionUpdate,
               EventNameType_XUL,
               eCompositionEventClass)
 NON_IDL_EVENT(compositionend,
-              NS_COMPOSITION_END,
+              eCompositionEnd,
               EventNameType_XUL,
               eCompositionEventClass)
 NON_IDL_EVENT(command,
               eXULCommand,
               EventNameType_XUL,
               eInputEventClass)
 NON_IDL_EVENT(close,
               eWindowClose,
@@ -727,21 +727,21 @@ NON_IDL_EVENT(dragdrop,
               eLegacyDragDrop,
               EventNameType_XUL,
               eDragEventClass)
 NON_IDL_EVENT(draggesture,
               eLegacyDragGesture,
               EventNameType_XUL,
               eDragEventClass)
 NON_IDL_EVENT(overflow,
-              NS_SCROLLPORT_OVERFLOW,
+              eScrollPortOverflow,
               EventNameType_XUL,
               eBasicEventClass)
 NON_IDL_EVENT(underflow,
-              NS_SCROLLPORT_UNDERFLOW,
+              eScrollPortUnderflow,
               EventNameType_XUL,
               eBasicEventClass)
 
 // Various SVG events
 NON_IDL_EVENT(SVGLoad,
               eSVGLoad,
               EventNameType_None,
               eBasicEventClass)
@@ -769,76 +769,76 @@ NON_IDL_EVENT(SVGZoom,
 NON_IDL_EVENT(zoom,
               eSVGZoom,
               EventNameType_SVGSVG,
               eBasicEventClass)
 #endif
 // Only map the ID to the real event name when MESSAGE_TO_EVENT is defined.
 #ifndef MESSAGE_TO_EVENT
 NON_IDL_EVENT(begin,
-              NS_SMIL_BEGIN,
+              eSMILBeginEvent,
               EventNameType_SMIL,
               eBasicEventClass)
 #endif
 NON_IDL_EVENT(beginEvent,
-              NS_SMIL_BEGIN,
+              eSMILBeginEvent,
               EventNameType_None,
               eSMILTimeEventClass)
 // Only map the ID to the real event name when MESSAGE_TO_EVENT is defined.
 #ifndef MESSAGE_TO_EVENT
 NON_IDL_EVENT(end,
-              NS_SMIL_END,
+              eSMILEndEvent,
               EventNameType_SMIL,
               eBasicEventClass)
 #endif
 NON_IDL_EVENT(endEvent,
-              NS_SMIL_END,
+              eSMILEndEvent,
               EventNameType_None,
               eSMILTimeEventClass)
 // Only map the ID to the real event name when MESSAGE_TO_EVENT is defined.
 #ifndef MESSAGE_TO_EVENT
 NON_IDL_EVENT(repeat,
-              NS_SMIL_REPEAT,
+              eSMILRepeatEvent,
               EventNameType_SMIL,
               eBasicEventClass)
 #endif
 NON_IDL_EVENT(repeatEvent,
-              NS_SMIL_REPEAT,
+              eSMILRepeatEvent,
               EventNameType_None,
               eSMILTimeEventClass)
 
 NON_IDL_EVENT(MozAfterPaint,
               eAfterPaint,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(MozScrolledAreaChanged,
-              NS_SCROLLEDAREACHANGED,
+              eScrolledAreaChanged,
               EventNameType_None,
               eScrollAreaEventClass)
 
 #ifdef MOZ_GAMEPAD
 NON_IDL_EVENT(gamepadbuttondown,
-              NS_GAMEPAD_BUTTONDOWN,
+              eGamepadButtonDown,
               EventNameType_None,
               eBasicEventClass)
 NON_IDL_EVENT(gamepadbuttonup,
-              NS_GAMEPAD_BUTTONUP,
+              eGamepadButtonUp,
               EventNameType_None,
               eBasicEventClass)
 NON_IDL_EVENT(gamepadaxismove,
-              NS_GAMEPAD_AXISMOVE,
+              eGamepadAxisMove,
               EventNameType_None,
               eBasicEventClass)
 NON_IDL_EVENT(gamepadconnected,
-              NS_GAMEPAD_CONNECTED,
+              eGamepadConnected,
               EventNameType_None,
               eBasicEventClass)
 NON_IDL_EVENT(gamepaddisconnected,
-              NS_GAMEPAD_DISCONNECTED,
+              eGamepadDisconnected,
               EventNameType_None,
               eBasicEventClass)
 #endif
 
 // Simple gesture events
 NON_IDL_EVENT(MozSwipeGestureMayStart,
               NS_SIMPLE_GESTURE_SWIPE_MAY_START,
               EventNameType_None,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -754,17 +754,17 @@ EventStateManager::PreHandleEvent(nsPres
   case eContentCommandUndo:
   case eContentCommandRedo:
   case eContentCommandPasteTransferable:
     DoContentCommandEvent(aEvent->AsContentCommandEvent());
     break;
   case eContentCommandScroll:
     DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
     break;
-  case NS_COMPOSITION_START:
+  case eCompositionStart:
     if (aEvent->mFlags.mIsTrusted) {
       // If the event is trusted event, set the selected text to data of
       // composition event.
       WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
       WidgetQueryContentEvent selectedText(true, eQuerySelectedText,
                                            compositionEvent->widget);
       HandleQueryContentEvent(&selectedText);
       NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
@@ -779,17 +779,17 @@ EventStateManager::PreHandleEvent(nsPres
 
 void
 EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
 {
   switch (aEvent->mMessage) {
     case eQuerySelectedText:
     case eQueryTextContent:
     case eQueryCaretRect:
-    case NS_QUERY_TEXT_RECT:
+    case eQueryTextRect:
     case eQueryEditorRect:
       if (!IsTargetCrossProcess(aEvent)) {
         break;
       }
       // Will not be handled locally, remote the event
       GetCrossProcessTarget()->HandleQueryContentEvent(*aEvent);
       return;
     // Following events have not been supported in e10s mode yet.
@@ -2558,17 +2558,17 @@ EventStateManager::DoScrollText(nsIScrol
   WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent);
 }
 
 void
 EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
                                       nsIFrame* targetFrame)
 {
 
-  NS_ASSERTION(aEvent->mMessage == NS_GESTURENOTIFY_EVENT_START,
+  NS_ASSERTION(aEvent->mMessage == eGestureNotify,
                "DecideGestureEvent called with a non-gesture event");
 
   /* Check the ancestor tree to decide if any frame is willing* to receive
    * a MozPixelScroll event. If that's the case, the current touch gesture
    * will be used as a pan gesture; otherwise it will be a regular
    * mousedown/mousemove/click event.
    *
    * *willing: determine if it makes sense to pan the element using scroll events:
@@ -3128,17 +3128,17 @@ EventStateManager::PostHandleEvent(nsPre
             CancelApplyingUserPrefsFromOverflowDelta(wheelEvent);
           wheelEvent->mViewPortIsOverscrolled = true;
           break;
       }
       *aStatus = nsEventStatus_eConsumeNoDefault;
     }
     break;
 
-  case NS_GESTURENOTIFY_EVENT_START:
+  case eGestureNotify:
     {
       if (nsEventStatus_eConsumeNoDefault != *aStatus) {
         DecideGestureEvent(aEvent->AsGestureNotifyEvent(), mCurrentTarget);
       }
     }
     break;
 
   case eDragEnter:
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -52,18 +52,18 @@ ToChar(EventMessage aEventMessage)
 {
   switch (aEventMessage) {
     case eQuerySelectedText:
       return "eQuerySelectedText";
     case eQueryTextContent:
       return "eQueryTextContent";
     case eQueryCaretRect:
       return "eQueryCaretRect";
-    case NS_QUERY_TEXT_RECT:
-      return "NS_QUERY_TEXT_RECT";
+    case eQueryTextRect:
+      return "eQueryTextRect";
     case eQueryEditorRect:
       return "eQueryEditorRect";
     case eQueryContentState:
       return "eQueryContentState";
     case eQuerySelectionAsTransferable:
       return "eQuerySelectionAsTransferable";
     case eQueryCharacterAtPoint:
       return "eQueryCharacterAtPoint";
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -132,28 +132,28 @@ GetIMEStateSetOpenName(IMEState::Open aO
       return "illegal value";
   }
 }
 
 static const char*
 GetEventMessageName(EventMessage aMessage)
 {
   switch (aMessage) {
-    case NS_COMPOSITION_START:
-      return "NS_COMPOSITION_START";
-    case NS_COMPOSITION_END:
-      return "NS_COMPOSITION_END";
-    case NS_COMPOSITION_UPDATE:
-      return "NS_COMPOSITION_UPDATE";
-    case NS_COMPOSITION_CHANGE:
-      return "NS_COMPOSITION_CHANGE";
-    case NS_COMPOSITION_COMMIT_AS_IS:
-      return "NS_COMPOSITION_COMMIT_AS_IS";
-    case NS_COMPOSITION_COMMIT:
-      return "NS_COMPOSITION_COMMIT";
+    case eCompositionStart:
+      return "eCompositionStart";
+    case eCompositionEnd:
+      return "eCompositionEnd";
+    case eCompositionUpdate:
+      return "eCompositionUpdate";
+    case eCompositionChange:
+      return "eCompositionChange";
+    case eCompositionCommitAsIs:
+      return "eCompositionCommitAsIs";
+    case eCompositionCommit:
+      return "eCompositionCommit";
     case eSetSelection:
       return "eSetSelection";
     default:
       return "unacceptable event message";
   }
 }
 
 static const char*
@@ -1142,41 +1142,41 @@ IMEStateManager::DispatchCompositionEven
      GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
      GetBoolName(aIsSynthesized), tabParent.get()));
 
   if (!aCompositionEvent->mFlags.mIsTrusted ||
       aCompositionEvent->mFlags.mPropagationStopped) {
     return;
   }
 
-  MOZ_ASSERT(aCompositionEvent->mMessage != NS_COMPOSITION_UPDATE,
+  MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
              "compositionupdate event shouldn't be dispatched manually");
 
   EnsureTextCompositionArray();
 
   nsRefPtr<TextComposition> composition =
     sTextCompositions->GetCompositionFor(aCompositionEvent->widget);
   if (!composition) {
     // If synthesized event comes after delayed native composition events
     // for request of commit or cancel, we should ignore it.
     if (NS_WARN_IF(aIsSynthesized)) {
       return;
     }
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::DispatchCompositionEvent(), "
        "adding new TextComposition to the array"));
-    MOZ_ASSERT(aCompositionEvent->mMessage == NS_COMPOSITION_START);
+    MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
     composition =
       new TextComposition(aPresContext, aEventTargetNode, tabParent,
                           aCompositionEvent);
     sTextCompositions->AppendElement(composition);
   }
 #ifdef DEBUG
   else {
-    MOZ_ASSERT(aCompositionEvent->mMessage != NS_COMPOSITION_START);
+    MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
   }
 #endif // #ifdef DEBUG
 
   // Dispatch the event on composing target.
   composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack,
                                         aIsSynthesized);
 
   // WARNING: the |composition| might have been destroyed already.
@@ -1272,17 +1272,17 @@ IMEStateManager::OnCompositionEventDisca
      GetBoolName(aCompositionEvent->mFlags.mIsTrusted)));
 
   if (!aCompositionEvent->mFlags.mIsTrusted) {
     return;
   }
 
   // Ignore compositionstart for now because sTextCompositions may not have
   // been created yet.
-  if (aCompositionEvent->mMessage == NS_COMPOSITION_START) {
+  if (aCompositionEvent->mMessage == eCompositionStart) {
     return;
   }
 
   nsRefPtr<TextComposition> composition =
     sTextCompositions->GetCompositionFor(aCompositionEvent->widget);
   if (!composition) {
     // If the PresShell has been being destroyed during composition,
     // a TextComposition instance for the composition was already removed from
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -88,17 +88,17 @@ TextComposition::MaybeDispatchCompositio
 
   if (!IsValidStateForComposition(aCompositionEvent->widget)) {
     return false;
   }
 
   if (mLastData == aCompositionEvent->mData) {
     return true;
   }
-  CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_UPDATE);
+  CloneAndDispatchAs(aCompositionEvent, eCompositionUpdate);
   return IsValidStateForComposition(aCompositionEvent->widget);
 }
 
 BaseEventFlags
 TextComposition::CloneAndDispatchAs(
                    const WidgetCompositionEvent* aCompositionEvent,
                    EventMessage aMessage,
                    nsEventStatus* aStatus,
@@ -115,17 +115,17 @@ TextComposition::CloneAndDispatchAs(
   compositionEvent.timeStamp = aCompositionEvent->timeStamp;
   compositionEvent.mData = aCompositionEvent->mData;
   compositionEvent.mOriginalMessage = aCompositionEvent->mMessage;
   compositionEvent.mFlags.mIsSynthesizedForTests =
     aCompositionEvent->mFlags.mIsSynthesizedForTests;
 
   nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault;
   nsEventStatus* status = aStatus ? aStatus : &dummyStatus;
-  if (aMessage == NS_COMPOSITION_UPDATE) {
+  if (aMessage == eCompositionUpdate) {
     mLastData = compositionEvent.mData;
   }
   EventDispatcher::Dispatch(mNode, mPresContext,
                             &compositionEvent, nullptr, status, aCallBack);
   return compositionEvent.mFlags;
 }
 
 void
@@ -229,33 +229,33 @@ TextComposition::DispatchCompositionEven
     }
     return;
   }
 
   if (!mAllowControlCharacters) {
     RemoveControlCharactersFrom(aCompositionEvent->mData,
                                 aCompositionEvent->mRanges);
   }
-  if (aCompositionEvent->mMessage == NS_COMPOSITION_COMMIT_AS_IS) {
+  if (aCompositionEvent->mMessage == eCompositionCommitAsIs) {
     NS_ASSERTION(!aCompositionEvent->mRanges,
-                 "mRanges of NS_COMPOSITION_COMMIT_AS_IS should be null");
+                 "mRanges of eCompositionCommitAsIs should be null");
     aCompositionEvent->mRanges = nullptr;
     NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
-                 "mData of NS_COMPOSITION_COMMIT_AS_IS should be empty string");
+                 "mData of eCompositionCommitAsIs should be empty string");
     if (mLastData == IDEOGRAPHIC_SPACE) {
       // If the last data is an ideographic space (FullWidth space), it must be
       // a placeholder character of some Chinese IME.  So, committing with
       // this data must not be expected by users.  Let's use empty string.
       aCompositionEvent->mData.Truncate();
     } else {
       aCompositionEvent->mData = mLastData;
     }
-  } else if (aCompositionEvent->mMessage == NS_COMPOSITION_COMMIT) {
+  } else if (aCompositionEvent->mMessage == eCompositionCommit) {
     NS_ASSERTION(!aCompositionEvent->mRanges,
-                 "mRanges of NS_COMPOSITION_COMMIT should be null");
+                 "mRanges of eCompositionCommit should be null");
     aCompositionEvent->mRanges = nullptr;
   }
 
   if (!IsValidStateForComposition(aCompositionEvent->widget)) {
     *aStatus = nsEventStatus_eConsumeNoDefault;
     return;
   }
 
@@ -279,20 +279,20 @@ TextComposition::DispatchCompositionEven
   // They typically tell us an IDEOGRAPHIC SPACE or empty string as composition
   // string.  Therefore, we should hack it only when:
   // 1. committing string is empty string at requesting commit but the last
   //    data isn't IDEOGRAPHIC SPACE.
   // 2. non-empty string is committed at requesting cancel.
   if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) {
     nsString* committingData = nullptr;
     switch (aCompositionEvent->mMessage) {
-      case NS_COMPOSITION_END:
-      case NS_COMPOSITION_CHANGE:
-      case NS_COMPOSITION_COMMIT_AS_IS:
-      case NS_COMPOSITION_COMMIT:
+      case eCompositionEnd:
+      case eCompositionChange:
+      case eCompositionCommitAsIs:
+      case eCompositionCommit:
         committingData = &aCompositionEvent->mData;
         break;
       default:
         NS_WARNING("Unexpected event comes during committing or "
                    "canceling composition");
         break;
     }
     if (committingData) {
@@ -304,51 +304,51 @@ TextComposition::DispatchCompositionEven
       }
     }
   }
 
   bool dispatchEvent = true;
   bool dispatchDOMTextEvent = aCompositionEvent->CausesDOMTextEvent();
 
   // When mIsComposing is false but the committing string is different from
-  // the last data (E.g., previous NS_COMPOSITION_CHANGE event made the
+  // the last data (E.g., previous eCompositionChange event made the
   // composition string empty or didn't have clause information), we don't
   // need to dispatch redundant DOM text event.
   if (dispatchDOMTextEvent &&
-      aCompositionEvent->mMessage != NS_COMPOSITION_CHANGE &&
+      aCompositionEvent->mMessage != eCompositionChange &&
       !mIsComposing && mLastData == aCompositionEvent->mData) {
     dispatchEvent = dispatchDOMTextEvent = false;
   }
 
-  // widget may dispatch redundant NS_COMPOSITION_CHANGE event
+  // widget may dispatch redundant eCompositionChange event
   // which modifies neither composition string, clauses nor caret
   // position.  In such case, we shouldn't dispatch DOM events.
   if (dispatchDOMTextEvent &&
-      aCompositionEvent->mMessage == NS_COMPOSITION_CHANGE &&
+      aCompositionEvent->mMessage == eCompositionChange &&
       mLastData == aCompositionEvent->mData &&
       mRanges && aCompositionEvent->mRanges &&
       mRanges->Equals(*aCompositionEvent->mRanges)) {
     dispatchEvent = dispatchDOMTextEvent = false;
   }
 
   if (dispatchDOMTextEvent) {
     if (!MaybeDispatchCompositionUpdate(aCompositionEvent)) {
       return;
     }
   }
 
   if (dispatchEvent) {
     // If the composition event should cause a DOM text event, we should
-    // overwrite the event message as NS_COMPOSITION_CHANGE because due to
+    // overwrite the event message as eCompositionChange because due to
     // the limitation of mapping between event messages and DOM event types,
     // we cannot map multiple event messages to a DOM event type.
     if (dispatchDOMTextEvent &&
-        aCompositionEvent->mMessage != NS_COMPOSITION_CHANGE) {
+        aCompositionEvent->mMessage != eCompositionChange) {
       aCompositionEvent->mFlags =
-        CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_CHANGE,
+        CloneAndDispatchAs(aCompositionEvent, eCompositionChange,
                            aStatus, aCallBack);
     } else {
       EventDispatcher::Dispatch(mNode, mPresContext,
                                 aCompositionEvent, nullptr, aStatus, aCallBack);
     }
   } else {
     *aStatus = nsEventStatus_eConsumeNoDefault;
   }
@@ -361,18 +361,18 @@ TextComposition::DispatchCompositionEven
   // if no editor handles composition events.
   if (dispatchDOMTextEvent && !HasEditor()) {
     EditorWillHandleCompositionChangeEvent(aCompositionEvent);
     EditorDidHandleCompositionChangeEvent();
   }
 
   if (aCompositionEvent->CausesDOMCompositionEndEvent()) {
     // Dispatch a compositionend event if it's necessary.
-    if (aCompositionEvent->mMessage != NS_COMPOSITION_END) {
-      CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_END);
+    if (aCompositionEvent->mMessage != eCompositionEnd) {
+      CloneAndDispatchAs(aCompositionEvent, eCompositionEnd);
     }
     MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?");
     MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
   }
 
   // Notify composition update to widget if possible
   NotityUpdateComposition(aCompositionEvent);
 }
@@ -407,17 +407,17 @@ TextComposition::NotityUpdateComposition
 {
   MOZ_RELEASE_ASSERT(!mTabParent);
 
   nsEventStatus status;
 
   // When compositon start, notify the rect of first offset character.
   // When not compositon start, notify the rect of selected composition
   // string if compositionchange event.
-  if (aCompositionEvent->mMessage == NS_COMPOSITION_START) {
+  if (aCompositionEvent->mMessage == eCompositionStart) {
     nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
     // Update composition start offset
     WidgetQueryContentEvent selectedTextEvent(true, eQuerySelectedText, widget);
     widget->DispatchEvent(&selectedTextEvent, status);
     if (selectedTextEvent.mSucceeded) {
       mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
     } else {
       // Unknown offset
@@ -485,20 +485,20 @@ TextComposition::RequestToCommit(nsIWidg
   // If the request is performed synchronously, this must be already destroyed.
   if (Destroyed()) {
     return NS_OK;
   }
 
   // Otherwise, synthesize the commit in content.
   nsAutoString data(aDiscard ? EmptyString() : lastData);
   if (data == mLastData) {
-    DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT_AS_IS, EmptyString(),
+    DispatchCompositionEventRunnable(eCompositionCommitAsIs, EmptyString(),
                                      true);
   } else {
-    DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT, data, true);
+    DispatchCompositionEventRunnable(eCompositionCommit, data, true);
   }
   return NS_OK;
 }
 
 nsresult
 TextComposition::NotifyIME(IMEMessage aMessage)
 {
   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
@@ -606,35 +606,35 @@ TextComposition::CompositionEventDispatc
   nsCOMPtr<nsIWidget> widget(mTextComposition->GetWidget());
   if (!mTextComposition->IsValidStateForComposition(widget)) {
     return NS_OK; // cannot dispatch any events anymore
   }
 
   nsRefPtr<nsPresContext> presContext = mTextComposition->mPresContext;
   nsEventStatus status = nsEventStatus_eIgnore;
   switch (mEventMessage) {
-    case NS_COMPOSITION_START: {
-      WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, widget);
+    case eCompositionStart: {
+      WidgetCompositionEvent compStart(true, eCompositionStart, widget);
       WidgetQueryContentEvent selectedText(true, eQuerySelectedText, widget);
       ContentEventHandler handler(presContext);
       handler.OnQuerySelectedText(&selectedText);
       NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
       compStart.mData = selectedText.mReply.mString;
       compStart.mFlags.mIsSynthesizedForTests =
         mTextComposition->IsSynthesizedForTests();
       IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
                                                 &compStart, &status, nullptr,
                                                 mIsSynthesizedEvent);
       break;
     }
-    case NS_COMPOSITION_CHANGE:
-    case NS_COMPOSITION_COMMIT_AS_IS:
-    case NS_COMPOSITION_COMMIT: {
+    case eCompositionChange:
+    case eCompositionCommitAsIs:
+    case eCompositionCommit: {
       WidgetCompositionEvent compEvent(true, mEventMessage, widget);
-      if (mEventMessage != NS_COMPOSITION_COMMIT_AS_IS) {
+      if (mEventMessage != eCompositionCommitAsIs) {
         compEvent.mData = mData;
       }
       compEvent.mFlags.mIsSynthesizedForTests =
         mTextComposition->IsSynthesizedForTests();
       IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
                                                 &compEvent, &status, nullptr,
                                                 mIsSynthesizedEvent);
       break;
new file mode 100644
--- /dev/null
+++ b/dom/fetch/FetchUtil.cpp
@@ -0,0 +1,37 @@
+#include "FetchUtil.h"
+#include "nsError.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+// static
+nsresult
+FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
+{
+  nsAutoCString upperCaseMethod(aMethod);
+  ToUpperCase(upperCaseMethod);
+  if (upperCaseMethod.EqualsLiteral("CONNECT") ||
+      upperCaseMethod.EqualsLiteral("TRACE") ||
+      upperCaseMethod.EqualsLiteral("TRACK") ||
+      !NS_IsValidHTTPToken(aMethod)) {
+    outMethod.SetIsVoid(true);
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  if (upperCaseMethod.EqualsLiteral("DELETE") ||
+      upperCaseMethod.EqualsLiteral("GET") ||
+      upperCaseMethod.EqualsLiteral("HEAD") ||
+      upperCaseMethod.EqualsLiteral("OPTIONS") ||
+      upperCaseMethod.EqualsLiteral("POST") ||
+      upperCaseMethod.EqualsLiteral("PUT")) {
+    outMethod = upperCaseMethod;
+  }
+  else {
+    outMethod = aMethod; // Case unchanged for non-standard methods
+  }
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/FetchUtil.h
@@ -0,0 +1,28 @@
+#ifndef mozilla_dom_FetchUtil_h
+#define mozilla_dom_FetchUtil_h
+
+#include "nsString.h"
+#include "nsError.h"
+
+namespace mozilla {
+namespace dom {
+
+class FetchUtil final
+{
+private:
+  FetchUtil() = delete;
+
+public:
+  /**
+  * Sets outMethod to a valid HTTP request method string based on an input method.
+  * Implements checks and normalization as specified by the Fetch specification.
+  * Returns NS_ERROR_DOM_SECURITY_ERR if the method is invalid.
+  * Otherwise returns NS_OK and the normalized method via outMethod.
+  */
+  static nsresult
+  GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod);
+};
+
+} // namespace dom
+} // namespace mozilla
+#endif
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -7,16 +7,17 @@
 #include "Request.h"
 
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Fetch.h"
+#include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/workers/bindings/URL.h"
 #include "mozilla/unused.h"
 
 #include "WorkerPrivate.h"
 
@@ -293,44 +294,31 @@ Request::Constructor(const GlobalObject&
 
   if (aInit.mRedirect.WasPassed()) {
     request->SetRedirectMode(aInit.mRedirect.Value());
   }
 
   // Request constructor step 14.
   if (aInit.mMethod.WasPassed()) {
     nsAutoCString method(aInit.mMethod.Value());
-    nsAutoCString upperCaseMethod = method;
-    ToUpperCase(upperCaseMethod);
 
     // Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
     // token, since HTTP states that Method may be any of the defined values or
     // a token (extension method).
-    if (upperCaseMethod.EqualsLiteral("CONNECT") ||
-        upperCaseMethod.EqualsLiteral("TRACE") ||
-        upperCaseMethod.EqualsLiteral("TRACK") ||
-        !NS_IsValidHTTPToken(method)) {
+    nsAutoCString outMethod;
+    nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod);
+    if (NS_FAILED(rv)) {
       NS_ConvertUTF8toUTF16 label(method);
       aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
       return nullptr;
     }
 
     // Step 14.2
-    if (upperCaseMethod.EqualsLiteral("DELETE") ||
-        upperCaseMethod.EqualsLiteral("GET") ||
-        upperCaseMethod.EqualsLiteral("HEAD") ||
-        upperCaseMethod.EqualsLiteral("POST") ||
-        upperCaseMethod.EqualsLiteral("PUT") ||
-        upperCaseMethod.EqualsLiteral("OPTIONS")) {
-      request->ClearCreatedByFetchEvent();
-      request->SetMethod(upperCaseMethod);
-    } else {
-      request->ClearCreatedByFetchEvent();
-      request->SetMethod(method);
-    }
+    request->ClearCreatedByFetchEvent();
+    request->SetMethod(outMethod);
   }
 
   nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
 
   nsRefPtr<InternalHeaders> headers;
   if (aInit.mHeaders.WasPassed()) {
     nsRefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
     if (aRv.Failed()) {
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -3,28 +3,30 @@
 # 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/.
 
 EXPORTS.mozilla.dom += [
     'ChannelInfo.h',
     'Fetch.h',
     'FetchDriver.h',
+    'FetchUtil.h',
     'Headers.h',
     'InternalHeaders.h',
     'InternalRequest.h',
     'InternalResponse.h',
     'Request.h',
     'Response.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChannelInfo.cpp',
     'Fetch.cpp',
     'FetchDriver.cpp',
+    'FetchUtil.cpp',
     'Headers.cpp',
     'InternalHeaders.cpp',
     'InternalRequest.cpp',
     'InternalResponse.cpp',
     'Request.cpp',
     'Response.cpp',
 ]
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3029,18 +3029,18 @@ HTMLInputElement::NeedToInitializeEditor
   }
 
   switch (aVisitor.mEvent->mMessage) {
   case eMouseMove:
   case eMouseEnterIntoWidget:
   case eMouseExitFromWidget:
   case eMouseOver:
   case eMouseOut:
-  case NS_SCROLLPORT_UNDERFLOW:
-  case NS_SCROLLPORT_OVERFLOW:
+  case eScrollPortUnderflow:
+  case eScrollPortOverflow:
     return false;
   default:
     return true;
   }
 }
 
 bool
 HTMLInputElement::IsDisabledForEvents(EventMessage aMessage)
--- a/dom/interfaces/push/nsIPushClient.idl
+++ b/dom/interfaces/push/nsIPushClient.idl
@@ -12,20 +12,23 @@ interface nsIPrincipal;
  *
  * If status is not NS_OK, endpoint should be ignored. When subscribing to
  * a new endpoint, endpoint will be a valid URL on success, when querying for
  * the presence of an existing subscription, this will be an empty string if
  * the calling {scope+principal} does not currently have an associated
  * endpoint.
  */
 
-[scriptable, uuid(0bcac389-a3ac-44a4-97fb-b50e41a46146)]
+[scriptable, uuid(dc201064-8e5c-4a26-bd37-d1e33558a903)]
 interface nsIPushEndpointCallback : nsISupports
 {
-  void onPushEndpoint(in nsresult status, in DOMString endpoint);
+  void onPushEndpoint(in nsresult status,
+                      in DOMString endpoint,
+                      in uint32_t keyLen,
+                      [array, size_is(keyLen)] in octet key);
 };
 
 /**
  * Satisfies contracts similar to the Push API specification.
  *
  * If status is not NS_OK, there was a problem unsubscribing and success should
  * be ignored.  success is true if unsubscribing was successful and false if
  * there was no subscription.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -32,16 +32,17 @@
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/DoubleTapToZoom.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/Move.h"
 #include "mozilla/PWebBrowserPersistDocumentChild.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 #include "mozIApplication.h"
 #include "nsContentUtils.h"
@@ -594,47 +595,16 @@ TabChild::Create(nsIContentChild* aManag
         return child.forget();
     }
 
     nsRefPtr<TabChild> iframe = new TabChild(aManager, aTabId,
                                              aContext, aChromeFlags);
     return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
 }
 
-class TabChildSetAllowedTouchBehaviorCallback : public SetAllowedTouchBehaviorCallback {
-public:
-  explicit TabChildSetAllowedTouchBehaviorCallback(TabChild* aTabChild)
-    : mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
-  {}
-
-  void Run(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aFlags) const override {
-    if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
-      static_cast<TabChild*>(tabChild.get())->SendSetAllowedTouchBehavior(aInputBlockId, aFlags);
-    }
-  }
-
-private:
-  nsWeakPtr mTabChild;
-};
-
-class TabChildContentReceivedInputBlockCallback : public ContentReceivedInputBlockCallback {
-public:
-  explicit TabChildContentReceivedInputBlockCallback(TabChild* aTabChild)
-    : mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
-  {}
-
-  void Run(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, bool aPreventDefault) const override {
-    if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
-      static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
-    }
-  }
-private:
-  nsWeakPtr mTabChild;
-};
-
 TabChild::TabChild(nsIContentChild* aManager,
                    const TabId& aTabId,
                    const TabContext& aContext,
                    uint32_t aChromeFlags)
   : TabContext(aContext)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
   , mChromeFlags(aChromeFlags)
@@ -644,32 +614,40 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mIgnoreKeyPressEvent(false)
-  , mSetAllowedTouchBehaviorCallback(new TabChildSetAllowedTouchBehaviorCallback(this))
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
   , mIPCOpen(true)
   , mParentIsActive(false)
 {
   // In the general case having the TabParent tell us if APZ is enabled or not
   // doesn't really work because the TabParent itself may not have a reference
   // to the owning widget during initialization. Instead we assume that this
   // TabChild corresponds to a widget type that would have APZ enabled, and just
   // check the other conditions necessary for enabling APZ.
   mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
 
+  nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this)));  // for capture by the lambda
+  mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId,
+                                                   const nsTArray<TouchBehaviorFlags>& aFlags)
+  {
+    if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
+      static_cast<TabChild*>(tabChild.get())->SendSetAllowedTouchBehavior(aInputBlockId, aFlags);
+    }
+  };
+
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
 
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
@@ -855,18 +833,27 @@ TabChild::Init()
   // XXX: ideally, we would set a chrome event handler earlier,
   // and all windows, even the root one, will use the docshell one.
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   nsCOMPtr<EventTarget> chromeHandler =
     do_QueryInterface(window->GetChromeEventHandler());
   docShell->SetChromeEventHandler(chromeHandler);
 
-  mAPZEventState = new APZEventState(mPuppetWidget,
-      new TabChildContentReceivedInputBlockCallback(this));
+  nsWeakPtr weakPtrThis = do_GetWeakReference(static_cast<nsITabChild*>(this));  // for capture by the lambda
+  ContentReceivedInputBlockCallback callback(
+      [weakPtrThis](const ScrollableLayerGuid& aGuid,
+                    uint64_t aInputBlockId,
+                    bool aPreventDefault)
+      {
+        if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
+          static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
+        }
+      });
+  mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
 
   return NS_OK;
 }
 
 void
 TabChild::NotifyTabContextUpdated()
 {
     nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -27,34 +27,33 @@
 #include "nsITabChild.h"
 #include "nsITooltipListener.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
 #include "nsIWebBrowserChrome3.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "AudioChannelService.h"
 #include "PuppetWidget.h"
 
 class nsICachedFileDescriptorListener;
 class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 } // namespace layout
 
 namespace layers {
 class APZEventState;
 class ImageCompositeNotification;
-struct SetTargetAPZCCallback;
-struct SetAllowedTouchBehaviorCallback;
 } // namespace layers
 
 namespace widget {
 struct AutoCacheNativeKeyCommands;
 } // namespace widget
 
 namespace plugins {
 class PluginWidgetChild;
@@ -225,17 +224,16 @@ class TabChild final : public TabChildBa
                        public nsITabChild,
                        public nsIObserver,
                        public TabContext,
                        public nsITooltipListener
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::layers::APZEventState APZEventState;
-    typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback;
     typedef mozilla::layers::SetAllowedTouchBehaviorCallback SetAllowedTouchBehaviorCallback;
 
 public:
     /**
      * Find TabChild of aTabId in the same content process of the
      * caller.
      */
     static already_AddRefed<TabChild> FindTabChild(const TabId& aTabId);
@@ -630,17 +628,17 @@ private:
     bool mDidFakeShow;
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientationInternal mOrientation;
     bool mUpdateHitRegion;
 
     bool mIgnoreKeyPressEvent;
     nsRefPtr<APZEventState> mAPZEventState;
-    nsRefPtr<SetAllowedTouchBehaviorCallback> mSetAllowedTouchBehaviorCallback;
+    SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
     bool mHasValidInnerSize;
     bool mDestroyed;
     // Position of tab, relative to parent widget (typically the window)
     LayoutDeviceIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
     bool mIPCOpen;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2364,17 +2364,17 @@ TabParent::HandleQueryContentEvent(Widge
   if (!widget) {
     return true;
   }
   if (NS_WARN_IF(!mContentCache.HandleQueryContentEvent(aEvent, widget)) ||
       NS_WARN_IF(!aEvent.mSucceeded)) {
     return true;
   }
   switch (aEvent.mMessage) {
-    case NS_QUERY_TEXT_RECT:
+    case eQueryTextRect:
     case eQueryCaretRect:
     case eQueryEditorRect:
       aEvent.mReply.mRect -= GetChildProcessOffset();
       break;
     default:
       break;
   }
   return true;
--- a/dom/media/webaudio/AudioBlock.cpp
+++ b/dom/media/webaudio/AudioBlock.cpp
@@ -119,19 +119,22 @@ AudioBlock::SetBuffer(ThreadSharedObject
 void
 AudioBlock::ClearDownstreamMark() {
   if (mBufferIsDownstreamRef) {
     mBuffer->AsAudioBlockBuffer()->DownstreamRefRemoved();
     mBufferIsDownstreamRef = false;
   }
 }
 
-void
-AudioBlock::AssertNoLastingShares() {
-  MOZ_ASSERT(!mBuffer->AsAudioBlockBuffer()->HasLastingShares());
+bool
+AudioBlock::CanWrite() {
+  // If mBufferIsDownstreamRef is set then the buffer is not ours to use.
+  // It may be in use by another node which is not downstream.
+  return !mBufferIsDownstreamRef &&
+    !mBuffer->AsAudioBlockBuffer()->HasLastingShares();
 }
 
 void
 AudioBlock::AllocateChannels(uint32_t aChannelCount)
 {
   MOZ_ASSERT(mDuration == WEBAUDIO_BLOCK_SIZE);
 
   if (mBufferIsDownstreamRef) {
--- a/dom/media/webaudio/AudioBlock.h
+++ b/dom/media/webaudio/AudioBlock.h
@@ -19,19 +19,26 @@ namespace mozilla {
  * Use on graph thread only.
  */
 class AudioBlock : private AudioChunk
 {
 public:
   AudioBlock() {
     mDuration = WEBAUDIO_BLOCK_SIZE;
   }
-  MOZ_IMPLICIT AudioBlock(const AudioChunk& aChunk) {
-    mDuration = WEBAUDIO_BLOCK_SIZE;
-    operator=(aChunk);
+  // No effort is made in constructors to ensure that mBufferIsDownstreamRef
+  // is set because the block is expected to be a temporary and so the
+  // reference will be released before the next iteration.
+  // The custom copy constructor is required so as not to set
+  // mBufferIsDownstreamRef without notifying AudioBlockBuffer.
+  AudioBlock(const AudioBlock& aBlock) : AudioChunk(aBlock.AsAudioChunk()) {}
+  explicit AudioBlock(const AudioChunk& aChunk)
+    : AudioChunk(aChunk)
+  {
+    MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
   }
   ~AudioBlock();
 
   using AudioChunk::GetDuration;
   using AudioChunk::IsNull;
   using AudioChunk::ChannelCount;
   using AudioChunk::ChannelData;
   using AudioChunk::SizeOfExcludingThisIfUnshared;
@@ -39,44 +46,52 @@ public:
   // mDuration is not exposed.  Use GetDuration().
   // mBuffer is not exposed.  Use SetBuffer().
   using AudioChunk::mChannelData;
   using AudioChunk::mVolume;
   using AudioChunk::mBufferFormat;
 
   const AudioChunk& AsAudioChunk() const { return *this; }
   AudioChunk* AsMutableChunk() {
-    void ClearDownstreamMark();
+    ClearDownstreamMark();
     return this;
   }
 
   /**
    * Allocates, if necessary, aChannelCount buffers of WEBAUDIO_BLOCK_SIZE float
    * samples for writing.
    */
   void AllocateChannels(uint32_t aChannelCount);
 
+  /**
+   * ChannelFloatsForWrite() should only be used when the buffers have been
+   * created with AllocateChannels().
+   */
   float* ChannelFloatsForWrite(size_t aChannel)
   {
     MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
-#if DEBUG
-    AssertNoLastingShares();
-#endif
+    MOZ_ASSERT(CanWrite());
     return static_cast<float*>(const_cast<void*>(mChannelData[aChannel]));
   }
 
   void SetBuffer(ThreadSharedObject* aNewBuffer);
   void SetNull(StreamTime aDuration) {
     MOZ_ASSERT(aDuration == WEBAUDIO_BLOCK_SIZE);
     SetBuffer(nullptr);
     mChannelData.Clear();
     mVolume = 1.0f;
     mBufferFormat = AUDIO_FORMAT_SILENCE;
   }
 
+  AudioBlock& operator=(const AudioBlock& aBlock) {
+    // Instead of just copying, mBufferIsDownstreamRef must be first cleared
+    // if set.  It is set again for the new mBuffer if possible.  This happens
+    // in SetBuffer().
+    return *this = aBlock.AsAudioChunk();
+  }
   AudioBlock& operator=(const AudioChunk& aChunk) {
     MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
     SetBuffer(aChunk.mBuffer);
     mChannelData = aChunk.mChannelData;
     mVolume = aChunk.mVolume;
     mBufferFormat = aChunk.mBufferFormat;
     return *this;
   }
@@ -98,17 +113,17 @@ public:
       }
     }
 
     return true;
   }
 
 private:
   void ClearDownstreamMark();
-  void AssertNoLastingShares();
+  bool CanWrite();
 
   // mBufferIsDownstreamRef is set only when mBuffer references an
   // AudioBlockBuffer created in a different AudioBlock.  That can happen when
   // this AudioBlock is on a node downstream from the node which created the
   // buffer.  When this is set, the AudioBlockBuffer is notified that this
   // reference does prevent the upstream node from re-using the buffer next
   // iteration and modifying its contents.  The AudioBlockBuffer is also
   // notified when mBuffer releases this reference.
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -48,16 +48,26 @@ AudioNodeStream::AudioNodeStream(AudioNo
   MOZ_COUNT_CTOR(AudioNodeStream);
 }
 
 AudioNodeStream::~AudioNodeStream()
 {
   MOZ_COUNT_DTOR(AudioNodeStream);
 }
 
+void
+AudioNodeStream::DestroyImpl()
+{
+  // These are graph thread objects, so clean up on graph thread.
+  mInputChunks.Clear();
+  mLastChunks.Clear();
+
+  ProcessedMediaStream::DestroyImpl();
+}
+
 /* static */ already_AddRefed<AudioNodeStream>
 AudioNodeStream::Create(MediaStreamGraph* aGraph, AudioNodeEngine* aEngine,
                         Flags aFlags)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // MediaRecorders use an AudioNodeStream, but no AudioNode
   AudioNode* node = aEngine->NodeMainThread();
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -164,16 +164,18 @@ public:
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   void SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
                                      AudioNodeSizes& aUsage) const;
 
 
 protected:
+  virtual void DestroyImpl() override;
+
   void AdvanceOutputSegment();
   void FinishOutput();
   void AccumulateInputChunk(uint32_t aInputIndex, const AudioBlock& aChunk,
                             AudioBlock* aBlock,
                             nsTArray<float>* aDownmixBuffer);
   void UpMixDownMixChunk(const AudioBlock* aChunk, uint32_t aOutputChannelCount,
                          nsTArray<const float*>& aOutputChannels,
                          nsTArray<float>& aDownmixBuffer);
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1363,17 +1363,17 @@ Promise::Settle(JS::Handle<JS::Value> aV
         JS_ClearPendingException(cx);
       }
     } else {
       JS_ClearPendingException(cx);
     }
   }
 #endif
 
-  if (mGlobal->IsDying()) {
+  if (!mGlobal || mGlobal->IsDying()) {
     return;
   }
 
   mSettlementTimestamp = TimeStamp::Now();
 
   SetResult(aValue);
   SetState(aState);
 
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -116,20 +116,32 @@ Push.prototype = {
   getEndpointResponse: function(fn) {
     debug("GetEndpointResponse " + fn.toSource());
     let that = this;
     let p = this.createPromise(function(resolve, reject) {
       this.askPermission(
         () => {
           fn(that._scope, that._principal, {
             QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]),
-            onPushEndpoint: function(ok, endpoint) {
+            onPushEndpoint: function(ok, endpoint, keyLen, key) {
               if (ok === Cr.NS_OK) {
                 if (endpoint) {
-                  let sub = new that._window.PushSubscription(endpoint, that._scope);
+                  let sub;
+                  if (keyLen) {
+                    let publicKey = new ArrayBuffer(keyLen);
+                    let keyView = new Uint8Array(publicKey);
+                    keyView.set(key);
+                    sub = new that._window.PushSubscription(endpoint,
+                                                            that._scope,
+                                                            publicKey);
+                  } else {
+                    sub = new that._window.PushSubscription(endpoint,
+                                                            that._scope,
+                                                            null);
+                  }
                   sub.setPrincipal(that._principal);
                   resolve(sub);
                 } else {
                   resolve(null);
                 }
               } else {
                 reject("AbortError");
               }
--- a/dom/push/PushClient.js
+++ b/dom/push/PushClient.js
@@ -95,45 +95,58 @@ PushClient.prototype = {
     let requestId = this.addRequest(callback);
     debug("Going to send  " + scope + " " + principal + " " + requestId);
     this._cpmm.sendAsyncMessage("Push:Registration", {
                                 scope: scope,
                                 requestID: requestId,
                               }, null, principal);
   },
 
+  _deliverPushEndpoint: function(request, registration) {
+    if (registration.p256dhKey) {
+      let key = new Uint8Array(registration.p256dhKey);
+      request.onPushEndpoint(Cr.NS_OK,
+                             registration.pushEndpoint,
+                             key.length,
+                             key);
+      return;
+    }
+
+    request.onPushEndpoint(Cr.NS_OK, registration.pushEndpoint, 0, null);
+  },
+
   receiveMessage: function(aMessage) {
 
     let json = aMessage.data;
     let request = this.takeRequest(json.requestID);
 
     if (!request) {
       return;
     }
 
     debug("receiveMessage(): " + JSON.stringify(aMessage))
     switch (aMessage.name) {
       case "PushService:Register:OK":
-      {
-        request.onPushEndpoint(Cr.NS_OK, json.pushEndpoint);
+        this._deliverPushEndpoint(request, json);
         break;
-      }
       case "PushService:Register:KO":
-        request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "");
+        request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
         break;
       case "PushService:Registration:OK":
       {
         let endpoint = "";
-        if (json.registration)
-          endpoint = json.registration.pushEndpoint;
-        request.onPushEndpoint(Cr.NS_OK, endpoint);
+        if (!json.registration) {
+          request.onPushEndpoint(Cr.NS_OK, "", 0, null);
+        } else {
+          this._deliverPushEndpoint(request, json.registration);
+        }
         break;
       }
       case "PushService:Registration:KO":
-        request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "");
+        request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
         break;
       case "PushService:Unregister:OK":
         if (typeof json.result !== "boolean") {
           debug("Expected boolean result from PushService!");
           request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
           return;
         }
 
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -85,49 +85,86 @@ PushSubscription::Unsubscribe(ErrorResul
   nsRefPtr<UnsubscribeResultCallback> callback =
     new UnsubscribeResultCallback(p);
   client->Unsubscribe(mScope, mPrincipal, callback);
   return p.forget();
 }
 
 PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
                                    const nsAString& aEndpoint,
-                                   const nsAString& aScope)
-  : mGlobal(aGlobal), mEndpoint(aEndpoint), mScope(aScope)
+                                   const nsAString& aScope,
+                                   const nsTArray<uint8_t>& aRawP256dhKey)
+  : mGlobal(aGlobal)
+  , mEndpoint(aEndpoint)
+  , mScope(aScope)
+  , mRawP256dhKey(aRawP256dhKey)
 {
 }
 
 PushSubscription::~PushSubscription()
-{}
+{
+}
 
 JSObject*
 PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
+PushSubscription::GetKey(JSContext* aCx,
+                         PushEncryptionKeyName aType,
+                         JS::MutableHandle<JSObject*> aP256dhKey)
+{
+  if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
+    aP256dhKey.set(ArrayBuffer::Create(aCx,
+                                       mRawP256dhKey.Length(),
+                                       mRawP256dhKey.Elements()));
+  } else {
+    aP256dhKey.set(nullptr);
+  }
+}
+
+void
 PushSubscription::SetPrincipal(nsIPrincipal* aPrincipal)
 {
   MOZ_ASSERT(!mPrincipal);
   mPrincipal = aPrincipal;
 }
 
 // static
 already_AddRefed<PushSubscription>
-PushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv)
+PushSubscription::Constructor(GlobalObject& aGlobal,
+                              const nsAString& aEndpoint,
+                              const nsAString& aScope,
+                              const Nullable<ArrayBuffer>& aP256dhKey,
+                              ErrorResult& aRv)
 {
   MOZ_ASSERT(!aEndpoint.IsEmpty());
   MOZ_ASSERT(!aScope.IsEmpty());
+
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
-  nsRefPtr<PushSubscription> sub = new PushSubscription(global, aEndpoint, aScope);
+
+  nsTArray<uint8_t> rawKey;
+  if (!aP256dhKey.IsNull()) {
+    const ArrayBuffer& key = aP256dhKey.Value();
+    key.ComputeLengthAndData();
+    rawKey.SetLength(key.Length());
+    rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
+  }
+  nsRefPtr<PushSubscription> sub = new PushSubscription(global,
+                                                        aEndpoint,
+                                                        aScope,
+                                                        rawKey);
+
   return sub.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mPrincipal)
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 PushManager::PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope)
@@ -183,44 +220,74 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMan
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // WorkerPushSubscription
 
 WorkerPushSubscription::WorkerPushSubscription(const nsAString& aEndpoint,
-                                               const nsAString& aScope)
-  : mEndpoint(aEndpoint), mScope(aScope)
+                                               const nsAString& aScope,
+                                               const nsTArray<uint8_t>& aRawP256dhKey)
+  : mEndpoint(aEndpoint), mScope(aScope), mRawP256dhKey(aRawP256dhKey)
 {
   MOZ_ASSERT(!aScope.IsEmpty());
   MOZ_ASSERT(!aEndpoint.IsEmpty());
 }
 
 WorkerPushSubscription::~WorkerPushSubscription()
 {}
 
 JSObject*
 WorkerPushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PushSubscriptionBinding_workers::Wrap(aCx, this, aGivenProto);
 }
 
 // static
 already_AddRefed<WorkerPushSubscription>
-WorkerPushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv)
+WorkerPushSubscription::Constructor(GlobalObject& aGlobal,
+                                    const nsAString& aEndpoint,
+                                    const nsAString& aScope,
+                                    const Nullable<ArrayBuffer>& aP256dhKey,
+                                    ErrorResult& aRv)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
-  nsRefPtr<WorkerPushSubscription> sub = new WorkerPushSubscription(aEndpoint, aScope);
+  nsTArray<uint8_t> rawKey;
+  if (!aP256dhKey.IsNull()) {
+    const ArrayBuffer& key = aP256dhKey.Value();
+    key.ComputeLengthAndData();
+    rawKey.SetLength(key.Length());
+    rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
+  }
+  nsRefPtr<WorkerPushSubscription> sub = new WorkerPushSubscription(aEndpoint,
+                                                                    aScope,
+                                                                    rawKey);
+
   return sub.forget();
 }
 
+void
+WorkerPushSubscription::GetKey(JSContext* aCx,
+                               PushEncryptionKeyName aType,
+                               JS::MutableHandle<JSObject*> aP256dhKey)
+{
+  if (aType == mozilla::dom::PushEncryptionKeyName::P256dh &&
+      !mRawP256dhKey.IsEmpty()) {
+    aP256dhKey.set(ArrayBuffer::Create(aCx,
+                                       mRawP256dhKey.Length(),
+                                       mRawP256dhKey.Elements()));
+  } else {
+    aP256dhKey.set(nullptr);
+  }
+}
+
 class UnsubscribeResultRunnable final : public WorkerRunnable
 {
 public:
   UnsubscribeResultRunnable(PromiseWorkerProxy* aProxy,
                             nsresult aStatus,
                             bool aSuccess)
     : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
     , mProxy(aProxy)
@@ -366,16 +433,17 @@ WorkerPushSubscription::Unsubscribe(Erro
   nsRefPtr<UnsubscribeRunnable> r =
     new UnsubscribeRunnable(proxy, mScope);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
 
   return p.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerPushSubscription)
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerPushSubscription)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerPushSubscription)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPushSubscription)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // WorkerPushManager
@@ -392,34 +460,36 @@ WorkerPushManager::WrapObject(JSContext*
 }
 
 class GetSubscriptionResultRunnable final : public WorkerRunnable
 {
 public:
   GetSubscriptionResultRunnable(PromiseWorkerProxy* aProxy,
                                 nsresult aStatus,
                                 const nsAString& aEndpoint,
-                                const nsAString& aScope)
+                                const nsAString& aScope,
+                                const nsTArray<uint8_t>& aRawP256dhKey)
     : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
     , mProxy(aProxy)
     , mStatus(aStatus)
     , mEndpoint(aEndpoint)
     , mScope(aScope)
+    , mRawP256dhKey(aRawP256dhKey)
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     nsRefPtr<Promise> promise = mProxy->WorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
       if (mEndpoint.IsEmpty()) {
         promise->MaybeResolve(JS::NullHandleValue);
       } else {
         nsRefPtr<WorkerPushSubscription> sub =
-          new WorkerPushSubscription(mEndpoint, mScope);
+          new WorkerPushSubscription(mEndpoint, mScope, mRawP256dhKey);
         promise->MaybeResolve(sub);
       }
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
     mProxy->CleanUp(aCx);
     return true;
@@ -427,47 +497,58 @@ public:
 private:
   ~GetSubscriptionResultRunnable()
   {}
 
   nsRefPtr<PromiseWorkerProxy> mProxy;
   nsresult mStatus;
   nsString mEndpoint;
   nsString mScope;
+  nsTArray<uint8_t> mRawP256dhKey;
 };
 
 class GetSubscriptionCallback final : public nsIPushEndpointCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit GetSubscriptionCallback(PromiseWorkerProxy* aProxy,
                                    const nsAString& aScope)
     : mProxy(aProxy)
     , mScope(aScope)
   {}
 
   NS_IMETHOD
-  OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
+  OnPushEndpoint(nsresult aStatus,
+                 const nsAString& aEndpoint,
+                 uint32_t aKeyLen,
+                 uint8_t* aKey) override
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?");
 
     nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
 
     MutexAutoLock lock(proxy->Lock());
     if (proxy->CleanedUp()) {
       return NS_OK;
     }
 
     AutoJSAPI jsapi;
     jsapi.Init();
 
+    nsTArray<uint8_t> rawP256dhKey(aKeyLen);
+    rawP256dhKey.ReplaceElementsAt(0, aKeyLen, aKey, aKeyLen);
+
     nsRefPtr<GetSubscriptionResultRunnable> r =
-      new GetSubscriptionResultRunnable(proxy, aStatus, aEndpoint, mScope);
+      new GetSubscriptionResultRunnable(proxy,
+                                        aStatus,
+                                        aEndpoint,
+                                        mScope,
+                                        rawP256dhKey);
     r->Dispatch(jsapi.cx());
     return NS_OK;
   }
 
 protected:
   ~GetSubscriptionCallback()
   {}
 
@@ -497,49 +578,49 @@ public:
       return NS_OK;
     }
 
     nsRefPtr<GetSubscriptionCallback> callback = new GetSubscriptionCallback(mProxy, mScope);
 
     nsCOMPtr<nsIPermissionManager> permManager =
       mozilla::services::GetPermissionManager();
     if (!permManager) {
-      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
+      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
       return NS_OK;
     }
 
     nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
 
     uint32_t permission = nsIPermissionManager::DENY_ACTION;
     nsresult rv = permManager->TestExactPermissionFromPrincipal(
                     principal,
                     "push",
                     &permission);
 
     if (NS_WARN_IF(NS_FAILED(rv)) || permission != nsIPermissionManager::ALLOW_ACTION) {
-      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
+      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
       return NS_OK;
     }
 
     nsCOMPtr<nsIPushClient> client =
       do_CreateInstance("@mozilla.org/push/PushClient;1");
     if (!client) {
-      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
+      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
       return NS_OK;
     }
 
     if (mAction == WorkerPushManager::SubscribeAction) {
       rv = client->Subscribe(mScope, principal, callback);
     } else {
       MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction);
       rv = client->GetSubscription(mScope, principal, callback);
     }
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
+      callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
       return rv;
     }
 
     return NS_OK;
   }
 
 private:
   ~GetSubscriptionRunnable()
--- a/dom/push/PushManager.h
+++ b/dom/push/PushManager.h
@@ -31,24 +31,27 @@
 #ifndef mozilla_dom_PushManager_h
 #define mozilla_dom_PushManager_h
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/TypedArray.h"
 
 #include "nsCOMPtr.h"
 #include "mozilla/nsRefPtr.h"
 #include "jsapi.h"
 
 class nsIGlobalObject;
 class nsIPrincipal;
 
+#include "mozilla/dom/PushSubscriptionBinding.h"
+
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 class WorkerPrivate;
 }
 
 class Promise;
@@ -58,50 +61,61 @@ class PushSubscription final : public ns
                              , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushSubscription)
 
   explicit PushSubscription(nsIGlobalObject* aGlobal,
                             const nsAString& aEndpoint,
-                            const nsAString& aScope);
+                            const nsAString& aScope,
+                            const nsTArray<uint8_t>& aP256dhKey);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mGlobal;
   }
 
   void
   GetEndpoint(nsAString& aEndpoint) const
   {
     aEndpoint = mEndpoint;
   }
 
+  void
+  GetKey(JSContext* cx,
+         PushEncryptionKeyName aType,
+         JS::MutableHandle<JSObject*> aP256dhKey);
+
   static already_AddRefed<PushSubscription>
-  Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv);
+  Constructor(GlobalObject& aGlobal,
+              const nsAString& aEndpoint,
+              const nsAString& aScope,
+              const Nullable<ArrayBuffer>& aP256dhKey,
+              ErrorResult& aRv);
 
   void
   SetPrincipal(nsIPrincipal* aPrincipal);
 
   already_AddRefed<Promise>
   Unsubscribe(ErrorResult& aRv);
 
 protected:
   ~PushSubscription();
 
 private:
   nsCOMPtr<nsIGlobalObject> mGlobal;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsString mEndpoint;
   nsString mScope;
+  nsTArray<uint8_t> mRawP256dhKey;
 };
 
 class PushManager final : public nsISupports
                         , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushManager)
@@ -141,45 +155,55 @@ private:
 class WorkerPushSubscription final : public nsISupports
                                    , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushSubscription)
 
   explicit WorkerPushSubscription(const nsAString& aEndpoint,
-                                  const nsAString& aScope);
+                                  const nsAString& aScope,
+                                  const nsTArray<uint8_t>& aRawP256dhKey);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return nullptr;
   }
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<WorkerPushSubscription>
-  Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv);
+  Constructor(GlobalObject& aGlobal,
+              const nsAString& aEndpoint,
+              const nsAString& aScope,
+              const Nullable<ArrayBuffer>& aP256dhKey,
+              ErrorResult& aRv);
 
   void
   GetEndpoint(nsAString& aEndpoint) const
   {
     aEndpoint = mEndpoint;
   }
 
+  void
+  GetKey(JSContext* cx, PushEncryptionKeyName aType,
+         JS::MutableHandle<JSObject*> aP256dhKey);
+
   already_AddRefed<Promise>
   Unsubscribe(ErrorResult& aRv);
 
 protected:
   ~WorkerPushSubscription();
 
 private:
   nsString mEndpoint;
   nsString mScope;
+  nsTArray<uint8_t> mRawP256dhKey;
 };
 
 class WorkerPushManager final : public nsISupports
                               , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushManager)
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -714,16 +714,21 @@ this.PushService = {
   },
 
   updateRegistrationAndNotifyApp: function(aOldKey, aRecord) {
     return this._db.delete(aOldKey)
       .then(_ => this._db.put(aRecord))
       .then(record => this._notifySubscriptionChangeObservers(record));
   },
 
+  updateRecordAndNotifyApp: function(aKeyID, aUpdateFunc) {
+    return this._db.update(aKeyID, aUpdateFunc)
+      .then(record => this._notifySubscriptionChangeObservers(record));
+  },
+
   /**
    * Dispatches an incoming message to a service worker, recalculating the
    * quota for the associated push registration. If the quota is exceeded,
    * the registration and message will be dropped, and the worker will not
    * be notified.
    *
    * @param {String} keyID The push registration ID.
    * @param {String} message The message contents.
@@ -732,17 +737,17 @@ this.PushService = {
    *  function returns `null` or `undefined`, the record will not be updated.
    *  `PushServiceWebSocket` uses this to drop incoming updates with older
    *  versions.
    */
   receivedPushMessage: function(keyID, message, updateFunc) {
     debug("receivedPushMessage()");
 
     let shouldNotify = false;
-    this.getByKeyID(keyID).then(record => {
+    return this.getByKeyID(keyID).then(record => {
       if (!record) {
         throw new Error("No record for key ID " + keyID);
       }
       return record.getLastVisit();
     }).then(lastVisit => {
       // As a special case, don't notify the service worker if the user
       // cleared their history.
       shouldNotify = isFinite(lastVisit);
@@ -756,41 +761,44 @@ this.PushService = {
           // for stale registrations from the server.
           debug("receivedPushMessage: Ignoring update for expired key ID " + keyID);
           return null;
         }
         newRecord.receivedPush(lastVisit);
         return newRecord;
       });
     }).then(record => {
+      var notified = false;
       if (!record) {
-        return null;
+        return notified;
       }
+
       if (shouldNotify) {
-        this._notifyApp(record, message);
+        notified = this._notifyApp(record, message);
       }
       if (record.isExpired()) {
         // Drop the registration in the background. If the user returns to the
         // site, the service worker will be notified on the next `idle-daily`
         // event.
         this._sendRequest("unregister", record).catch(error => {
           debug("receivedPushMessage: Unregister error: " + error);
         });
       }
+      return notified;
     }).catch(error => {
       debug("receivedPushMessage: Error notifying app: " + error);
     });
   },
 
   _notifyApp: function(aPushRecord, message) {
     if (!aPushRecord || !aPushRecord.scope ||
         aPushRecord.originAttributes === undefined) {
       debug("notifyApp() something is undefined.  Dropping notification: " +
         JSON.stringify(aPushRecord) );
-      return;
+      return false;
     }
 
     debug("notifyApp() " + aPushRecord.scope);
     // Notify XPCOM observers.
     let notification = Cc["@mozilla.org/push/ObserverNotification;1"]
                          .createInstance(Ci.nsIPushObserverNotification);
     notification.pushEndpoint = aPushRecord.pushEndpoint;
     notification.version = aPushRecord.version;
@@ -802,27 +810,28 @@ this.PushService = {
       notification,
       "push-notification",
       aPushRecord.scope
     );
 
     // If permission has been revoked, trash the message.
     if (!aPushRecord.hasPermission()) {
       debug("Does not have permission for push.");
-      return;
+      return false;
     }
 
     // TODO data.
     let data = {
       payload: message,
       originAttributes: aPushRecord.originAttributes,
       scope: aPushRecord.scope
     };
 
     this._notifyListeners('push', data);
+    return true;
   },
 
   getByKeyID: function(aKeyID) {
     return this._db.getByKeyID(aKeyID);
   },
 
   getAllUnexpired: function() {
     return this._db.getAllUnexpired();
--- a/dom/push/PushServiceHttp2.jsm
+++ b/dom/push/PushServiceHttp2.jsm
@@ -14,32 +14,35 @@ const {PushDB} = Cu.import("resource://g
 const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
+const {PushServiceHttp2Crypto, concatArray} =
+  Cu.import("resource://gre/modules/PushServiceHttp2Crypto.jsm");
+
 this.EXPORTED_SYMBOLS = ["PushServiceHttp2"];
 
 const prefs = new Preferences("dom.push.");
 
 // Don't modify this, instead set dom.push.debug.
 // Set debug first so that all debugging actually works.
 var gDebuggingEnabled = prefs.get("debug");
 
 function debug(s) {
   if (gDebuggingEnabled) {
     dump("-*- PushServiceHttp2.jsm: " + s + "\n");
   }
 }
 
 const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
-const kPUSHHTTP2DB_DB_VERSION = 4; // Change this if the IndexedDB format changes
+const kPUSHHTTP2DB_DB_VERSION = 5; // Change this if the IndexedDB format changes
 const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
 
 /**
  * A proxy between the PushService and connections listening for incoming push
  * messages. The PushService can silence messages from the connections by
  * setting PushSubscriptionListener._pushService to null. This is required
  * because it can happen that there is an outstanding push message that will
  * be send on OnStopRequest but the PushService may not be interested in these.
@@ -109,57 +112,117 @@ PushSubscriptionListener.prototype = {
 
 /**
  * The listener for pushed messages. The message data is collected in
  * OnDataAvailable and send to the app in OnStopRequest.
  */
 var PushChannelListener = function(pushSubscriptionListener) {
   debug("Creating a new push channel listener.");
   this._mainListener = pushSubscriptionListener;
+  this._message = [];
+  this._ackUri = null;
 };
 
 PushChannelListener.prototype = {
 
-  _message: null,
-  _ackUri: null,
-
   onStartRequest: function(aRequest, aContext) {
     this._ackUri = aRequest.URI.spec;
   },
 
   onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
     debug("push channel listener onDataAvailable()");
 
     if (aCount === 0) {
       return;
     }
 
-    let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
-                        .createInstance(Ci.nsIScriptableInputStream);
+    let inputStream = Cc["@mozilla.org/binaryinputstream;1"]
+                        .createInstance(Ci.nsIBinaryInputStream);
 
-    inputStream.init(aStream);
-    if (!this._message) {
-      this._message = inputStream.read(aCount);
-    } else {
-      this._message.concat(inputStream.read(aCount));
-    }
+    inputStream.setInputStream(aStream);
+    let chunk = new ArrayBuffer(aCount);
+    inputStream.readArrayBuffer(aCount, chunk);
+    this._message.push(chunk);
   },
 
   onStopRequest: function(aRequest, aContext, aStatusCode) {
     debug("push channel listener onStopRequest()  status code:" + aStatusCode);
     if (Components.isSuccessCode(aStatusCode) &&
         this._mainListener &&
         this._mainListener._pushService) {
+
+      var keymap = encryptKeyFieldParser(aRequest);
+      if (!keymap) {
+        return;
+      }
+      var enc = encryptFieldParser(aRequest);
+      if (!enc || !enc.keyid) {
+        return;
+      }
+      var dh = keymap[enc.keyid];
+      var salt = enc.salt;
+      var rs = (enc.rs)? parseInt(enc.rs, 10) : 4096;
+      if (!dh || !salt || isNaN(rs) || (rs <= 1)) {
+        return;
+      }
+
+      var msg = concatArray(this._message);
+
       this._mainListener._pushService._pushChannelOnStop(this._mainListener.uri,
                                                          this._ackUri,
-                                                         this._message);
+                                                         msg,
+                                                         dh,
+                                                         salt,
+                                                         rs);
     }
   }
 };
 
+var parseHeaderFieldParams = (m, v) => {
+  var i = v.indexOf('=');
+  if (i >= 0) {
+    // A quoted string with internal quotes is invalid for all the possible
+    // values of this header field.
+    m[v.substring(0, i).trim()] = v.substring(i + 1).trim()
+                                    .replace(/^"(.*)"$/, '$1');
+  }
+  return m;
+};
+
+function encryptKeyFieldParser(aRequest) {
+  try {
+    var encryptKeyField = aRequest.getRequestHeader("Encryption-Key");
+
+    var params = encryptKeyField.split(',');
+    return params.reduce((m, p) => {
+      var pmap = p.split(';').reduce(parseHeaderFieldParams, {});
+      if (pmap.keyid && pmap.dh) {
+        m[pmap.keyid] = pmap.dh;
+      }
+      return m;
+    }, {});
+
+  } catch(e) {
+    // getRequestHeader can throw.
+    return null;
+  }
+}
+
+function encryptFieldParser(aRequest) {
+  try {
+    return aRequest.getRequestHeader("Encryption")
+             .split(',', 1)[0]
+             .split(';')
+             .reduce(parseHeaderFieldParams, {});
+  } catch(e) {
+    // getRequestHeader can throw.
+    return null;
+  }
+}
+
 var PushServiceDelete = function(resolve, reject) {
   this._resolve = resolve;
   this._reject = reject;
 };
 
 PushServiceDelete.prototype = {
 
   onStartRequest: function(aRequest, aContext) {},
@@ -183,19 +246,22 @@ PushServiceDelete.prototype = {
     if (Components.isSuccessCode(aStatusCode)) {
        this._resolve();
     } else {
        this._reject({status: 0, error: "NetworkError"});
     }
   }
 };
 
-var SubscriptionListener = function(aSubInfo, aServerURI, aPushServiceHttp2) {
+var SubscriptionListener = function(aSubInfo, aResolve, aReject,
+                                    aServerURI, aPushServiceHttp2) {
   debug("Creating a new subscription listener.");
   this._subInfo = aSubInfo;
+  this._resolve = aResolve;
+  this._reject = aReject;
   this._data = '';
   this._serverURI = aServerURI;
   this._service = aPushServiceHttp2;
 };
 
 SubscriptionListener.prototype = {
 
   onStartRequest: function(aRequest, aContext) {},
@@ -216,91 +282,94 @@ SubscriptionListener.prototype = {
     this._data.concat(inputStream.read(aCount));
   },
 
   onStopRequest: function(aRequest, aContext, aStatus) {
     debug("subscription listener onStopRequest()");
 
     // Check if pushService is still active.
     if (!this._service.hasmainPushService()) {
-      this._subInfo.reject({error: "Service deactivated"});
+      this._reject({error: "Service deactivated"});
       return;
     }
 
     if (!Components.isSuccessCode(aStatus)) {
-      this._subInfo.reject({error: "Error status" + aStatus});
+      this._reject({error: "Error status" + aStatus});
       return;
     }
 
     var statusCode = aRequest.QueryInterface(Ci.nsIHttpChannel).responseStatus;
 
     if (Math.floor(statusCode / 100) == 5) {
       if (this._subInfo.retries < prefs.get("http2.maxRetries")) {
         this._subInfo.retries++;
         var retryAfter = retryAfterParser(aRequest);
-        setTimeout(this._service.retrySubscription.bind(this._service,
-                                                        this._subInfo),
-                   retryAfter);
+        setTimeout(_ => this._reject(
+          {
+            retry: true,
+            subInfo: this._subInfo
+          }),
+          retryAfter);
       } else {
-        this._subInfo.reject({error: "Error response code: " + statusCode });
+        this._reject({error: "Error response code: " + statusCode });
       }
       return;
     } else if (statusCode != 201) {
-      this._subInfo.reject({error: "Error response code: " + statusCode });
+      this._reject({error: "Error response code: " + statusCode });
       return;
     }
 
     var subscriptionUri;
     try {
       subscriptionUri = aRequest.getResponseHeader("location");
     } catch (err) {
-      this._subInfo.reject({error: "Return code 201, but the answer is bogus"});
+      this._reject({error: "Return code 201, but the answer is bogus"});
       return;
     }
 
     debug("subscriptionUri: " + subscriptionUri);
 
     var linkList;
     try {
       linkList = aRequest.getResponseHeader("link");
     } catch (err) {
-      this._subInfo.reject({error: "Return code 201, but the answer is bogus"});
+      this._reject({error: "Return code 201, but the answer is bogus"});
       return;
     }
 
     var linkParserResult = linkParser(linkList, this._serverURI);
     if (linkParserResult.error) {
-      this._subInfo.reject(linkParserResult);
+      this._reject(linkParserResult);
       return;
     }
 
     if (!subscriptionUri) {
-      this._subInfo.reject({error: "Return code 201, but the answer is bogus," +
-                                   " missing subscriptionUri"});
+      this._reject({error: "Return code 201, but the answer is bogus," +
+                           " missing subscriptionUri"});
       return;
     }
     try {
       let uriTry = Services.io.newURI(subscriptionUri, null, null);
     } catch (e) {
       debug("Invalid URI " + subscriptionUri);
-      this._subInfo.reject({error: "Return code 201, but URI is bogus. " +
-                                   subscriptionUri});
+      this._reject({error: "Return code 201, but URI is bogus. " +
+                    subscriptionUri});
       return;
     }
 
     let reply = new PushRecordHttp2({
       subscriptionUri: subscriptionUri,
       pushEndpoint: linkParserResult.pushEndpoint,
       pushReceiptEndpoint: linkParserResult.pushReceiptEndpoint,
       scope: this._subInfo.record.scope,
       originAttributes: this._subInfo.record.originAttributes,
       quota: this._subInfo.record.maxQuota,
     });
 
-    this._subInfo.resolve(reply);
+    this._resolve(reply);
   },
 };
 
 function retryAfterParser(aRequest) {
   var retryAfter = 0;
   try {
     var retryField = aRequest.getResponseHeader("retry-after");
     if (isNaN(retryField)) {
@@ -451,51 +520,63 @@ this.PushServiceHttp2 = {
   },
 
   /**
    * Subscribe new resource.
    */
   _subscribeResource: function(aRecord) {
     debug("subscribeResource()");
 
-    return new Promise((resolve, reject) => {
-      this._subscribeResourceInternal({record: aRecord,
-                                       resolve,
-                                       reject,
-                                       retries: 0});
+    return this._subscribeResourceInternal({
+      record: aRecord,
+      retries: 0
     })
-    .then(result => {
-      this._conns[result.subscriptionUri] = {channel: null,
-                                             listener: null,
-                                             countUnableToConnect: 0,
-                                             lastStartListening: 0,
-                                             waitingForAlarm: false};
-      this._listenForMsgs(result.subscriptionUri);
-      return result;
-    });
+    .then(result =>
+      PushServiceHttp2Crypto.generateKeys()
+      .then(exportedKeys => {
+        result.p256dhPublicKey = exportedKeys[0];
+        result.p256dhPrivateKey = exportedKeys[1];
+        this._conns[result.subscriptionUri] = {
+          channel: null,
+          listener: null,
+          countUnableToConnect: 0,
+          lastStartListening: 0,
+          waitingForAlarm: false
+        };
+        this._listenForMsgs(result.subscriptionUri);
+        return result;
+      })
+    );
   },
 
   _subscribeResourceInternal: function(aSubInfo) {
-    debug("subscribeResource()");
+    debug("subscribeResourceInternal()");
 
-    var listener = new SubscriptionListener(aSubInfo,
-                                            this._serverURI,
-                                            this);
+    return new Promise((resolve, reject) => {
+      var listener = new SubscriptionListener(aSubInfo,
+                                              resolve,
+                                              reject,
+                                              this._serverURI,
+                                              this);
 
-    var chan = this._makeChannel(this._serverURI.spec);
-    chan.requestMethod = "POST";
-    try{
-      chan.asyncOpen(listener, null);
-    } catch(e) {
-      aSubInfo.reject({status: 0, error: "NetworkError"});
-    }
-  },
-
-  retrySubscription: function(aSubInfo) {
-    this._subscribeResourceInternal(aSubInfo);
+      var chan = this._makeChannel(this._serverURI.spec);
+      chan.requestMethod = "POST";
+      try {
+        chan.asyncOpen(listener, null);
+      } catch(e) {
+        reject({status: 0, error: "NetworkError"});
+      }
+    })
+    .catch(err => {
+      if ("retry" in err) {
+        return this._subscribeResourceInternal(err.subInfo);
+      } else {
+        throw err;
+      }
+    })
   },
 
   _deleteResource: function(aUri) {
 
     return new Promise((resolve,reject) => {
       var chan = this._makeChannel(aUri);
       chan.requestMethod = "DELETE";
       try {
@@ -635,26 +716,58 @@ this.PushServiceHttp2 = {
   },
 
   // Start listening if subscriptions present.
   startConnections: function(aSubscriptions) {
     debug("startConnections() " + aSubscriptions.length);
 
     for (let i = 0; i < aSubscriptions.length; i++) {
       let record = aSubscriptions[i];
-      if (typeof this._conns[record.subscriptionUri] != "object") {
-        this._conns[record.subscriptionUri] = {channel: null,
-                                               listener: null,
-                                               countUnableToConnect: 0,
-                                               waitingForAlarm: false};
+      if (record.p256dhPublicKey && record.p256dhPrivateKey) {
+        this._startSingleConnection(record);
+      } else {
+        // We do not have a encryption key. so we need to generate it. This
+        // is only going to happen on db upgrade from version 4 to higher.
+        PushServiceHttp2Crypto.generateKeys()
+          .then(exportedKeys => {
+            if (this._mainPushService) {
+              return this._mainPushService
+                .updateRecordAndNotifyApp(record.subscriptionUri, record => {
+                  record.p256dhPublicKey = exportedKeys[0];
+                  record.p256dhPrivateKey = exportedKeys[1];
+                  return record;
+                });
+            }
+          }, error => {
+            record = null;
+            if (this._mainPushService) {
+              this._mainPushService
+                .dropRegistrationAndNotifyApp(record.subscriptionUri);
+            }
+          })
+          .then(_ => {
+            if (record) {
+              this._startSingleConnection(record);
+            }
+          });
       }
-      if (!this._conns[record.subscriptionUri].conn) {
-        this._conns[record.subscriptionUri].waitingForAlarm = false;
-        this._listenForMsgs(record.subscriptionUri);
-      }
+    }
+  },
+
+  _startSingleConnection: function(record) {
+    debug("_startSingleConnection()");
+    if (typeof this._conns[record.subscriptionUri] != "object") {
+      this._conns[record.subscriptionUri] = {channel: null,
+                                             listener: null,
+                                             countUnableToConnect: 0,
+                                             waitingForAlarm: false};
+    }
+    if (!this._conns[record.subscriptionUri].conn) {
+      this._conns[record.subscriptionUri].waitingForAlarm = false;
+      this._listenForMsgs(record.subscriptionUri);
     }
   },
 
   // Start listening if subscriptions present.
   _startConnectionsWaitingForAlarm: function() {
     debug("startConnectionsWaitingForAlarm()");
     for (let subscriptionUri in this._conns) {
       if ((this._conns[subscriptionUri]) &&
@@ -751,50 +864,68 @@ this.PushServiceHttp2 = {
       this._resubscribe(aSubscriptionUri);
     } else if (Math.floor(aRequest.responseStatus / 100) == 2) { // This should be 204
       setTimeout(_ => this._listenForMsgs(aSubscriptionUri), 0);
     } else {
       this._retryAfterBackoff(aSubscriptionUri, -1);
     }
   },
 
-  _pushChannelOnStop: function(aUri, aAckUri, aMessage) {
+  _pushChannelOnStop: function(aUri, aAckUri, aMessage, dh, salt, rs) {
     debug("pushChannelOnStop() ");
 
-    this._mainPushService.receivedPushMessage(aUri, aMessage, record => {
-      // Always update the stored record.
-      return record;
+    this._mainPushService.getByKeyID(aUri)
+    .then(aPushRecord =>
+      PushServiceHttp2Crypto.decodeMsg(aMessage, aPushRecord.p256dhPrivateKey,
+                                       dh, salt, rs)
+      .then(msg => {
+        var msgString = '';
+        for (var i=0; i<msg.length; i++) {
+          msgString += String.fromCharCode(msg[i]);
+        }
+        return this._mainPushService.receivedPushMessage(aUri,
+                                                         msgString,
+                                                         record => {
+          // Always update the stored record.
+          return record;
+        });
+      })
+    )
+    .then(_ => this._ackMsgRecv(aAckUri))
+    .catch(err => {
+      debug("Error receiving message: " + err);
     });
-    this._ackMsgRecv(aAckUri);
   },
 
   onAlarmFired: function() {
-    // Conditions are arranged in decreasing specificity.
-    // i.e. when _waitingForPong is true, other conditions are also true.
     this._startConnectionsWaitingForAlarm();
   },
 };
 
 function PushRecordHttp2(record) {
   PushRecord.call(this, record);
   this.subscriptionUri = record.subscriptionUri;
   this.pushReceiptEndpoint = record.pushReceiptEndpoint;
+  this.p256dhPublicKey = record.p256dhPublicKey;
+  this.p256dhPrivateKey = record.p256dhPrivateKey;
 }
 
 PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
   keyID: {
     get() {
       return this.subscriptionUri;
     },
   },
 });
 
 PushRecordHttp2.prototype.toRegistration = function() {
   let registration = PushRecord.prototype.toRegistration.call(this);
   registration.pushReceiptEndpoint = this.pushReceiptEndpoint;
+  registration.p256dhKey = this.p256dhPublicKey;
   return registration;
 };
 
 PushRecordHttp2.prototype.toRegister = function() {
   let register = PushRecord.prototype.toRegister.call(this);
   register.pushReceiptEndpoint = this.pushReceiptEndpoint;
+  register.p256dhKey = this.p256dhPublicKey;
   return register;
 };
new file mode 100644
--- /dev/null
+++ b/dom/push/PushServiceHttp2Crypto.jsm
@@ -0,0 +1,189 @@
+/* jshint moz: true, esnext: true */
+/* 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/. */
+
+'use strict';
+
+const Cu = Components.utils;
+
+Cu.importGlobalProperties(['crypto']);
+
+this.EXPORTED_SYMBOLS = ['PushServiceHttp2Crypto', 'concatArray'];
+
+var ENCRYPT_INFO = new TextEncoder('utf-8').encode('Content-Encoding: aesgcm128');
+var NONCE_INFO = new TextEncoder('utf-8').encode('Content-Encoding: nonce');
+
+function chunkArray(array, size) {
+  var start = array.byteOffset || 0;
+  array = array.buffer || array;
+  var index = 0;
+  var result = [];
+  while(index + size <= array.byteLength) {
+    result.push(new Uint8Array(array, start + index, size));
+    index += size;
+  }
+  if (index < array.byteLength) {
+    result.push(new Uint8Array(array, start + index));
+  }
+  return result;
+}
+
+function base64UrlDecode(s) {
+  s = s.replace(/-/g, '+').replace(/_/g, '/');
+
+  // Replace padding if it was stripped by the sender.
+  // See http://tools.ietf.org/html/rfc4648#section-4
+  switch (s.length % 4) {
+    case 0:
+      break; // No pad chars in this case
+    case 2:
+      s += '==';
+      break; // Two pad chars
+    case 3:
+      s += '=';
+      break; // One pad char
+    default:
+      throw new Error('Illegal base64url string!');
+  }
+
+  // With correct padding restored, apply the standard base64 decoder
+  var decoded = atob(s);
+
+  var array = new Uint8Array(new ArrayBuffer(decoded.length));
+  for (var i = 0; i < decoded.length; i++) {
+    array[i] = decoded.charCodeAt(i);
+  }
+  return array;
+}
+
+this.concatArray = function(arrays) {
+  var size = arrays.reduce((total, a) => total + a.byteLength, 0);
+  var index = 0;
+  return arrays.reduce((result, a) => {
+    result.set(new Uint8Array(a), index);
+    index += a.byteLength;
+    return result;
+  }, new Uint8Array(size));
+};
+
+var HMAC_SHA256 = { name: 'HMAC', hash: 'SHA-256' };
+
+function hmac(key) {
+  this.keyPromise = crypto.subtle.importKey('raw', key, HMAC_SHA256,
+                                            false, ['sign']);
+}
+
+hmac.prototype.hash = function(input) {
+  return this.keyPromise.then(k => crypto.subtle.sign('HMAC', k, input));
+};
+
+function hkdf(salt, ikm) {
+  this.prkhPromise = new hmac(salt).hash(ikm)
+    .then(prk => new hmac(prk));
+}
+
+hkdf.prototype.generate = function(info, len) {
+  var input = concatArray([info, new Uint8Array([1])]);
+  return this.prkhPromise
+    .then(prkh => prkh.hash(input))
+    .then(h => {
+      if (h.byteLength < len) {
+        throw new Error('Length is too long');
+      }
+      return h.slice(0, len);
+    });
+};
+
+/* generate a 96-bit IV for use in GCM, 48-bits of which are populated */
+function generateNonce(base, index) {
+  if (index >= Math.pow(2, 48)) {
+    throw new Error('Error generating IV - index is too large.');
+  }
+  var nonce = base.slice(0, 12);
+  nonce = new Uint8Array(nonce);
+  for (var i = 0; i < 6; ++i) {
+    nonce[nonce.byteLength - 1 - i] ^= (index / Math.pow(256, i)) & 0xff;
+  }
+  return nonce;
+}
+
+this.PushServiceHttp2Crypto = {
+
+  generateKeys: function() {
+    return crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256'},
+                                     true,
+                                     ['deriveBits'])
+      .then(cryptoKey =>
+         Promise.all([
+           crypto.subtle.exportKey('raw', cryptoKey.publicKey),
+           // TODO: change this when bug 1048931 lands.
+           crypto.subtle.exportKey('jwk', cryptoKey.privateKey)
+         ]));
+  },
+
+  decodeMsg: function(aData, aPrivateKey, aRemotePublicKey, aSalt, aRs) {
+
+    if (aData.byteLength === 0) {
+      // Zero length messages will be passed as null.
+      return Promise.resolve(null);
+    }
+
+    // The last chunk of data must be less than aRs, if it is not return an
+    // error.
+    if (aData.byteLength % (aRs + 16) === 0) {
+      return Promise.reject(new Error('Data truncated'));
+    }
+
+    return Promise.all([
+      crypto.subtle.importKey('raw', base64UrlDecode(aRemotePublicKey),
+                              { name: 'ECDH', namedCurve: 'P-256' },
+                              false,
+                              ['deriveBits']),
+      crypto.subtle.importKey('jwk', aPrivateKey,
+                              { name: 'ECDH', namedCurve: 'P-256' },
+                              false,
+                              ['deriveBits'])
+    ])
+    .then(keys =>
+      crypto.subtle.deriveBits({ name: 'ECDH', public: keys[0] }, keys[1], 256))
+    .then(rawKey => {
+      var kdf = new hkdf(base64UrlDecode(aSalt), new Uint8Array(rawKey));
+      return Promise.all([
+        kdf.generate(ENCRYPT_INFO, 16)
+          .then(gcmBits =>
+                crypto.subtle.importKey('raw', gcmBits, 'AES-GCM', false,
+                                        ['decrypt'])),
+        kdf.generate(NONCE_INFO, 12)
+      ])
+    })
+    .then(r =>
+      // AEAD_AES_128_GCM expands ciphertext to be 16 octets longer.
+      Promise.all(chunkArray(aData, aRs + 16).map((slice, index) =>
+        this._decodeChunk(slice, index, r[1], r[0]))))
+    .then(r => concatArray(r));
+  },
+
+  _decodeChunk: function(aSlice, aIndex, aNonce, aKey) {
+    return crypto.subtle.decrypt({name: 'AES-GCM',
+                                  iv: generateNonce(aNonce, aIndex)
+                                 },
+                                 aKey, aSlice)
+      .then(decoded => {
+        decoded = new Uint8Array(decoded);
+        if (decoded.length == 0) {
+          return Promise.reject(new Error('Decoded array is too short!'));
+        } else if (decoded[0] > decoded.length) {
+          return Promise.reject(new Error ('Padding is wrong!'));
+        } else {
+          // All padded bytes must be zero except the first one.
+          for (var i = 1; i <= decoded[0]; i++) {
+            if (decoded[i] != 0) {
+              return Promise.reject(new Error('Padding is wrong!'));
+            }
+          }
+          return decoded.slice(decoded[0] + 1);
+        }
+      });
+  }
+};
--- a/dom/push/moz.build
+++ b/dom/push/moz.build
@@ -15,16 +15,17 @@ EXTRA_PP_JS_MODULES += [
 ]
 
 EXTRA_JS_MODULES += [
     'PushDB.jsm',
     'PushRecord.jsm',
     'PushService.jsm',
     'PushServiceChildPreload.jsm',
     'PushServiceHttp2.jsm',
+    'PushServiceHttp2Crypto.jsm',
 ]
 
 MOCHITEST_MANIFESTS += [
     'test/mochitest.ini',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += [
     'test/xpcshell/xpcshell.ini',
--- a/dom/push/test/xpcshell/test_notification_http2.js
+++ b/dom/push/test/xpcshell/test_notification_http2.js
@@ -41,54 +41,109 @@ function run_test() {
     'https://example.com/page/3'
   );
 
   run_next_test();
 }
 
 add_task(function* test_pushNotifications() {
 
+  // /pushNotifications/subscription1 will send a message with no rs and padding
+  // length 1.
+  // /pushNotifications/subscription2 will send a message with no rs and padding
+  // length 16.
+  // /pushNotifications/subscription3 will send a message with rs equal 24 and
+  // padding length 16.
+
   let db = PushServiceHttp2.newPushDB();
   do_register_cleanup(() => {
     return db.drop().then(_ => db.close());
   });
 
   var serverURL = "https://localhost:" + serverPort;
 
   let records = [{
     subscriptionUri: serverURL + '/pushNotifications/subscription1',
     pushEndpoint: serverURL + '/pushEndpoint1',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint1',
     scope: 'https://example.com/page/1',
+    p256dhPublicKey: 'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
+    p256dhPrivateKey: {
+      crv: 'P-256',
+      d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
+      ext: true,
+      key_ops: ["deriveBits"],
+      kty: "EC",
+      x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
+      y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
+    },
     originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     quota: Infinity,
   }, {
     subscriptionUri: serverURL + '/pushNotifications/subscription2',
     pushEndpoint: serverURL + '/pushEndpoint2',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint2',
     scope: 'https://example.com/page/2',
+    p256dhPublicKey: 'BPnWyUo7yMnuMlyKtERuLfWE8a09dtdjHSW2lpC9_BqR5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E',
+    p256dhPrivateKey: {
+      crv: 'P-256',
+      d: 'lFm4nPsUKYgNGBJb5nXXKxl8bspCSp0bAhCYxbveqT4',
+      ext: true,
+      key_ops: ["deriveBits"],
+      kty: 'EC',
+      x: '-dbJSjvIye4yXIq0RG4t9YTxrT1212MdJbaWkL38GpE',
+      y: '5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E'
+    },
     originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     quota: Infinity,
   }, {
     subscriptionUri: serverURL + '/pushNotifications/subscription3',
     pushEndpoint: serverURL + '/pushEndpoint3',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint3',
     scope: 'https://example.com/page/3',
+    p256dhPublicKey: 'BDhUHITSeVrWYybFnb7ylVTCDDLPdQWMpf8gXhcWwvaaJa6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI',
+    p256dhPrivateKey: {
+      crv: 'P-256',
+      d: 'Q1_SE1NySTYzjbqgWwPgrYh7XRg3adqZLkQPsy319G8',
+      ext: true,
+      key_ops: ["deriveBits"],
+      kty: 'EC',
+      x: 'OFQchNJ5WtZjJsWdvvKVVMIMMs91BYyl_yBeFxbC9po',
+      y: 'Ja6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI'
+    },
     originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
-    promiseObserverNotification('push-notification'),
-    promiseObserverNotification('push-notification'),
-    promiseObserverNotification('push-notification')
+    promiseObserverNotification('push-notification', function(subject, data) {
+      var notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
+      if (notification && (data == "https://example.com/page/1")){
+        equal(subject.data, "Some message", "decoded message is incorrect");
+        return true;
+      }
+    }),
+    promiseObserverNotification('push-notification', function(subject, data) {
+      var notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
+      if (notification && (data == "https://example.com/page/2")){
+        equal(subject.data, "Some message", "decoded message is incorrect");
+        return true;
+      }
+    }),
+    promiseObserverNotification('push-notification', function(subject, data) {
+      var notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
+      if (notification && (data == "https://example.com/page/3")){
+        equal(subject.data, "Some message", "decoded message is incorrect");
+        return true;
+      }
+    })
   ]);
 
   PushService.init({
     serverURI: serverURL,
     db
   });
 
   yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
--- a/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
@@ -63,16 +63,26 @@ add_task(function* test1() {
 
   var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
 
   let records = [{
     subscriptionUri: 'http://localhost/subscriptionNotExist',
     pushEndpoint: serverURL + '/pushEndpoint',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
     scope: 'https://example.com/page',
+    p256dhPublicKey: 'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
+    p256dhPrivateKey: {
+      crv: 'P-256',
+      d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
+      ext: true,
+      key_ops: ["deriveBits"],
+      kty: "EC",
+      x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
+      y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
+    },
     originAttributes: '',
     quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://testing-common/httpd.js");
+
+const {PushDB, PushService, PushServiceHttp2} = serviceExports;
+
+var httpServer = null;
+
+XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
+  return httpServer.identity.primaryPort;
+});
+
+function listenHandler(metadata, response) {
+  do_check_true(true, "Start listening");
+  httpServer.stop(do_test_finished);
+  response.setHeader("Retry-After", "10");
+  response.setStatusLine(metadata.httpVersion, 500, "Retry");
+}
+
+httpServer = new HttpServer();
+httpServer.registerPathHandler("/subscriptionNoKey", listenHandler);
+httpServer.start(-1);
+
+function run_test() {
+
+  do_get_profile();
+  setPrefs({
+    'http2.retryInterval': 1000,
+    'http2.maxRetries': 2
+  });
+  disableServiceWorkerEvents(
+    'https://example.com/page'
+  );
+
+  run_next_test();
+}
+
+add_task(function* test1() {
+
+  let db = PushServiceHttp2.newPushDB();
+  do_register_cleanup(_ => {
+    return db.drop().then(_ => db.close());
+  });
+
+  do_test_pending();
+
+  var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
+
+  let record = {
+    subscriptionUri: serverURL + '/subscriptionNoKey',
+    pushEndpoint: serverURL + '/pushEndpoint',
+    pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
+    scope: 'https://example.com/page',
+    originAttributes: '',
+    quota: Infinity,
+  };
+
+  yield db.put(record);
+
+  let notifyPromise = promiseObserverNotification('push-subscription-change',
+                                                  _ => true);
+
+  PushService.init({
+    serverURI: serverURL + "/subscribe",
+    service: PushServiceHttp2,
+    db
+  });
+
+  yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
+    'Timed out waiting for notifications');
+
+  let aRecord = yield db.getByKeyID(serverURL + '/subscriptionNoKey');
+  ok(aRecord, 'The record should still be there');
+  ok(aRecord.p256dhPublicKey, 'There should be a public key');
+  ok(aRecord.p256dhPrivateKey, 'There should be a private key');
+});
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -33,16 +33,17 @@ skip-if = toolkit == 'android'
 [test_unregister_not_found.js]
 [test_unregister_success.js]
 [test_webapps_cleardata.js]
 #http2 test
 [test_resubscribe_4xxCode_http2.js]
 [test_resubscribe_5xxCode_http2.js]
 [test_resubscribe_listening_for_msg_error_http2.js]
 [test_register_5xxCode_http2.js]
+[test_updateRecordNoEncryptionKeys.js]
 [test_register_success_http2.js]
 skip-if = !hasNode
 run-sequentially = node server exceptions dont replay well
 [test_register_error_http2.js]
 skip-if = !hasNode
 run-sequentially = node server exceptions dont replay well
 [test_unregister_success_http2.js]
 skip-if = !hasNode
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -186,8 +186,10 @@ skip-if = buildapp == 'b2g' #no ssl supp
 # no ssl support as well as websocket tests do not work (see test_websocket.html)
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
 [test_upgrade_insecure_reporting.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
 [test_upgrade_insecure_referrer.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
 [test_upgrade_insecure_cors.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
+[test_blocked_uri_in_reports.html]
+skip-if = e10s || buildapp == 'b2g' # http-on-opening-request observer not supported in child process (bug 1009632)
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_blocked_uri_in_reports.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1069762 - Check blocked-uri in csp-reports after redirect</title>
+  <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We try to load a script from:
+ *   http://example.com/tests/dom/security/test/csp/file_path_matching_redirect_server.sjs
+ * which gets redirected to:
+ *  http://test1.example.com/tests/dom/security//test/csp/file_path_matching.js
+ *
+ * The blocked-uri in the csp-report should be:
+ *   test1.example.com
+ * instead of:
+ *  http://test1.example.com/tests/com/security/test/csp/file_path_matching.js
+ *
+ * see also: http://www.w3.org/TR/CSP/#violation-reports
+ *
+ * Note, that we reuse the test-setup from
+ * test_path_matching_redirect.html
+ */
+
+const reportURI = "http://mochi.test:8888/foo.sjs";
+const policy = "script-src http://example.com; report-uri " + reportURI;
+const testfile = "tests/dom/security/test/csp/file_path_matching_redirect.html";
+
+// This is used to watch requests go out so we can see if the report is
+// sent correctly
+function examiner() {
+  SpecialPowers.addObserver(this, "http-on-opening-request", false);
+}
+examiner.prototype  = {
+  observe: function(subject, topic, data) {
+    // subject should be an nsURI
+    if (!SpecialPowers.can_QI(subject))
+      return;
+
+    if (topic === "http-on-opening-request") {
+      var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
+      if (asciiSpec !== reportURI) return;
+
+      try {
+        // Verify that the report was properly formatted.
+        // We'll parse the report text as JSON and verify that the properties
+        // have expected values.
+        var reportText = "{}";
+        var uploadStream = SpecialPowers.wrap(SpecialPowers.do_QueryInterface(subject, "nsIUploadChannel")).uploadStream;
+
+        if (uploadStream) {
+          // get the bytes from the request body
+          var binstream = SpecialPowers.Cc["@mozilla.org/binaryinputstream;1"]
+                                          .createInstance(SpecialPowers.Ci.nsIBinaryInputStream);
+          binstream.setInputStream(uploadStream);
+
+          var segments = [];
+          for (var count = uploadStream.available(); count; count = uploadStream.available()) {
+            var data = binstream.readBytes(count);
+            segments.push(data);
+          }
+
+          var reportText = segments.join("");
+          // rewind stream as we are supposed to - there will be an assertion later if we don't.
+          SpecialPowers.do_QueryInterface(uploadStream, "nsISeekableStream").seek(SpecialPowers.Ci.nsISeekableStream.NS_SEEK_SET, 0);
+        }
+        try {
+          var reportObj = JSON.parse(reportText);
+        }
+        catch (e) {
+          ok(false, "Could not parse JSON (exception: " + e + ")");
+        }
+        var cspReport = reportObj["csp-report"];
+        // blocked-uri should only be the asciiHost instead of:
+        // http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js
+        is(cspReport["blocked-uri"], "http://test1.example.com", "Incorrect blocked-uri");
+      }
+      catch (e) {
+        ok(false, "Could not query report (exception: " + e + ")");
+      }
+
+      // finish up
+      window.examiner.remove();
+      SimpleTest.finish();
+    }
+  },
+
+  // remove the listener
+  remove: function() {
+    SpecialPowers.removeObserver(this, "http-on-opening-request");
+  }
+}
+window.examiner = new examiner();
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  var src = "file_testserver.sjs";
+  // append the file that should be served
+  src += "?file=" + escape(testfile);
+  // append the CSP that should be used to serve the file
+  src += "&csp=" + escape(policy);
+
+  document.getElementById("cspframe").src = src;
+}
+
+runTest();
+
+</script>
+</body>
+</html>
--- a/dom/smil/nsSMILTimedElement.cpp
+++ b/dom/smil/nsSMILTimedElement.cpp
@@ -638,17 +638,17 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
       {
         if (mCurrentInterval->Begin()->Time() <= sampleTime) {
           mElementState = STATE_ACTIVE;
           mCurrentInterval->FixBegin();
           if (mClient) {
             mClient->Activate(mCurrentInterval->Begin()->Time().GetMillis());
           }
           if (mSeekState == SEEK_NOT_SEEKING) {
-            FireTimeEventAsync(NS_SMIL_BEGIN, 0);
+            FireTimeEventAsync(eSMILBeginEvent, 0);
           }
           if (HasPlayed()) {
             Reset(); // Apply restart behaviour
             // The call to Reset() may mean that the end point of our current
             // interval should be changed and so we should update the interval
             // now. However, calling UpdateCurrentInterval could result in the
             // interval getting deleted (perhaps through some web of syncbase
             // dependencies) therefore we make updating the interval the last
@@ -674,17 +674,17 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
             GetNextInterval(mCurrentInterval, nullptr, nullptr, newInterval)
             ? STATE_WAITING
             : STATE_POSTACTIVE;
           if (mClient) {
             mClient->Inactivate(mFillMode == FILL_FREEZE);
           }
           mCurrentInterval->FixEnd();
           if (mSeekState == SEEK_NOT_SEEKING) {
-            FireTimeEventAsync(NS_SMIL_END, 0);
+            FireTimeEventAsync(eSMILEndEvent, 0);
           }
           mCurrentRepeatIteration = 0;
           mOldIntervals.AppendElement(mCurrentInterval.forget());
           SampleFillValue();
           if (mElementState == STATE_WAITING) {
             mCurrentInterval = new nsSMILInterval(newInterval);
           }
           // We are now in a consistent state to dispatch notifications
@@ -722,17 +722,17 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
             // mCurrentRepeatIteration so we do that first before testing the
             // seek state.)
             uint32_t prevRepeatIteration = mCurrentRepeatIteration;
             if (
               ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 &&
               mCurrentRepeatIteration != prevRepeatIteration &&
               mCurrentRepeatIteration &&
               mSeekState == SEEK_NOT_SEEKING) {
-              FireTimeEventAsync(NS_SMIL_REPEAT,
+              FireTimeEventAsync(eSMILRepeatEvent,
                             static_cast<int32_t>(mCurrentRepeatIteration));
             }
           }
         }
       }
       break;
 
     case STATE_POSTACTIVE:
@@ -1513,24 +1513,24 @@ nsSMILTimedElement::DoPostSeek()
     UpdateCurrentInterval();
   }
 
   switch (mSeekState)
   {
   case SEEK_FORWARD_FROM_ACTIVE:
   case SEEK_BACKWARD_FROM_ACTIVE:
     if (mElementState != STATE_ACTIVE) {
-      FireTimeEventAsync(NS_SMIL_END, 0);
+      FireTimeEventAsync(eSMILEndEvent, 0);
     }
     break;
 
   case SEEK_FORWARD_FROM_INACTIVE:
   case SEEK_BACKWARD_FROM_INACTIVE:
     if (mElementState == STATE_ACTIVE) {
-      FireTimeEventAsync(NS_SMIL_BEGIN, 0);
+      FireTimeEventAsync(eSMILBeginEvent, 0);
     }
     break;
 
   case SEEK_NOT_SEEKING:
     /* Do nothing */
     break;
   }
 
--- a/dom/webidl/PushSubscription.webidl
+++ b/dom/webidl/PushSubscription.webidl
@@ -4,21 +4,27 @@
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * The origin of this IDL file is
 * https://w3c.github.io/push-api/
 */
 
 interface Principal;
 
+enum PushEncryptionKeyName
+{
+  "p256dh"
+};
+
 [Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled",
- ChromeConstructor(DOMString pushEndpoint, DOMString scope)]
+ ChromeConstructor(DOMString pushEndpoint, DOMString scope, ArrayBuffer? key)]
 interface PushSubscription
 {
     readonly attribute USVString endpoint;
+    ArrayBuffer? getKey(PushEncryptionKeyName name);
     [Throws, UseCounter]
     Promise<boolean> unsubscribe();
     jsonifier;
 
     // Used to set the principal from the JS implemented PushManager.
     [Exposed=Window,ChromeOnly]
     void setPrincipal(Principal principal);
 };
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1098,21 +1098,21 @@ private:
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     return aWorkerPrivate->RunExpiredTimeouts(aCx);
   }
 };
 
 class DebuggerImmediateRunnable : public WorkerRunnable
 {
-  nsRefPtr<Function> mHandler;
+  nsRefPtr<dom::Function> mHandler;
 
 public:
   explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
-                                     Function& aHandler)
+                                     dom::Function& aHandler)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
     mHandler(&aHandler)
   { }
 
 private:
   virtual bool
   IsDebuggerRunnable() const override
   {
@@ -5661,17 +5661,17 @@ WorkerPrivate::LeaveDebuggerEventLoop()
 
 void
 WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
 {
   mDebugger->PostMessageToDebugger(aMessage);
 }
 
 void
-WorkerPrivate::SetDebuggerImmediate(JSContext* aCx, Function& aHandler,
+WorkerPrivate::SetDebuggerImmediate(JSContext* aCx, dom::Function& aHandler,
                                     ErrorResult& aRv)
 {
   AssertIsOnWorkerThread();
 
   nsRefPtr<DebuggerImmediateRunnable> runnable =
     new DebuggerImmediateRunnable(this, aHandler);
   if (!runnable->Dispatch(aCx)) {
     aRv.Throw(NS_ERROR_FAILURE);
@@ -5899,17 +5899,17 @@ WorkerPrivate::ReportError(JSContext* aC
     JS_ReportPendingException(aCx);
   }
 
   mErrorHandlerRecursionCount--;
 }
 
 int32_t
 WorkerPrivate::SetTimeout(JSContext* aCx,
-                          Function* aHandler,
+                          dom::Function* aHandler,
                           const nsAString& aStringHandler,
                           int32_t aTimeout,
                           const Sequence<JS::Value>& aArguments,
                           bool aIsInterval,
                           ErrorResult& aRv)
 {
   AssertIsOnWorkerThread();
 
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -249,17 +249,17 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsI
     mRootElement = do_QueryInterface(aRoot);
 
   mUpdateCount=0;
 
   // If this is an editor for <input> or <textarea>, mIMETextNode is always
   // recreated with same content. Therefore, we need to forget mIMETextNode,
   // but we need to keep storing mIMETextOffset and mIMETextLength becuase
   // they are necessary to restore IME selection and replacing composing string
-  // when this receives NS_COMPOSITION_CHANGE event next time.
+  // when this receives eCompositionChange event next time.
   if (mIMETextNode && !mIMETextNode->IsInComposedDoc()) {
     mIMETextNode = nullptr;
   }
 
   /* Show the caret */
   selCon->SetCaretReadOnly(false);
   selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
 
@@ -5154,21 +5154,21 @@ nsEditor::IsAcceptableInputEvent(nsIDOME
   // strange event order.
   bool needsWidget = false;
   WidgetGUIEvent* widgetGUIEvent = nullptr;
   switch (widgetEvent->mMessage) {
     case eUnidentifiedEvent:
       // If events are not created with proper event interface, their message
       // are initialized with eUnidentifiedEvent.  Let's ignore such event.
       return false;
-    case NS_COMPOSITION_START:
-    case NS_COMPOSITION_END:
-    case NS_COMPOSITION_UPDATE:
-    case NS_COMPOSITION_CHANGE:
-    case NS_COMPOSITION_COMMIT_AS_IS:
+    case eCompositionStart:
+    case eCompositionEnd:
+    case eCompositionUpdate:
+    case eCompositionChange:
+    case eCompositionCommitAsIs:
       // Don't allow composition events whose internal event are not
       // WidgetCompositionEvent.
       widgetGUIEvent = aEvent->GetInternalNSEvent()->AsCompositionEvent();
       needsWidget = true;
       break;
     default:
       break;
   }
--- a/editor/libeditor/nsEditorEventListener.cpp
+++ b/editor/libeditor/nsEditorEventListener.cpp
@@ -455,23 +455,23 @@ nsEditorEventListener::HandleEvent(nsIDO
     }
     // focus
     case eFocus:
       return Focus(aEvent);
     // blur
     case eBlur:
       return Blur(aEvent);
     // text
-    case NS_COMPOSITION_CHANGE:
+    case eCompositionChange:
       return HandleText(aEvent);
     // compositionstart
-    case NS_COMPOSITION_START:
+    case eCompositionStart:
       return HandleStartComposition(aEvent);
     // compositionend
-    case NS_COMPOSITION_END:
+    case eCompositionEnd:
       HandleEndComposition(aEvent);
       return NS_OK;
     default:
       break;
   }
 
   nsAutoString eventType;
   aEvent->GetType(eventType);
--- a/editor/libeditor/nsPlaintextEditor.cpp
+++ b/editor/libeditor/nsPlaintextEditor.cpp
@@ -840,18 +840,18 @@ nsPlaintextEditor::BeginIMEComposition(W
 nsresult
 nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
 {
   MOZ_ASSERT(aDOMTextEvent, "aDOMTextEvent must not be nullptr");
 
   WidgetCompositionEvent* compositionChangeEvent =
     aDOMTextEvent->GetInternalNSEvent()->AsCompositionEvent();
   NS_ENSURE_TRUE(compositionChangeEvent, NS_ERROR_INVALID_ARG);
-  MOZ_ASSERT(compositionChangeEvent->mMessage == NS_COMPOSITION_CHANGE,
-             "The internal event should be NS_COMPOSITION_CHANGE");
+  MOZ_ASSERT(compositionChangeEvent->mMessage == eCompositionChange,
+             "The internal event should be eCompositionChange");
 
   EnsureComposition(compositionChangeEvent);
 
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
 
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
--- a/gfx/graphite2/README.mozilla
+++ b/gfx/graphite2/README.mozilla
@@ -1,6 +1,6 @@
 This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev
 
-Current version derived from upstream changeset e6539b6769cf
+Current version derived from upstream changeset 0f9edca71849
 
 See gfx/graphite2/moz-gr-update.sh for update procedure.
 
--- a/gfx/graphite2/include/graphite2/Font.h
+++ b/gfx/graphite2/include/graphite2/Font.h
@@ -25,17 +25,17 @@
     either version 2 of the License or (at your option) any later version.
 */
 #pragma once
 
 #include "graphite2/Types.h"
 
 #define GR2_VERSION_MAJOR   1
 #define GR2_VERSION_MINOR   3
-#define GR2_VERSION_BUGFIX  0
+#define GR2_VERSION_BUGFIX  2
 
 #ifdef __cplusplus
 extern "C"
 {
 #endif
 
 typedef struct gr_face          gr_face;
 typedef struct gr_font          gr_font;
--- a/gfx/graphite2/include/graphite2/Segment.h
+++ b/gfx/graphite2/include/graphite2/Segment.h
@@ -165,17 +165,18 @@ enum gr_attrCode {
     /// not implemented
     gr_slatNoEffect = gr_slatMax + 1    
 };
 
 enum gr_bidirtl {
     /// Underlying paragraph direction is RTL
     gr_rtl = 1,
     /// Set this to not run the bidi pass internally, even if the font asks for it.
-    /// This presumes that the segment is in a single direction.
+    /// This presumes that the segment is in a single direction. Most of the time
+    /// this bit should be set unless you know you are passing full paragraphs of text.
     gr_nobidi = 2,
     /// Disable auto mirroring for rtl text
     gr_nomirror = 4
 };
 
 typedef struct gr_char_info     gr_char_info;
 typedef struct gr_segment       gr_segment;
 typedef struct gr_slot          gr_slot;
--- a/gfx/graphite2/src/Bidi.cpp
+++ b/gfx/graphite2/src/Bidi.cpp
@@ -742,17 +742,17 @@ void resolveImplicit(Slot *s, Segment *s
     }
 }
 
 void resolveWhitespace(int baseLevel, Slot *s)
 {
     for ( ; s; s = s->prev())
     {
         int8 cls = s->getBidiClass();
-        if (cls == WS || cls & WSflag)
+        if (cls == WS || (cls & WSflag))
             s->setBidiLevel(baseLevel);
         else if (cls != BN)
             break;
     }
 }
 
 
 /*
--- a/gfx/graphite2/src/CMakeLists.txt
+++ b/gfx/graphite2/src/CMakeLists.txt
@@ -69,17 +69,16 @@ add_library(graphite2 SHARED
     ${GRAPHITE2_VM_TYPE}_machine.cpp
     gr_char_info.cpp
     gr_features.cpp
     gr_face.cpp
     gr_font.cpp
     gr_logging.cpp
     gr_segment.cpp
     gr_slot.cpp
-    Bidi.cpp
     CachedFace.cpp
     CmapCache.cpp
     Code.cpp
     Collider.cpp
     Decompressor.cpp
     Face.cpp
     FeatureMap.cpp
     Font.cpp
@@ -102,27 +101,24 @@ add_library(graphite2 SHARED
 
 set_target_properties(graphite2 PROPERTIES  PUBLIC_HEADER "${GRAPHITE_HEADERS}"
                                             SOVERSION ${GRAPHITE_SO_VERSION}
                                             VERSION ${GRAPHITE_VERSION}
                                             LT_VERSION_CURRENT ${GRAPHITE_API_CURRENT}
                                             LT_VERSION_REVISION ${GRAPHITE_API_REVISION}
                                             LT_VERSION_AGE ${GRAPHITE_API_AGE})
 
-if (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
-    set(GRAPHITE_LINK_FLAGS "-fsanitize=address")
-else (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
-    set(GRAPHITE_LINK_FLAGS "")
-endif (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
-
 if  (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
     set_target_properties(graphite2 PROPERTIES 
-        COMPILE_FLAGS   "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector -Wdouble-promotion"
+        COMPILE_FLAGS   "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector"
         LINK_FLAGS      "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}" 
         LINKER_LANGUAGE C)
+    if (CMAKE_COMPILER_IS_GNUCXX)
+        add_definitions(-Wdouble-promotion)
+    endif (CMAKE_COMPILER_IS_GNUCXX)
     if (${CMAKE_CXX_COMPILER} MATCHES  ".*mingw.*")
         target_link_libraries(graphite2 kernel32 msvcr90 mingw32 gcc user32)
     else (${CMAKE_CXX_COMPILER} MATCHES  ".*mingw.*")
         if (GRAPHITE2_ASAN)
             target_link_libraries(graphite2 c gcc_s)
         else (GRAPHITE2_ASAN)
             target_link_libraries(graphite2 c gcc)
         endif (GRAPHITE2_ASAN)
--- a/gfx/graphite2/src/CmapCache.cpp
+++ b/gfx/graphite2/src/CmapCache.cpp
@@ -33,31 +33,31 @@ of the License or (at your option) any l
 
 
 using namespace graphite2;
 
 const void * bmp_subtable(const Face::Table & cmap)
 {
     const void * stbl;
     if (!cmap.size()) return 0;
-    if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()))
-     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()))
-     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()))
-     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()))
-     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size())))
+    if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()), cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()), cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()), cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()), cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size()), cmap.size()))
         return stbl;
     return 0;
 }
 
 const void * smp_subtable(const Face::Table & cmap)
 {
     const void * stbl;
     if (!cmap.size()) return 0;
-    if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()))
-     || TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size())))
+    if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()), cmap.size())
+     || TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size()), cmap.size()))
         return stbl;
     return 0;
 }
 
 template <unsigned int (*NextCodePoint)(const void *, unsigned int, int *),
           uint16 (*LookupCodePoint)(const void *, unsigned int, int)>
 bool cache_subtable(uint16 * blocks[], const void * cst, const unsigned int limit)
 {
--- a/gfx/graphite2/src/Code.cpp
+++ b/gfx/graphite2/src/Code.cpp
@@ -72,30 +72,30 @@ struct context
                 referenced:1,
                 inserted:1;
     } flags;
     uint8       codeRef;
 };
 
 } // end namespace
 
-byte * Machine::Code::local_memory = 0;
 
 class Machine::Code::decoder
 {
 public:
     struct limits;
     struct analysis
     {
         uint8     slotref;
         context   contexts[256];
         byte      max_ref;
         
         analysis() : slotref(0), max_ref(0) {};
-        void set_ref(int index) throw();
+        void set_ref(int index, bool incinsert=false) throw();
+        void set_noref(int index) throw();
         void set_changed(int index) throw();
 
     };
     
     decoder(limits & lims, Code &code, enum passtype pt) throw();
     
     bool        load(const byte * bc_begin, const byte * bc_end);
     void        apply_analysis(instr * const code, instr * code_end);
@@ -141,37 +141,36 @@ inline Machine::Code::decoder::decoder(l
   _instr(code._code), _data(code._data), _max(lims), _passtype(pt),
   _stack_depth(0)
 { }
     
 
 
 Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
            uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
-           enum passtype pt, byte * & _out)
+           enum passtype pt, byte * * const _out)
  :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
     _constraint(is_constraint), _modify(false), _delete(false), _own(_out==0)
 {
 #ifdef GRAPHITE2_TELEMETRY
     telemetry::category _code_cat(face.tele.code);
 #endif
     assert(bytecode_begin != 0);
     if (bytecode_begin == bytecode_end)
     {
       // ::new (this) Code();
       return;
     }
     assert(bytecode_end > bytecode_begin);
     const opcode_t *    op_to_fn = Machine::getOpcodeTable();
     
-    // Allocate code and dat target buffers, these sizes are a worst case 
+    // Allocate code and data target buffers, these sizes are a worst case
     // estimate.  Once we know their real sizes the we'll shrink them.
-    if (_out)   _code = reinterpret_cast<instr *>(_out);
-    else        _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
-                                             * (sizeof(instr)+sizeof(byte))));
+    if (_out)   _code = reinterpret_cast<instr *>(*_out);
+    else        _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin)));
     _data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
     
     if (!_code || !_data) {
         failure(alloc_failed);
         return;
     }
     
     decoder::limits lims = {
@@ -215,17 +214,17 @@ Machine::Code::Code(bool is_constraint, 
     // Now we know exactly how much code and data the program really needs
     // realloc the buffers to exactly the right size so we don't waste any 
     // memory.
     assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_instr_count));
     assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_data_size));
     memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte));
     size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr);
     if (_out)
-        _out += total_sz;
+        *_out += total_sz;
     else
         _code = static_cast<instr *>(realloc(_code, total_sz));
    _data = reinterpret_cast<byte *>(_code + (_instr_count+1));
 
     if (!_code)
     {
         failure(alloc_failed);
         return;
@@ -413,19 +412,21 @@ opcode Machine::Code::decoder::fetch_opc
             if (valid_upto(gr_slatMax, bc[0]))
             {
                 valid_upto(_rule_length, _pre_context + int8(bc[1]));
                 valid_upto(_max.attrid[bc[0]], bc[2]);
             }
             break;
         case PUSH_IGLYPH_ATTR :// not implemented
             ++_stack_depth;
+            break;
         case POP_RET :
             if (--_stack_depth < 0)
                 failure(underfull_stack);
+            // no break
         case RET_ZERO :
         case RET_TRUE :
             break;
         case IATTR_SET :
         case IATTR_ADD :
         case IATTR_SUB :
             if (--_stack_depth < 0)
                 failure(underfull_stack);
@@ -472,62 +473,74 @@ void Machine::Code::decoder::analyse_opc
   switch (opc)
   {
     case DELETE :
       _code._delete = true;
       break;
     case PUT_GLYPH_8BIT_OBS :
     case PUT_GLYPH :
       _code._modify = true;
-      _analysis.set_changed(_analysis.slotref);
+      _analysis.set_changed(0);
+      break;
+    case ATTR_SET :
+    case ATTR_ADD :
+    case ATTR_SET_SLOT :
+    case IATTR_SET_SLOT :
+    case IATTR_SET :
+    case IATTR_ADD :
+    case IATTR_SUB :
+      _analysis.set_noref(0);
       break;
     case NEXT :
     case COPY_NEXT :
       if (!_analysis.contexts[_analysis.slotref].flags.inserted)
         ++_analysis.slotref;
       _analysis.contexts[_analysis.slotref] = context(_code._instr_count+1);
-      if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
+      // if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
       break;
     case INSERT :
       _analysis.contexts[_analysis.slotref].flags.inserted = true;
       _code._modify = true;
       break;
     case PUT_SUBS_8BIT_OBS :    // slotref on 1st parameter
     case PUT_SUBS : 
       _code._modify = true;
-      _analysis.set_changed(_analysis.slotref);
+      _analysis.set_changed(0);
       // no break
     case PUT_COPY :
     {
-      if (arg[0] != 0) { _analysis.set_changed(_analysis.slotref); _code._modify = true; }
+      if (arg[0] != 0) { _analysis.set_changed(0); _code._modify = true; }
       if (arg[0] <= 0 && -arg[0] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
-        _analysis.set_ref(_analysis.slotref + arg[0] - _analysis.contexts[_analysis.slotref].flags.inserted);
-      else if (_analysis.slotref + arg[0] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[0];
+        _analysis.set_ref(arg[0], true);
+      else if (arg[0] > 0)
+        _analysis.set_ref(arg[0], true);
       break;
     }
     case PUSH_ATT_TO_GATTR_OBS : // slotref on 2nd parameter
         if (_code._constraint) return;
         // no break
     case PUSH_GLYPH_ATTR_OBS :
     case PUSH_SLOT_ATTR :
     case PUSH_GLYPH_METRIC :
     case PUSH_ATT_TO_GLYPH_METRIC :
     case PUSH_ISLOT_ATTR :
     case PUSH_FEAT :
       if (arg[1] <= 0 && -arg[1] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
-        _analysis.set_ref(_analysis.slotref + arg[1] - _analysis.contexts[_analysis.slotref].flags.inserted);
-      else if (_analysis.slotref + arg[1] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[1];
+        _analysis.set_ref(arg[1], true);
+      else if (arg[1] > 0)
+        _analysis.set_ref(arg[1], true);
       break;
     case PUSH_ATT_TO_GLYPH_ATTR :
         if (_code._constraint) return;
         // no break
     case PUSH_GLYPH_ATTR :
       if (arg[2] <= 0 && -arg[2] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
-        _analysis.set_ref(_analysis.slotref + arg[2] - _analysis.contexts[_analysis.slotref].flags.inserted);
-      else if (_analysis.slotref + arg[2] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[2];
+        _analysis.set_ref(arg[2], true);
+      else if (arg[2] > 0)
+        _analysis.set_ref(arg[2], true);
       break;
     case ASSOC :                // slotrefs in varargs
       break;
     default:
         break;
   }
 }
 
@@ -599,33 +612,39 @@ void Machine::Code::decoder::apply_analy
     {
         if (!c->flags.referenced || !c->flags.changed) continue;
         
         instr * const tip = code + c->codeRef + tempcount;        
         memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
         *tip = temp_copy;
         ++code_end;
         ++tempcount;
+        _code._delete = true;
     }
     
     _code._instr_count = code_end - code;
 }
 
 
 inline
 bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * const bc)
 {
     if (opc >= MAX_OPCODE)
     {
         failure(invalid_opcode);
         return false;
     }
     const opcode_t & op = Machine::getOpcodeTable()[opc];
+    if (op.param_sz == VARARGS && bc >= _max.bytecode)
+    {
+        failure(arguments_exhausted);
+        return false;
+    }
     const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
-    if (bc - 1 + param_sz > _max.bytecode)
+    if (bc - 1 + param_sz >= _max.bytecode)
     {
         failure(arguments_exhausted);
         return false;
     }
     return true;
 }
 
 
@@ -649,26 +668,38 @@ bool Machine::Code::decoder::test_contex
 inline 
 void Machine::Code::failure(const status_t s) throw() {
     release_buffers();
     _status = s;
 }
 
 
 inline
-void Machine::Code::decoder::analysis::set_ref(const int index) throw() {
-    contexts[index].flags.referenced = true;
-    if (index > max_ref) max_ref = index;
+void Machine::Code::decoder::analysis::set_ref(int index, bool incinsert) throw() {
+    if (incinsert && contexts[slotref].flags.inserted) --index;
+    if (index + slotref < 0) return;
+    contexts[index + slotref].flags.referenced = true;
+    if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
 }
 
 
 inline
-void Machine::Code::decoder::analysis::set_changed(const int index) throw() {
-    contexts[index].flags.changed = true;
-    if (index > max_ref) max_ref = index;
+void Machine::Code::decoder::analysis::set_noref(int index) throw() {
+    if (contexts[slotref].flags.inserted) --index;
+    if (index + slotref < 0) return;
+    if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
+}
+
+
+inline
+void Machine::Code::decoder::analysis::set_changed(int index) throw() {
+    if (contexts[slotref].flags.inserted) --index;
+    if (index + slotref < 0) return;
+    contexts[index + slotref].flags.changed = true;
+    if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
 }
 
 
 void Machine::Code::release_buffers() throw()
 {
     if (_own)
         free(_code);
     _code = 0;
@@ -677,17 +708,19 @@ void Machine::Code::release_buffers() th
 }
 
 
 int32 Machine::Code::run(Machine & m, slotref * & map) const
 {
 //    assert(_own);
     assert(*this);          // Check we are actually runnable
 
-    if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()))
+    if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context())
+        || m.slotMap()[_max_ref + m.slotMap().context()] == 0)
     {
         m._status = Machine::slot_offset_out_bounds;
         return 1;
+//        return m.run(_code, _data, map);
     }
 
     return  m.run(_code, _data, map);
 }
 
--- a/gfx/graphite2/src/Collider.cpp
+++ b/gfx/graphite2/src/Collider.cpp
@@ -34,17 +34,17 @@ of the License or (at your option) any l
 #include "inc/Slot.h"
 #include "inc/GlyphCache.h"
 #include "inc/Sparse.h"
 
 #define ISQRT2 0.707106781f
 
 // Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4 
 // (values in font range from 0..256)
-#define SUBBOX_RND_ERR 0.016
+// #define SUBBOX_RND_ERR 0.016
 
 using namespace graphite2;
 
 ////    SHIFT-COLLIDER    ////
 
 // Initialize the Collider to hold the basic movement limits for the
 // target slot, the one we are focusing on fixing.
 bool ShiftCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, float marginWeight,
@@ -538,17 +538,17 @@ bool ShiftCollider::mergeSlot(Segment *s
                 _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0,
                                         sqr(_margin - otmin + omax) * _marginWt, false);
             else
                 _ranges[i].exclude_with_margins(vmin, vmax, i);
 
         }
     }
     bool res = true;
-    if (cslot && cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
+    if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
     {
         // Set up the bogus slot representing the exclusion glyph.
         Slot *exclSlot = seg->newSlot();
         exclSlot->setGlyph(seg, cslot->exclGlyph());
         Position exclOrigin(slot->origin() + cslot->exclOffset());
         exclSlot->origin(exclOrigin);
         res &= mergeSlot(seg, exclSlot, currShift, isAfter, sameCluster, isCol, true, dbgout );
         seg->freeSlot(exclSlot);
@@ -920,16 +920,18 @@ bool KernCollider::initSlot(Segment *seg
 
 // Determine how much the target slot needs to kern away from the given slot.
 // In other words, merge information from given slot's position with what the target slot knows
 // about how it can kern.
 // Return false if we know there is no collision, true if we think there might be one.
 bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout)
 {
     int rtl = (dir & 1) * 2 - 1;
+    if (!seg->getFace()->glyphs().check(slot->gid()))
+        return false;
     const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid());
     const float sx = slot->origin().x + currShift.x;
     float x = sx + (rtl > 0 ? bb.tr.x : bb.bl.x);
     // this isn't going to reduce _mingap so skip
     if ((rtl > 0 && x < _xbound - _mingap - currSpace) || (rtl <= 0 && x > _xbound + _mingap + currSpace))
         return false;
 
     const float sy = slot->origin().y + currShift.y;
@@ -966,17 +968,16 @@ bool KernCollider::mergeSlot(Segment *se
         }
     }
     return collides;   // note that true is not a necessarily reliable value
     
 }   // end of KernCollider::mergeSlot
 
 
 // Return the amount to kern by.
-// TODO: do we need to make use of marginMin here? Probably not.
 Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot,
         int dir, float margin, GR_MAYBE_UNUSED json * const dbgout)
 {
     float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin);
     float result = min(_limit.tr.x - _offsetPrev.x, max(resultNeeded, _limit.bl.x - _offsetPrev.x));
 
 #if !defined GRAPHITE2_NTRACING
     if (dbgout)
--- a/gfx/graphite2/src/Decompressor.cpp
+++ b/gfx/graphite2/src/Decompressor.cpp
@@ -1,102 +1,107 @@
-/*  Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
-    Copyright (c) 2015, SIL International
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2015, SIL International
     All rights reserved.
-    
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions are met:
-    
-    1. Redistributions of source code must retain the above copyright notice,
-       this list of conditions and the following disclaimer.
-    
-    2. Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-    
-    3. Neither the name of the copyright holder nor the names of its
-       contributors may be used to endorse or promote products derived from
-       this software without specific prior written permission.
-    
-    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
-    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
-    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
-    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
-    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
-    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
-    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
-    POSSIBILITY OF SUCH DAMAGE.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street, 
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
 */
 #include <cassert>
 
 #include "inc/Decompressor.h"
-#include "inc/Shrinker.h"
+#include "inc/Compression.h"
 
-using namespace shrinker;
+using namespace lz4;
 
 namespace {
 
-u8 const LONG_DIST = 0x10;
-u8 const MATCH_LEN = 0x0f;
-
-template <int M>
 inline
 u32 read_literal(u8 const * &s, u8 const * const e, u32 l) {
-    if (unlikely(l == M))
+    if (unlikely(l == 15) && likely(s != e))
     {
-        u8 b = 0; 
+        u8 b = 0;
         do { l += b = *s++; } while(b==0xff && s != e);
     }
     return l;
 }
 
-bool read_directive(u8 const * &src, u8 const * const end, u32 & literal_len, u32 & match_len, u32 & match_dist)
+bool read_sequence(u8 const * &src, u8 const * const end, u8 const * &literal, u32 & literal_len, u32 & match_len, u32 & match_dist)
 {
-    u8 const flag = *src++;
+    u8 const token = *src++;
+    
+    literal_len = read_literal(src, end, token >> 4);
+    literal = src;
+    src += literal_len;
     
-    literal_len = read_literal<7>(src, end, flag >> 5);
-    match_len = read_literal<15>(src, end, flag & MATCH_LEN);
+    if (unlikely(src > end - 2))
+        return false;
     
-    match_dist = *src++;
-    if (flag & LONG_DIST) 
-        match_dist |= ((*src++) << 8);
+    match_dist  = *src++;
+    match_dist |= *src++ << 8;
+    match_len = read_literal(src, end, token & 0xf);
     
-    return match_dist != 0xffff;
+    return true;
 }
 
 }
 
-int shrinker::decompress(void const *in, size_t in_size, void *out, size_t out_size)
+int lz4::decompress(void const *in, size_t in_size, void *out, size_t out_size)
 {
+    if (out_size <= in_size)
+        return -1;
+
     u8 const *       src     = static_cast<u8 const *>(in),
+             *       literal = 0,
              * const src_end = src + in_size;
 
     u8 *       dst     = static_cast<u8*>(out),
        * const dst_end = dst + out_size;
     
     u32 literal_len = 0,
         match_len = 0,
         match_dist = 0;
-        
-    while (read_directive(src, src_end, literal_len, match_len, match_dist))
+    
+    while (read_sequence(src, src_end, literal, literal_len, match_len, match_dist))
     {
-        // Copy in literal
-        if (unlikely(dst + literal_len + sizeof(unsigned long) > dst_end)) return -1;
-        dst = memcpy_nooverlap(dst, src, literal_len);
-        src += literal_len;
-        
+        // Copy in literal. At this point the last full sequence must be at
+        // least MINMATCH + 5 from the end of the output buffer.
+        if (unlikely(literal + align(literal_len) > src_end
+                  || dst + align(literal_len) > dst_end - MINMATCH+5))
+            return -1;
+        dst = overrun_copy(dst, literal, literal_len);
+
         // Copy, possibly repeating, match from earlier in the
         //  decoded output.
-        u8 const * const pcpy = dst - match_dist - 1;
-        if (unlikely(pcpy < static_cast<u8*>(out) 
-                  || dst + match_len + MINMATCH  + sizeof(unsigned long) > dst_end)) return -1;
-        dst = memcpy_(dst, pcpy, match_len + MINMATCH);
+        u8 const * const pcpy = dst - match_dist;
+        if (unlikely(pcpy < static_cast<u8*>(out)
+                  || dst + align(match_len + MINMATCH) > dst_end))
+            return -1;
+        dst = copy(dst, pcpy, match_len + MINMATCH);
     }
     
-    if (unlikely(dst + literal_len > dst_end)) return -1;
-    dst = memcpy_nooverlap_surpass(dst, src, literal_len);
+    if (unlikely(literal + literal_len > src_end
+              || dst + literal_len > dst_end)) 
+        return -1;
+    dst = fast_copy(dst, literal, literal_len);
     
     return dst - (u8*)out;
 }
 
--- a/gfx/graphite2/src/Face.cpp
+++ b/gfx/graphite2/src/Face.cpp
@@ -41,17 +41,17 @@ of the License or (at your option) any l
 
 using namespace graphite2;
 
 namespace
 {
 enum compression
 {
     NONE,
-    SHRINKER
+    LZ4
 };
 
 }
 
 Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
 : m_appFaceHandle(appFaceHandle),
   m_pFileFace(NULL),
   m_pGlyphFaceCache(NULL),
@@ -120,17 +120,17 @@ bool Face::readGlyphs(uint32 faceOptions
 bool Face::readGraphite(const Table & silf)
 {
 #ifdef GRAPHITE2_TELEMETRY
     telemetry::category _silf_cat(tele.silf);
 #endif
     Error e;
     error_context(EC_READSILF);
     const byte * p = silf;
-    if (e.test(!p, E_NOSILF)) return error(e);
+    if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
 
     const uint32 version = be::read<uint32>(p);
     if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
     if (version >= 0x00030000)
         be::skip<uint32>(p);        // compilerVersion
     m_numSilf = be::read<uint16>(p);
 
     be::skip<uint16>(p);            // reserved
@@ -168,28 +168,33 @@ bool Face::runGraphite(Segment *seg, con
     if (dbgout)
     {
         *dbgout << json::object
                     << "id"         << objectid(seg)
                     << "passes"     << json::array;
     }
 #endif
 
+//    if ((seg->dir() & 1) != aSilf->dir())
+//        seg->reverseSlots();
+    if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
+        seg->doMirror(aSilf->aMirror());
     bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
     if (res)
     {
         seg->associateChars(0, seg->charInfoCount());
         if (aSilf->flags() & 0x20)
             res &= seg->initCollisions();
         res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
     }
 
 #if !defined GRAPHITE2_NTRACING
     if (dbgout)
 {
+        seg->positionSlots(0, 0, 0, aSilf->dir());
         *dbgout             << json::item
                             << json::close // Close up the passes array
                 << "output" << json::array;
         for(Slot * s = seg->first(); s; s = s->next())
             *dbgout     << dslot(seg, s);
         seg->finalise(0);                   // Call this here to fix up charinfo back indexes.
         *dbgout         << json::close
                 << "advance" << seg->advance()
@@ -228,17 +233,19 @@ uint16 Face::findPseudo(uint32 uid) cons
 }
 
 uint16 Face::getGlyphMetric(uint16 gid, uint8 metric) const
 {
     switch (metrics(metric))
     {
         case kgmetAscent : return m_ascent;
         case kgmetDescent : return m_descent;
-        default: return glyphs().glyph(gid)->getMetric(metric);
+        default: 
+            if (gid > glyphs().numGlyphs()) return 0;
+            return glyphs().glyph(gid)->getMetric(metric);
     }
 }
 
 void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
 {
 #ifndef GRAPHITE2_NFILEFACE
     if (m_pFileFace==pFileFace)
       return;
@@ -272,66 +279,81 @@ Face::Table::Table(const Face & face, co
 {
     size_t sz = 0;
     _p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz));
     _sz = uint32(sz);
 
     if (!TtfUtil::CheckTable(n, _p, _sz))
     {
         this->~Table();     // Make sure we release the table buffer even if the table filed it's checks
-        _p = 0; _sz = 0;
         return;
     }
 
     if (be::peek<uint32>(_p) >= version)
         decompress();
 }
 
+void Face::Table::releaseBuffers()
+{
+    if (_compressed)
+        free(const_cast<byte *>(_p));
+    else if (_p && _f->m_ops.release_table)
+        (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
+    _p = 0; _sz = 0;
+}
+
 Face::Table & Face::Table::operator = (const Table & rhs) throw()
 {
     if (_p == rhs._p)   return *this;
 
     this->~Table();
     new (this) Table(rhs);
     return *this;
 }
 
 Error Face::Table::decompress()
 {
     Error e;
+    if (e.test(_sz < 2 * sizeof(uint32) + 3, E_BADSIZE))
+        return e;
     byte * uncompressed_table = 0;
     size_t uncompressed_size = 0;
 
     const byte * p = _p;
     const uint32 version = be::read<uint32>(p);    // Table version number.
 
     // The scheme is in the top 5 bits of the 1st uint32.
     const uint32 hdr = be::read<uint32>(p);
     switch(compression(hdr >> 27))
     {
     case NONE: return e;
-    case SHRINKER:
+
+    case LZ4:
     {
         uncompressed_size  = hdr & 0x07ffffff;
         uncompressed_table = gralloc<byte>(uncompressed_size);
+        //TODO: Coverty: 1315803: FORWARD_NULL
         if (!e.test(!uncompressed_table, E_OUTOFMEM))
-            e.test(shrinker::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
+            //TODO: Coverty: 1315800: CHECKED_RETURN
+            e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
         break;
     }
+
     default:
         e.error(E_BADSCHEME);
     };
 
     // Check the uncompressed version number against the original.
     if (!e)
+        //TODO: Coverty: 1315800: CHECKED_RETURN
         e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
 
     // Tell the provider to release the compressed form since were replacing
     //   it anyway.
-    this->~Table();
+    releaseBuffers();
 
     if (e)
     {
         free(uncompressed_table);
         uncompressed_table = 0;
         uncompressed_size  = 0;
     }
 
--- a/gfx/graphite2/src/FeatureMap.cpp
+++ b/gfx/graphite2/src/FeatureMap.cpp
@@ -126,37 +126,38 @@ bool FeatureMap::readFeats(const Face & 
     unsigned short bits = 0;     //to cause overflow on first Feature
 
     for (int i = 0, ie = m_numFeats; i != ie; i++)
     {
         const uint32    label   = version < 0x00020000 ? be::read<uint16>(p) : be::read<uint32>(p);
         const uint16    num_settings = be::read<uint16>(p);
         if (version >= 0x00020000)
             be::skip<uint16>(p);
-        const byte * const feat_setts = feat_start + be::read<uint32>(p);
+        const uint32    settings_offset = be::read<uint32>(p);
         const uint16    flags  = be::read<uint16>(p),
                         uiName = be::read<uint16>(p);
 
-        if (feat_setts + num_settings * FEATURE_SETTING_SIZE > feat_end)
+        if (settings_offset > size_t(feat_end - feat_start) 
+            || settings_offset + num_settings * FEATURE_SETTING_SIZE > size_t(feat_end - feat_start))
         {
             free(defVals);
             return false;
         }
 
         FeatureSetting *uiSet;
         uint32 maxVal;
         if (num_settings != 0)
         {
             uiSet = gralloc<FeatureSetting>(num_settings);
             if (!uiSet)
             {
                 free(defVals);
                 return false;
             }
-            maxVal = readFeatureSettings(feat_setts, uiSet, num_settings);
+            maxVal = readFeatureSettings(feat_start + settings_offset, uiSet, num_settings);
             defVals[i] = uiSet[0].value();
         }
         else
         {
             uiSet = 0;
             maxVal = 0xffffffff;
             defVals[i] = 0;
         }
--- a/gfx/graphite2/src/FileFace.cpp
+++ b/gfx/graphite2/src/FileFace.cpp
@@ -78,17 +78,17 @@ const void *FileFace::get_table_fn(const
     if (appFaceHandle == 0)     return 0;
     const FileFace & file_face = *static_cast<const FileFace *>(appFaceHandle);
 
     void *tbl;
     size_t tbl_offset, tbl_len;
     if (!TtfUtil::GetTableInfo(name, file_face._header_tbl, file_face._table_dir, tbl_offset, tbl_len))
         return 0;
 
-    if (tbl_offset + tbl_len > file_face._file_len
+    if (tbl_offset > file_face._file_len || tbl_len > file_face._file_len - tbl_offset
             || fseek(file_face._file, tbl_offset, SEEK_SET) != 0)
         return 0;
 
     tbl = malloc(tbl_len);
     if (fread(tbl, 1, tbl_len, file_face._file) != tbl_len)
     {
         free(tbl);
         return 0;
--- a/gfx/graphite2/src/GlyphCache.cpp
+++ b/gfx/graphite2/src/GlyphCache.cpp
@@ -57,33 +57,34 @@ namespace
             if (_n == run()) advance_entry();
             return *this;
         }
         _glat_iterator<W>   operator ++ (int)   { _glat_iterator<W> tmp(*this); operator++(); return tmp; }
 
         // This is strictly a >= operator. A true == operator could be
         // implemented that test for overlap but it would be more expensive a
         // test.
-        bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e; }
+        bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e - 1; }
         bool operator != (const _glat_iterator<W> & rhs) { return !operator==(rhs); }
 
         value_type          operator * () const {
             return value_type(key(), be::peek<uint16>(_v));
         }
 
     protected:
         const byte     * _e, * _v;
         size_t        _n;
     };
 
     typedef _glat_iterator<uint8>   glat_iterator;
     typedef _glat_iterator<uint16>  glat2_iterator;
 }
 
-const Rect GlyphCache::nullRect = Rect();
+const SlantBox SlantBox::empty = {0,0,0,0};
+
 
 class GlyphCache::Loader
 {
 public:
     Loader(const Face & face, const bool dumb_font);    //return result indicates success. Do not use if failed.
 
     operator bool () const throw();
     unsigned short int units_per_em() const throw();
@@ -140,17 +141,17 @@ GlyphCache::GlyphCache(const Face & face
 
         if (!loaded)
         {
             _glyphs[0] = 0;
             delete [] glyphs;
         }
         else if (numsubs > 0)
         {
-            GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + (numsubs-1) * 8 * sizeof(float));
+            GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float));
             GlyphBox * currbox = boxes;
 
             for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid)
             {
                 _boxes[gid] = currbox;
                 currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]);
             }
             if (!currbox)
@@ -272,29 +273,30 @@ GlyphCache::Loader::Loader(const Face & 
         const byte    * p = m_pGloc;
         int       version = be::read<uint32>(p);
         const uint16    flags = be::read<uint16>(p);
         _num_attrs = be::read<uint16>(p);
         // We can accurately calculate the number of attributed glyphs by
         //  subtracting the length of the attribids array (numAttribs long if present)
         //  and dividing by either 2 or 4 depending on shor or lonf format
         _long_fmt              = flags & 1;
-        _num_glyphs_attributes = (m_pGloc.size()
+        int tmpnumgattrs       = (m_pGloc.size()
                                    - (p - m_pGloc)
                                    - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
                                        / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
 
-        if (version >= 0x00020000
+        if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
             || _num_attrs == 0 || _num_attrs > 0x3000  // is this hard limit appropriate?
-            || _num_glyphs_graphics > _num_glyphs_attributes)
+            || _num_glyphs_graphics > tmpnumgattrs)
         {
             _head = Face::Table();
             return;
         }
 
+        _num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
         p = m_pGlat;
         version = be::read<uint32>(p);
         if (version >= 0x00040000)       // reject Glat tables that are too new
         {
             _head = Face::Table();
             return;
         }
         _has_boxes = (version == 0x00030000);
@@ -342,18 +344,22 @@ const GlyphFace * GlyphCache::Loader::re
         unsigned int nAdvWid;
         if (_glyf)
         {
             int xMin, yMin, xMax, yMax;
             size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head);
             void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size());
 
             if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax))
+            {
+                if ((xMin > xMax) || (yMin > yMax))
+                    return 0;
                 bbox = Rect(Position(static_cast<float>(xMin), static_cast<float>(yMin)),
                     Position(static_cast<float>(xMax), static_cast<float>(yMax)));
+            }
         }
         if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid))
             advance = Position(static_cast<float>(nAdvWid), 0);
     }
 
     if (glyphid < _num_glyphs_attributes)
     {
         const byte * gloc = m_pGloc;
@@ -393,17 +399,18 @@ const GlyphFace * GlyphCache::Loader::re
             if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16)
                 || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16)))
                     return 0;
             new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce));
         }
         else
         {
             if (gloce - glocs < 3*sizeof(uint16)        // can a glyph have no attributes? why not?
-                || gloce - glocs > _num_attrs*3*sizeof(uint16))
+                || gloce - glocs > _num_attrs*3*sizeof(uint16)
+                || glocs > m_pGlat.size() - 2*sizeof(uint16))
                     return 0;
             new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));
         }
         if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs)
             return 0;
     }
     return &glyph;
 }
--- a/gfx/graphite2/src/Justifier.cpp
+++ b/gfx/graphite2/src/Justifier.cpp
@@ -26,17 +26,17 @@ of the License or (at your option) any l
 */
 
 #include "inc/Segment.h"
 #include "graphite2/Font.h"
 #include "inc/debug.h"
 #include "inc/CharInfo.h"
 #include "inc/Slot.h"
 #include "inc/Main.h"
-#include <math.h>
+#include <cmath>
 
 using namespace graphite2;
 
 class JustifyTotal {
 public:
     JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
     void accumulate(Slot *s, Segment *seg, int level);
     int weight() const { return m_tWeight; }
@@ -65,16 +65,23 @@ float Segment::justify(Slot *pSlot, cons
     Slot *s, *end;
     float currWidth = 0.0;
     const float scale = font ? font->scale() : 1.0f;
     Position res;
 
     if (width < 0 && !(silf()->flags()))
         return width;
 
+    if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
+    {
+        reverseSlots();
+        s = pFirst;
+        pFirst = pLast;
+        pLast = s;
+    }
     if (!pFirst) pFirst = pSlot;
     while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
     if (!pLast) pLast = last();
     while (!pLast->isBase()) pLast = pLast->attachedTo();
     const float base = pFirst->origin().x / scale;
     width = width / scale;
     if ((jflags & gr_justEndInline) == 0)
     {
@@ -165,17 +172,17 @@ float Segment::justify(Slot *pSlot, cons
                     error += diffpw * w - actual;
                     if (i == 0)
                         s->just(s->just() + actual);
                     else
                         s->setJustify(this, i, 4, actual);
                 }
             }
             currWidth += diff - error;
-        } while (i == 0 && int(abs(error)) > 0 && tWeight);
+        } while (i == 0 && int(std::abs(error)) > 0 && tWeight);
     }
 
     Slot *oldFirst = m_first;
     Slot *oldLast = m_last;
     if (silf()->flags() & 1)
     {
         m_first = pSlot = addLineEnd(pSlot);
         m_last = pLast = addLineEnd(end);
@@ -198,34 +205,37 @@ float Segment::justify(Slot *pSlot, cons
 
     if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
         m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
 
 #if !defined GRAPHITE2_NTRACING
     if (dbgout)
     {
         *dbgout     << json::item << json::close; // Close up the passes array
-        positionSlots(NULL, pSlot, pLast);
+        positionSlots(NULL, pSlot, pLast, m_dir);
         Slot *lEnd = pLast->nextSibling();
         *dbgout << "output" << json::array;
         for(Slot * t = pSlot; t != lEnd; t = t->next())
             *dbgout     << dslot(this, t);
         *dbgout         << json::close << json::close;
     }
 #endif
 
-    res = positionSlots(font, pSlot, pLast);
+    res = positionSlots(font, pSlot, pLast, m_dir);
 
     if (silf()->flags() & 1)
     {
         delLineEnd(m_first);
         delLineEnd(m_last);
     }
     m_first = oldFirst;
     m_last = oldLast;
+
+    if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
+        reverseSlots();
     return res.x;
 }
 
 Slot *Segment::addLineEnd(Slot *nSlot)
 {
     Slot *eSlot = newSlot();
     if (!eSlot) return NULL;
     const uint16 gid = silf()->endLineGlyphid();
--- a/gfx/graphite2/src/Pass.cpp
+++ b/gfx/graphite2/src/Pass.cpp
@@ -37,37 +37,48 @@ of the License or (at your option) any l
 #include "inc/Rule.h"
 #include "inc/Error.h"
 #include "inc/Collider.h"
 
 using namespace graphite2;
 using vm::Machine;
 typedef Machine::Code  Code;
 
+enum KernCollison
+{
+    None       = 0,
+    CrossSpace = 1,
+    InWord     = 2,
+    reserved   = 3
+};
 
 Pass::Pass()
 : m_silf(0),
   m_cols(0),
   m_rules(0),
   m_ruleMap(0),
   m_startStates(0),
   m_transitions(0),
   m_states(0),
   m_codes(0),
   m_progs(0),
-  m_flags(0),
+  m_numCollRuns(0),
+  m_kernColls(0),
   m_iMaxLoop(0),
   m_numGlyphs(0),
   m_numRules(0),
   m_numStates(0),
   m_numTransition(0),
   m_numSuccess(0),
+  m_successStart(0),
   m_numColumns(0),
   m_minPreCtxt(0),
-  m_maxPreCtxt(0)
+  m_maxPreCtxt(0),
+  m_colThreshold(0),
+  m_isReverseDir(false)
 {
 }
 
 Pass::~Pass()
 {
     free(m_cols);
     free(m_startStates);
     free(m_transitions);
@@ -83,23 +94,27 @@ bool Pass::readPass(const byte * const p
         GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e)
 {
     const byte * p              = pass_start,
                * const pass_end = p + pass_length;
     size_t numRanges;
 
     if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); 
     // Read in basic values
-    m_flags = be::read<byte>(p);
-    if (e.test((m_flags & 15) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS))
+    const byte flags = be::read<byte>(p);
+    if (e.test((flags & 0x1f) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS))
         return face.error(e);
+    m_numCollRuns = flags & 0x7;
+    m_kernColls   = (flags >> 3) & 0x3;
+    m_isReverseDir = (flags >> 5) & 0x1;
     m_iMaxLoop = be::read<byte>(p);
+    if (m_iMaxLoop < 1) m_iMaxLoop = 1;
     be::skip<byte>(p,2); // skip maxContext & maxBackup
     m_numRules = be::read<uint16>(p);
-    if (e.test(!m_numRules && !(m_flags & 7), E_BADEMPTYPASS)) return face.error(e);
+    if (e.test(!m_numRules && m_numCollRuns == 0, E_BADEMPTYPASS)) return face.error(e);
     be::skip<uint16>(p);   // fsmOffset - not sure why we would want this
     const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
                * const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
                * const aCode  = pass_start + be::read<uint32>(p) - subtable_base;
     be::skip<uint32>(p);
     m_numStates = be::read<uint16>(p);
     m_numTransition = be::read<uint16>(p);
     m_numSuccess = be::read<uint16>(p);
@@ -147,17 +162,17 @@ bool Pass::readPass(const byte * const p
     if (m_colThreshold == 0) m_colThreshold = 10;       // A default
     const size_t pass_constraint_len = be::read<uint16>(p);
 
     const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
     be::skip<uint16>(p, m_numRules + 1);
     const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
     be::skip<uint16>(p, m_numRules + 1);
     const byte * const states = p;
-    if (e.test(p + 2 * m_numTransition*m_numColumns >= pass_end, E_BADPASSLENGTH)) return face.error(e);
+    if (e.test(p + 2u*m_numTransition*m_numColumns >= pass_end, E_BADPASSLENGTH)) return face.error(e);
     be::skip<int16>(p, m_numTransition*m_numColumns);
     be::skip<uint8>(p);
     if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e);
     be::skip<byte>(p, pass_constraint_len);
     if (e.test(p != rcCode, E_BADRULECCODEPTR)
         || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e);
     be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
     if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e);
@@ -207,19 +222,20 @@ bool Pass::readRules(const byte * rule_m
     // Load rules.
     const byte * ac_begin = 0, * rc_begin = 0,
                * ac_end = ac_data + be::peek<uint16>(o_action),
                * rc_end = rc_data + be::peek<uint16>(o_constraint);
 
     // Allocate pools
     m_rules = new Rule [m_numRules];
     m_codes = new Code [m_numRules*2];
-    m_progs = static_cast<byte *>(malloc((ac_end - ac_data + rc_end - rc_data)
-                                    *(sizeof(vm::instr)+sizeof(byte))));
-    byte * prog_pool_free = m_progs;
+    const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data);
+    m_progs = gralloc<byte>(prog_pool_sz);
+    byte * prog_pool_free = m_progs,
+         * prog_pool_end  = m_progs + prog_pool_sz;
     if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
 
     Rule * r = m_rules + m_numRules - 1;
     for (size_t n = m_numRules; n; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
     {
         face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + ((n - 1) << 24));
         r->preContext = *--precontext;
         r->sort       = be::peek<uint16>(--sort_key);
@@ -228,41 +244,44 @@ bool Pass::readRules(const byte * rule_m
 #endif
         if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt)
             return false;
         ac_begin      = ac_data + be::peek<uint16>(--o_action);
         --o_constraint;
         rc_begin      = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end;
 
         if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
-                || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end)
+                || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end
+                || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin) > size_t(prog_pool_end - prog_pool_free))
             return false;
-        r->action     = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free);
-        r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free);
+        r->action     = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
+        r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
 
         if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
                 || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
                 || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE)
                 || e.test(!r->constraint->immutable(), E_MUTABLECCODE))
             return face.error(e);
     }
 
-    // Shrink the program pool
-    ptrdiff_t const delta = static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs)) - m_progs;
-    if (delta)
+    byte * moved_progs = static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs));
+    if (e.test(!moved_progs, E_OUTOFMEM))   return face.error(e);
+
+    if (moved_progs != m_progs)
     {
-        m_progs += delta;
         for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
         {
-            c->externalProgramMoved(delta);
+            c->externalProgramMoved(moved_progs - m_progs);
         }
+        m_progs = moved_progs;
     }
 
     // Load the rule entries map
     face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
+    //TODO: Coverty: 1315804: FORWARD_NULL
     RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries);
     if (e.test(!re, E_OUTOFMEM)) return face.error(e);
     for (size_t n = num_entries; n; --n, ++re)
     {
         const ptrdiff_t rn = be::read<uint16>(rule_map);
         if (e.test(rn >= m_numRules, E_BADRULENUM))  return face.error(e);
         re->rule = m_rules + rn;
     }
@@ -355,20 +374,21 @@ bool Pass::readRanges(const byte * range
 
         if (e.test(ci != ci_end, E_BADRANGE))
             return false;
     }
     return true;
 }
 
 
-bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const
+bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const
 {
     Slot *s = m.slotMap().segment.first();
     if (!s || !testPassConstraint(m)) return true;
+    if (reverse) m.slotMap().segment.reverseSlots();
     if (m_numRules)
     {
         Slot *currHigh = s->next();
 
 #if !defined GRAPHITE2_NTRACING
         if (fsm.dbgout)  *fsm.dbgout << "rules" << json::array;
         json::closer rules_array_closer(fsm.dbgout);
 #endif
@@ -382,33 +402,35 @@ bool Pass::runGraphite(vm::Machine & m, 
                 if (!lc)
                     s = m.slotMap().highwater();
                 lc = m_iMaxLoop;
                 if (s)
                     m.slotMap().highwater(s->next());
             }
         } while (s);
     }
+    //TODO: Use enums for flags
+    const bool collisions = m_numCollRuns || m_kernColls;
 
-    if (!(m_flags & 15) || !m.slotMap().segment.hasCollisionInfo())
+    if (!collisions || !m.slotMap().segment.hasCollisionInfo())
         return true;
 
-    if (m_flags & 7)
+    if (m_numCollRuns)
     {
         if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
         {
-            m.slotMap().segment.positionSlots(0, 0, 0, true);
+            m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true);
 //            m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
         }
-        if (!collisionShift(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
+        if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
             return false;
     }
-    if ((m_flags & 24) && !collisionKern(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
+    if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
         return false;
-    if ((m_flags & 15) && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
+    if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
         return false;
     return true;
 }
 
 bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const
 {
     fsm.reset(slot, m_maxPreCtxt);
     if (fsm.slots.context() < m_minPreCtxt)
@@ -473,18 +495,18 @@ void Pass::findNDoRule(Slot * & slot, Ma
         {
             if (fsm.rules.size() != 0)
             {
                 *fsm.dbgout << json::item << json::object;
                 dumpRuleEventConsidered(fsm, *r);
                 if (r != re)
                 {
                     const int adv = doAction(r->rule->action, slot, m);
-                    dumpRuleEventOutput(fsm, *r->rule, slot);
-                    if (r->rule->action->deletes()) fsm.slots.collectGarbage();
+                    dumpRuleEventOutput(fsm, m, *r->rule, slot);
+                    if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
                     adjustSlot(adv, slot, fsm.slots);
                     *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot))
                             << json::close; // Close RuelEvent object
 
                     return;
                 }
                 else
                 {
@@ -496,17 +518,17 @@ void Pass::findNDoRule(Slot * & slot, Ma
             }
         }
         else
 #endif
         {
             if (r != re)
             {
                 const int adv = doAction(r->rule->action, slot, m);
-                if (r->rule->action->deletes()) fsm.slots.collectGarbage();
+                if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
                 adjustSlot(adv, slot, fsm.slots);
                 return;
             }
         }
     }
 
     slot = slot->next();
     return;
@@ -528,17 +550,17 @@ void Pass::dumpRuleEventConsidered(const
                         << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext)))
                         << "length" << r->rule->sort
                         << json::close  // close "input"
                     << json::close; // close Rule object
     }
 }
 
 
-void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const
+void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, Machine & m, const Rule & r, Slot * const last_slot) const
 {
     *fsm.dbgout     << json::item << json::flat << json::object
                         << "id"     << &r - m_rules
                         << "failed" << false
                         << "input" << json::flat << json::object
                             << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
                             << "length" << r.sort - r.preContext
                             << json::close // close "input"
@@ -546,17 +568,17 @@ void Pass::dumpRuleEventOutput(const Fin
                 << json::close // close considered array
                 << "output" << json::object
                     << "range" << json::flat << json::object
                         << "start"  << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
                         << "end"    << objectid(dslot(&fsm.slots.segment, last_slot))
                     << json::close // close "input"
                     << "slots"  << json::array;
     const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance();
-    fsm.slots.segment.positionSlots(0);
+    fsm.slots.segment.positionSlots(0, 0, 0, m.slotMap().dir());
 
     for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next())
         *fsm.dbgout     << dslot(&fsm.slots.segment, slot);
     *fsm.dbgout         << json::close  // close "slots"
                     << "postshift"  << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos
                 << json::close;         // close "output" object
 
 }
@@ -602,22 +624,26 @@ bool Pass::testConstraint(const Rule & r
         if (!ret || m.status() != Machine::finished)
             return false;
     }
 
     return true;
 }
 
 
-void SlotMap::collectGarbage()
+void SlotMap::collectGarbage(Slot * &aSlot)
 {
     for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) {
         Slot *& slot = *s;
         if(slot->isDeleted() || slot->isCopied())
+        {
+            if (slot == aSlot)
+                aSlot = slot->prev() ? slot->prev() : slot->next();
             segment.freeSlot(slot);
+        }
     }
 }
 
 
 
 int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const
 {
     assert(codeptr);
@@ -676,56 +702,55 @@ void Pass::adjustSlot(int delta, Slot * 
         }
     }
 }
 
 bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
 {
     ShiftCollider shiftcoll(dbgout);
     // bool isfirst = true;
-    const uint8 numLoops = m_flags & 7;   // number of loops permitted to fix collisions; does not include kerning
     bool hasCollisions = false;
     Slot *start = seg->first();      // turn on collision fixing for the first slot
     Slot *end = NULL;
     bool moved = false;
 
 #if !defined GRAPHITE2_NTRACING
     if (dbgout)
         *dbgout << "collisions" << json::array
-            << json::flat << json::object << "num-loops" << numLoops << json::close;
+            << json::flat << json::object << "num-loops" << m_numCollRuns << json::close;
 #endif
 
     while (start)
     {
 #if !defined GRAPHITE2_NTRACING
         if (dbgout)  *dbgout << json::object << "phase" << "1" << "moves" << json::array;
 #endif
         hasCollisions = false;
         end = NULL;
         // phase 1 : position shiftable glyphs, ignoring kernable glyphs
         for (Slot *s = start; s; s = s->next())
         {
             const SlotCollision * c = seg->collisionInfo(s);
             if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
                       && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
                 return false;
-            if (s != start && c->flags() & SlotCollision::COLL_END)
+            if (s != start && (c->flags() & SlotCollision::COLL_END))
             {
                 end = s->next();
                 break;
             }
         }
 
 #if !defined GRAPHITE2_NTRACING
         if (dbgout)
             *dbgout << json::close << json::close; // phase-1
 #endif
 
         // phase 2 : loop until happy. 
-        for (int i = 0; i < numLoops - 1; ++i)
+        for (int i = 0; i < m_numCollRuns - 1; ++i)
         {
             if (hasCollisions || moved)
             {
 
 #if !defined GRAPHITE2_NTRACING
                 if (dbgout)
                     *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array;
 #endif
@@ -913,17 +938,17 @@ bool Pass::resolveCollisions(Segment *se
         bool sameCluster = nbor->isChildOf(base);
         if (nbor != slotFix         // don't process if this is the slot of interest
                       && !(cNbor->flags() & SlotCollision::COLL_IGNORE)    // don't process if ignoring
                       && (nbor == base || sameCluster       // process if in the same cluster as slotFix
                             || !inKernCluster(seg, nbor)    // or this cluster is not to be kerned
                             || (rtl ^ ignoreForKern))       // or it comes before(ltr) or after(rtl)
                       && (!isRev    // if processing forwards then good to merge otherwise only:
                             || !(cNbor->flags() & SlotCollision::COLL_FIX)     // merge in immovable stuff
-                            || (cNbor->flags() & SlotCollision::COLL_KERN && !sameCluster)     // ignore other kernable clusters
+                            || ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster)     // ignore other kernable clusters
                             || (cNbor->flags() & SlotCollision::COLL_ISCOL))   // test against other collided glyphs
                       && !coll.mergeSlot(seg, nbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout))
             return false;
         else if (nbor == slotFix)
             // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore.
             ignoreForKern = !ignoreForKern;
             
         if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END)))
@@ -939,17 +964,17 @@ bool Pass::resolveCollisions(Segment *se
             if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold)
                 moved = true;
             cFix->setShift(shift);
             if (slotFix->firstChild())
             {
                 Rect bbox;
                 Position here = slotFix->origin() + shift;
                 float clusterMin = here.x;
-                slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, false);
+                slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false);
             }
         }
     }
     else
     {
         // This glyph is not colliding with anything.
 #if !defined GRAPHITE2_NTRACING
         if (dbgout)
@@ -997,17 +1022,17 @@ float Pass::resolveKern(Segment *seg, Sl
         if (nbor->isChildOf(base))
             continue;
         if (!gc.check(nbor->gid()))
             return 0.;
         const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid());
         SlotCollision *cNbor = seg->collisionInfo(nbor);
         if (bb.bl.y == 0.f && bb.tr.y == 0.f)
         {
-            if ((m_flags & 24) == 16)
+            if (m_kernColls == InWord)
                 break;
             // Add space for a space glyph.
             currSpace += nbor->advance();
             ++space_count;
         }
         else
         {
             space_count = 0; 
--- a/gfx/graphite2/src/SegCache.cpp
+++ b/gfx/graphite2/src/SegCache.cpp
@@ -35,17 +35,17 @@ of the License or (at your option) any l
 
 
 using namespace graphite2;
 
 #ifndef GRAPHITE2_NSEGCACHE
 
 SegCache::SegCache(const SegCacheStore * store, const Features & feats)
 : m_prefixLength(ePrefixLength),
-  m_maxCachedSegLength(eMaxSpliceSize),
+//  m_maxCachedSegLength(eMaxSpliceSize),
   m_segmentCount(0),
   m_features(feats),
   m_totalAccessCount(0l), m_totalMisses(0l),
   m_purgeFactor(1.0f / (ePurgeFactor * store->maxSegmentCount()))
 {
     m_prefixes.raw = grzeroalloc<void*>(store->maxCmapGid() + 2);
     m_prefixes.range[SEG_CACHE_MIN_INDEX] = SEG_CACHE_UNSET_INDEX;
     m_prefixes.range[SEG_CACHE_MAX_INDEX] = SEG_CACHE_UNSET_INDEX;
@@ -79,17 +79,17 @@ SegCache::~SegCache()
 {
     assert(m_prefixes.raw == NULL);
 }
 
 SegCacheEntry* SegCache::cache(SegCacheStore * store, const uint16* cmapGlyphs, size_t length, Segment * seg, size_t charOffset)
 {
     uint16 pos = 0;
     if (!length) return NULL;
-    assert(length < m_maxCachedSegLength);
+//    assert(length < m_maxCachedSegLength);
     SegCachePrefixArray pArray = m_prefixes;
     while (pos + 1 < m_prefixLength)
     {
         uint16 gid = (pos < length)? cmapGlyphs[pos] : 0;
         if (!pArray.array[gid].raw)
         {
             pArray.array[gid].raw = grzeroalloc<void*>(store->maxCmapGid() + 2);
             if (!pArray.array[gid].raw)
--- a/gfx/graphite2/src/Segment.cpp
+++ b/gfx/graphite2/src/Segment.cpp
@@ -31,17 +31,17 @@ of the License or (at your option) any l
 #include "inc/bits.h"
 #include "inc/Segment.h"
 #include "graphite2/Font.h"
 #include "inc/CharInfo.h"
 #include "inc/debug.h"
 #include "inc/Slot.h"
 #include "inc/Main.h"
 #include "inc/CmapCache.h"
-#include "inc/Bidi.h"
+//#include "inc/Bidi.h"
 #include "inc/Collider.h"
 #include "graphite2/Segment.h"
 
 
 using namespace graphite2;
 
 Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int textDir)
 : m_freeSlots(NULL),
@@ -63,18 +63,20 @@ Segment::Segment(unsigned int numchars, 
     freeSlot(newSlot());
     m_bufSize = log_binary(numchars)+1;
 }
 
 Segment::~Segment()
 {
     for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
         free(*i);
-    for (AttributeRope::iterator j = m_userAttrs.begin(); j != m_userAttrs.end(); ++j)
-        free(*j);
+    for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i)
+        free(*i);
+    for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i)
+        free(*i);
     delete[] m_charinfo;
 }
 
 #ifndef GRAPHITE2_NSEGCACHE
 SegmentScopeState Segment::setScope(Slot * firstSlot, Slot * lastSlot, size_t subLength)
 {
     SegmentScopeState state;
     state.numGlyphsOutsideScope = m_numGlyphs - subLength;
@@ -149,41 +151,49 @@ void Segment::appendSlot(int id, int cid
     const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
     m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0);
     
     aSlot->child(NULL);
     aSlot->setGlyph(this, gid, theGlyph);
     aSlot->originate(id);
     aSlot->before(id);
     aSlot->after(id);
+//    uint8 aBidi = m_silf->aBidi();
+//    if (aBidi != 0xFF)
+//    {
+//        unsigned int bAttr = glyphAttr(gid, aBidi);
+//        aSlot->setBidiClass((bAttr <= 22) * bAttr);
+//    }
     if (m_last) m_last->next(aSlot);
     aSlot->prev(m_last);
     m_last = aSlot;
     if (!m_first) m_first = aSlot;
     if (theGlyph && m_silf->aPassBits())
         m_passBits &= theGlyph->attrs()[m_silf->aPassBits()] 
                     | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
 }
 
 Slot *Segment::newSlot()
 {
     if (!m_freeSlots)
     {
+        // check that the segment doesn't grow indefinintely
+        if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR)
+            return NULL;
         int numUser = m_silf->numUser();
 #if !defined GRAPHITE2_NTRACING
         if (m_face->logger()) ++numUser;
 #endif
         Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
         int16 *newAttrs = grzeroalloc<int16>(numUser * m_bufSize);
         if (!newSlots || !newAttrs) return NULL;
         for (size_t i = 0; i < m_bufSize; i++)
         {
+            ::new (newSlots + i) Slot(newAttrs + i * numUser);
             newSlots[i].next(newSlots + i + 1);
-            newSlots[i].userAttrs(newAttrs + i * numUser);
-            newSlots[i].setBidiClass(-1);
         }
         newSlots[m_bufSize - 1].next(NULL);
         newSlots[0].next(NULL);
         m_slots.push_back(newSlots);
         m_userAttrs.push_back(newAttrs);
         m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL;
         return newSlots;
     }
@@ -200,17 +210,17 @@ void Segment::freeSlot(Slot *aSlot)
     if (aSlot->attachedTo())
         aSlot->attachedTo()->removeChild(aSlot);
     while (aSlot->firstChild())
     {
         aSlot->firstChild()->attachTo(NULL);
         aSlot->removeChild(aSlot->firstChild());
     }
     // reset the slot incase it is reused
-    ::new (aSlot) Slot;
+    ::new (aSlot) Slot(aSlot->userAttrs());
     memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
     // Update generation counter for debug
 #if !defined GRAPHITE2_NTRACING
     if (m_face->logger())
         ++aSlot->userAttrs()[m_silf->numUser()];
 #endif
     // update next pointer
     if (!m_freeSlots)
@@ -304,16 +314,71 @@ void Segment::splice(size_t offset, size
         slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels(), numChars);
         if (srcSlot->attachedTo())  slot->attachTo(indexmap[srcSlot->attachedTo()->index()]);
         if (srcSlot->nextSibling()) slot->m_sibling = indexmap[srcSlot->nextSibling()->index()];
         if (srcSlot->firstChild())  slot->m_child = indexmap[srcSlot->firstChild()->index()];
     }
 }
 #endif // GRAPHITE2_NSEGCACHE
 
+// reverse the slots but keep diacritics in their same position after their bases
+void Segment::reverseSlots()
+{
+    m_dir = m_dir ^ 64;                 // invert the reverse flag
+    if (m_first == m_last) return;      // skip 0 or 1 glyph runs
+
+    Slot *t = 0;
+    Slot *curr = m_first;
+    Slot *tlast;
+    Slot *tfirst;
+    Slot *out = 0;
+
+    while (curr && getSlotBidiClass(curr) == 16)
+        curr = curr->next();
+    if (!curr) return;
+    tfirst = curr->prev();
+    tlast = curr;
+
+    while (curr)
+    {
+        if (getSlotBidiClass(curr) == 16)
+        {
+            Slot *d = curr->next();
+            while (d && getSlotBidiClass(d) == 16)
+                d = d->next();
+
+            d = d ? d->prev() : m_last;
+            Slot *p = out->next();    // one after the diacritics. out can't be null
+            if (p)
+                p->prev(d);
+            else
+                tlast = d;
+            t = d->next();
+            d->next(p);
+            curr->prev(out);
+            out->next(curr);
+        }
+        else    // will always fire first time round the loop
+        {
+            if (out)
+                out->prev(curr);
+            t = curr->next();
+            curr->next(out);
+            out = curr;
+        }
+        curr = t;
+    }
+    out->prev(tfirst);
+    if (tfirst)
+        tfirst->next(out);
+    else
+        m_first = out;
+    m_last = tlast;
+}
+
 void Segment::linkClusters(Slot *s, Slot * end)
 {
     end = end->next();
 
     for (; s != end && !s->isBase(); s = s->next());
     Slot * ls = s;
 
     if (m_dir & 1)
@@ -333,39 +398,39 @@ void Segment::linkClusters(Slot *s, Slot
             if (!s->isBase())   continue;
 
             ls->sibling(s);
             ls = s;
         }
     }
 }
 
-Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isFinal)
+Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal)
 {
     Position currpos(0., 0.);
     float clusterMin = 0.;
     Rect bbox;
 
     if (!iStart)    iStart = m_first;
     if (!iEnd)      iEnd   = m_last;
 
-    if (m_dir & 1)
+    if (isRtl)
     {
         for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
         {
             if (s->isBase())
-                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
+                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
         }
     }
     else
     {
         for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
         {
             if (s->isBase())
-                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
+                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
         }
     }
     return currpos;
 }
 
 
 void Segment::associateChars(int offset, int numChars)
 {
@@ -432,37 +497,35 @@ bool Segment::read_text(const Face *face
     {
     case gr_utf8:   process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
     case gr_utf16:  process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
     case gr_utf32:  process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
     }
     return true;
 }
 
+#if 0
 Slot *process_bidi(Slot *start, int level, int prelevel, int &nextLevel, int dirover, int isol, int &cisol, int &isolerr, int &embederr, int init, Segment *seg, uint8 aMirror, BracketPairStack &stack);
 void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror);
 void resolveWhitespace(int baseLevel, Slot *s);
 Slot *resolveOrder(Slot * & s, const bool reordered, const int level = 0);
 
-void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
+void Segment::bidiPass(int paradir, uint8 aMirror)
 {
     if (slotCount() == 0)
         return;
 
     Slot *s;
     int baseLevel = paradir ? 1 : 0;
     unsigned int bmask = 0;
     unsigned int ssize = 0;
     for (s = first(); s; s = s->next())
     {
-        if (s->getBidiClass() == -1)
-        {
-            unsigned int bAttr = glyphAttr(s->gid(), aBidi);
-            s->setBidiClass((bAttr <= 22) * bAttr);
-        }
+        if (getSlotBidiClass(s) < 0)
+            s->setBidiClass(0);
         bmask |= (1 << s->getBidiClass());
         s->setBidiLevel(baseLevel);
         if (s->getBidiClass() == 21)
             ++ssize;
     }
 
     BracketPairStack bstack(ssize);
     if (bmask & (paradir ? 0x2E7892 : 0x2E789C))
@@ -484,16 +547,28 @@ void Segment::bidiPass(uint8 aBidi, int 
     {
         for (s = first(); s; s = s->next())
         {
             unsigned short g = glyphAttr(s->gid(), aMirror);
             if (g) s->setGlyph(this, g);
         }
     }
 }
+#endif
+
+void Segment::doMirror(uint16 aMirror)
+{
+    Slot * s;
+    for (s = m_first; s; s = s->next())
+    {
+        unsigned short g = glyphAttr(s->gid(), aMirror);
+        if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1)))
+            s->setGlyph(this, g);
+    }
+}
 
 bool Segment::initCollisions()
 {
     if (m_collisions) free(m_collisions);
     Slot *p = m_first;
     m_collisions = gralloc<SlotCollision>(slotCount());
     if (!m_collisions) return false;
     for (unsigned short i = 0; i < slotCount(); ++i)
--- a/gfx/graphite2/src/Silf.cpp
+++ b/gfx/graphite2/src/Silf.cpp
@@ -46,23 +46,25 @@ Silf::Silf() throw()
   m_justs(0),
   m_numPasses(0),
   m_numJusts(0),
   m_sPass(0),
   m_pPass(0),
   m_jPass(0),
   m_bPass(0),
   m_flags(0),
+  m_dir(0),
   m_aPseudo(0),
   m_aBreak(0),
   m_aUser(0),
   m_aBidi(0),
   m_aMirror(0),
   m_aPassBits(0),
   m_iMaxComp(0),
+  m_aCollision(0),
   m_aLig(0),
   m_numPseudo(0),
   m_nClass(0),
   m_nLinear(0),
   m_gEndLine(0)
 {
     memset(&m_silfinfo, 0, sizeof m_silfinfo);
 }
@@ -136,20 +138,20 @@ bool Silf::readGraphite(const byte * con
         for (uint8 i = 0; i < m_numJusts; i++)
         {
             ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
             be::skip<byte>(p,8);
         }
     }
 
     if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
-    m_aLig      = be::read<uint16>(p);
-    m_aUser     = be::read<uint8>(p);
-    m_iMaxComp  = be::read<uint8>(p);
-    be::skip<byte>(p);                          // direction
+    m_aLig       = be::read<uint16>(p);
+    m_aUser      = be::read<uint8>(p);
+    m_iMaxComp   = be::read<uint8>(p);
+    m_dir        = be::read<uint8>(p) - 1;
     m_aCollision = be::read<uint8>(p);
     be::skip<byte>(p,3);
     be::skip<uint16>(p, be::read<uint8>(p));    // don't need critical features yet
     be::skip<byte>(p);                          // reserved
     if (e.test(p >= silf_end, E_BADCRITFEATURES))   { releaseBuffers(); return face.error(e); }
     be::skip<uint32>(p, be::read<uint8>(p));    // don't use scriptTag array.
     if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
     m_gEndLine  = be::read<uint16>(p);          // lbGID
@@ -193,17 +195,19 @@ bool Silf::readGraphite(const byte * con
           || e.test(!m_passes, E_OUTOFMEM))
     { releaseBuffers(); return face.error(e); }
 
     for (size_t i = 0; i < m_numPasses; ++i)
     {
         const byte * const pass_start = silf_start + be::read<uint32>(o_passes),
                    * const pass_end = silf_start + be::peek<uint32>(o_passes);
         face.error_context((face.error_context() & 0xFF00) + EC_ASILF + (i << 16));
-        if (e.test(pass_start > pass_end, E_BADPASSSTART) || e.test(pass_end > silf_end, E_BADPASSEND)) {
+        if (e.test(pass_start > pass_end, E_BADPASSSTART) 
+                || e.test(pass_start < passes_start, E_BADPASSSTART)
+                || e.test(pass_end > silf_end, E_BADPASSEND)) {
             releaseBuffers(); return face.error(e);
         }
 
         enum passtype pt = PASS_TYPE_UNKNOWN;
         if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION;
         else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING;
         else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE;
         else pt = PASS_TYPE_LINEBREAK;
@@ -263,35 +267,38 @@ size_t Silf::readClassMap(const byte *p,
     uint32 max_off;
     if (version >= 0x00040000)
         max_off = readClassOffsets<uint32>(p, data_len, e);
     else
         max_off = readClassOffsets<uint16>(p, data_len, e);
 
     if (max_off == ERROROFFSET) return ERROROFFSET;
 
+    if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG))
+        return ERROROFFSET;
+
     // Check the linear offsets are sane, these must be monotonically increasing.
     for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
         if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
             return ERROROFFSET;
 
     // Fortunately the class data is all uint16s so we can decode these now
     m_classData = gralloc<uint16>(max_off);
     if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
     for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
         *d = be::read<uint16>(p);
 
     // Check the lookup class invariants for each non-linear class
     for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
     {
         const uint16 * lookup = m_classData + *o;
-        if (e.test(*o > max_off - 4, E_HIGHCLASSOFFSET)                        // LookupClass doesn't stretch over max_off
+        if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET)                        // LookupClass doesn't stretch over max_off
          || e.test(lookup[0] == 0                                                   // A LookupClass with no looks is a suspicious thing ...
-                    || lookup[0] > (max_off - *o - 4)/2                             // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
-                    || lookup[3] != lookup[0] - lookup[1], E_BADCLASSLOOKUPINFO))   // rangeShift:   numIDs  - searchRange
+                    || lookup[0] * 2 + *o + 4 > max_off                             // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
+                    || lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO))   // rangeShift:   numIDs  - searchRange
             return ERROROFFSET;
     }
 
     return max_off;
 }
 
 uint16 Silf::findPseudo(uint32 uid) const
 {
@@ -302,17 +309,17 @@ uint16 Silf::findPseudo(uint32 uid) cons
 
 uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
 {
     if (cid > m_nClass) return -1;
 
     const uint16 * cls = m_classData + m_classOffsets[cid];
     if (cid < m_nLinear)        // output class being used for input, shouldn't happen
     {
-        for (unsigned int i = 0, n = m_classOffsets[cid + 1]; i < n; ++i, ++cls)
+        for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls)
             if (*cls == gid) return i;
         return -1;
     }
     else
     {
         const uint16 *  min = cls + 4,      // lookups array
                      *  max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long
         do
@@ -343,91 +350,82 @@ uint16 Silf::getClassGlyph(uint16 cid, u
     }
     return 0;
 }
 
 
 bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
 {
     assert(seg != 0);
-    SlotMap            map(*seg);
+    SlotMap            map(*seg, m_dir);
     FiniteStateMachine fsm(map, seg->getFace()->logger());
     vm::Machine        m(map);
     unsigned int       initSize = seg->slotCount();
     uint8              lbidi = m_bPass;
 #if !defined GRAPHITE2_NTRACING
     json * const dbgout = seg->getFace()->logger();
 #endif
 
     if (lastPass == 0)
     {
         if (firstPass == lastPass && lbidi == 0xFF)
             return true;
         lastPass = m_numPasses;
     }
-    if (firstPass <= lbidi && lastPass >= lbidi && dobidi)
+    if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi)))
         lastPass++;
     else
         lbidi = 0xFF;
 
     for (size_t i = firstPass; i < lastPass; ++i)
     {
         // bidi and mirroring
         if (i == lbidi)
         {
 #if !defined GRAPHITE2_NTRACING
             if (dbgout)
             {
                 *dbgout << json::item << json::object
                             << "id"     << -1
                             << "slots"  << json::array;
-                seg->positionSlots(0);
+                seg->positionSlots(0, 0, 0, m_dir);
                 for(Slot * s = seg->first(); s; s = s->next())
                     *dbgout     << dslot(seg, s);
                 *dbgout         << json::close
                             << "rules"  << json::array << json::close
                             << json::close;
             }
 #endif
-
-            if (!(seg->dir() & 2))
-                seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror);
-            else if (m_aMirror && (seg->dir() & 1))
-            {
-                Slot * s;
-                for (s = seg->first(); s; s = s->next())
-                {
-                    unsigned short g = seg->glyphAttr(s->gid(), m_aMirror);
-                    if (g && (!(seg->dir() & 4) || !seg->glyphAttr(s->gid(), m_aMirror + 1)))
-                        s->setGlyph(seg, g);
-                }
-            }
+            if (seg->currdir() != m_dir)
+                seg->reverseSlots();
+            if (m_aMirror && (seg->dir() & 3) == 3)
+                seg->doMirror(m_aMirror);
         --i;
+        lbidi = lastPass;
         --lastPass;
-        lbidi = 0xFF;
         continue;
         }
 
 #if !defined GRAPHITE2_NTRACING
         if (dbgout)
         {
             *dbgout << json::item << json::object
                         << "id"     << i+1
                         << "slots"  << json::array;
-            seg->positionSlots(0);
+            seg->positionSlots(0, 0, 0, m_dir);
             for(Slot * s = seg->first(); s; s = s->next())
                 *dbgout     << dslot(seg, s);
             *dbgout         << json::close;
         }
 #endif
 
         // test whether to reorder, prepare for positioning
-        if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || (m_passes[i].flags() & 7))
-                && !m_passes[i].runGraphite(m, fsm))
+        bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
+        if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
+                && !m_passes[i].runGraphite(m, fsm, reverse))
             return false;
         // only subsitution passes can change segment length, cached subsegments are short for their text
         if (m.status() != vm::Machine::finished
-            || (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR
-            || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))))
+            || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))
             return false;
     }
     return true;
 }
--- a/gfx/graphite2/src/Slot.cpp
+++ b/gfx/graphite2/src/Slot.cpp
@@ -29,25 +29,24 @@ of the License or (at your option) any l
 #include "inc/Silf.h"
 #include "inc/CharInfo.h"
 #include "inc/Rule.h"
 #include "inc/Collider.h"
 
 
 using namespace graphite2;
 
-Slot::Slot() :
+Slot::Slot(int16 *user_attrs) :
     m_next(NULL), m_prev(NULL),
     m_glyphid(0), m_realglyphid(0), m_original(0), m_before(0), m_after(0),
     m_index(0), m_parent(NULL), m_child(NULL), m_sibling(NULL),
     m_position(0, 0), m_shift(0, 0), m_advance(0, 0),
     m_attach(0, 0), m_with(0, 0), m_just(0.),
-    m_flags(0), m_attLevel(0), m_bidiCls(-1), m_bidiLevel(0), m_justs(NULL)
-    // Do not set m_userAttr since it is set *before* new is called since this
-    // is used as a positional new to reset the GrSlot
+    m_flags(0), m_attLevel(0), m_bidiCls(-1), m_bidiLevel(0), 
+    m_userAttr(user_attrs), m_justs(NULL)
 {
 }
 
 // take care, this does not copy any of the GrSlot pointer fields
 void Slot::set(const Slot & orig, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars)
 {
     // leave m_next and m_prev unchanged
     m_glyphid = orig.m_glyphid;
@@ -81,27 +80,27 @@ void Slot::set(const Slot & orig, int ch
 
 void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
 {
     m_before += numCharInfo;
     m_after += numCharInfo;
     m_position = m_position + relpos;
 }
 
-Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal)
+Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal)
 {
     SlotCollision *coll = NULL;
     if (attrLevel && m_attLevel > attrLevel) return Position(0, 0);
     float scale = font ? font->scale() : 1.0f;
-    Position shift(m_shift.x * ((seg->dir() & 1) * -2 + 1) + m_just, m_shift.y);
+    Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y);
     float tAdvance = m_advance.x + m_just;
     if (isFinal && (coll = seg->collisionInfo(this)))
     {
         const Position &collshift = coll->offset();
-        if (!(coll->flags() & SlotCollision::COLL_KERN) || (seg->dir() & 1))
+        if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl)
             shift = shift + collshift;
     }
     const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph());
     if (font)
     {
         scale = font->scale();
         shift *= scale;
         if (font->isHinted() && glyphFace)
@@ -129,42 +128,44 @@ Position Slot::finalise(const Segment *s
     if (glyphFace)
     {
         Rect ourBbox = glyphFace->theBBox() * scale + m_position;
         bbox = bbox.widen(ourBbox);
     }
 
     if (m_child && m_child != this && m_child->attachedTo() == this)
     {
-        Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, isFinal);
+        Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal);
         if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
     }
 
     if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent)
     {
-        Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, isFinal);
+        Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal);
         if (tRes.x > res.x) res = tRes;
     }
     
     if (!m_parent && clusterMin < base.x)
     {
         Position adj = Position(m_position.x - clusterMin, 0.);
         res += adj;
         m_position += adj;
         if (m_child) m_child->floodShift(adj);
     }
     return res;
 }
 
-int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
+int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel, bool rtl)
 {
     Position base;
+    if (glyph() >= seg->getFace()->glyphs().numGlyphs())
+        return 0;
     Rect bbox = seg->theGlyphBBoxTemporary(glyph());
     float clusterMin = 0.;
-    Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, false);
+    Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false);
 
     switch (metrics(metric))
     {
     case kgmetLsb :
         return static_cast<uint32>(bbox.bl.x);
     case kgmetRsb :
         return static_cast<uint32>(res.x - bbox.tr.x);
     case kgmetBbTop :
@@ -187,17 +188,16 @@ int32 Slot::clusterMetric(const Segment 
         return 0;
     }
 }
 
 #define SLOTGETCOLATTR(x) { SlotCollision *c = seg->collisionInfo(this); return c ? int(c-> x) : 0; }
 
 int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
 {
-    if (!this) return 0;
     if (ind == gr_slatUserDefnV1)
     {
         ind = gr_slatUserDefn;
         subindex = 0;
     }
     else if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
     {
         int indx = ind - gr_slatJStretch;
@@ -215,19 +215,17 @@ int Slot::getAttr(const Segment *seg, at
     case gr_slatAttYOff :   return 0;
     case gr_slatAttWithX :  return int(m_with.x);
     case gr_slatAttWithY :  return int(m_with.y);
     case gr_slatAttWithXOff:
     case gr_slatAttWithYOff:return 0;
     case gr_slatAttLevel :  return m_attLevel;
     case gr_slatBreak :     return seg->charinfo(m_original)->breakWeight();
     case gr_slatCompRef :   return 0;
-    case gr_slatDir :       if (m_bidiCls == -1)
-                                const_cast<Slot *>(this)->setBidiClass(int8(seg->glyphAttr(gid(), seg->silf()->aBidi())));
-                            return m_bidiCls;
+    case gr_slatDir :       return seg->dir() & 1;
     case gr_slatInsert :    return isInsertBefore();
     case gr_slatPosX :      return int(m_position.x); // but need to calculate it
     case gr_slatPosY :      return int(m_position.y);
     case gr_slatShiftX :    return int(m_shift.x);
     case gr_slatShiftY :    return int(m_shift.y);
     case gr_slatMeasureSol: return -1; // err what's this?
     case gr_slatMeasureEol: return -1;
     case gr_slatJWidth:     return int(m_just);
@@ -267,17 +265,16 @@ int Slot::getAttr(const Segment *seg, at
         SlotCollision *c = seg->collisionInfo(this); \
         if (c) { \
         const t &s = c-> y; \
         c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
         break; }
 
 void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, const SlotMap & map)
 {
-    if (!this) return;
     if (ind == gr_slatUserDefnV1)
     {
         ind = gr_slatUserDefn;
         subindex = 0;
     }
     else if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
     {
         int indx = ind - gr_slatJStretch;
@@ -294,17 +291,17 @@ void Slot::setAttr(Segment *seg, attrCod
         if (idx < map.size() && map[idx])
         {
             Slot *other = map[idx];
             if (other == this || other == m_parent) break;
             if (m_parent) m_parent->removeChild(this);
             if (!other->isChildOf(this) && other->child(this))
             {
                 attachTo(other);
-                if (((seg->dir() & 1) != 0) ^ (idx > subindex))
+                if ((map.dir() != 0) ^ (idx > subindex))
                     m_with = Position(advance(), 0);
                 else        // normal match to previous root
                     m_attach = Position(other->advance(), 0);
             }
         }
         break;
     }
     case gr_slatAttX :          m_attach.x = value; break;
@@ -317,17 +314,17 @@ void Slot::setAttr(Segment *seg, attrCod
     case gr_slatAttWithYOff :   break;
     case gr_slatAttLevel :
         m_attLevel = byte(value);
         break;
     case gr_slatBreak :
         seg->charinfo(m_original)->breakWeight(value);
         break;
     case gr_slatCompRef :   break;      // not sure what to do here
-    case gr_slatDir :       m_bidiCls = int8(value); break;
+    case gr_slatDir : break;
     case gr_slatInsert :
         markInsertBefore(value? true : false);
         break;
     case gr_slatPosX :      break; // can't set these here
     case gr_slatPosY :      break;
     case gr_slatShiftX :    m_shift.x = value; break;
     case gr_slatShiftY :    m_shift.y = value; break;
     case gr_slatMeasureSol :    break;
@@ -445,27 +442,30 @@ bool Slot::removeSibling(Slot *ap)
     else
         return m_sibling->removeSibling(ap);
     return true;
 }
 
 void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
 {
     m_glyphid = glyphid;
+    m_bidiCls = -1;
     if (!theGlyph)
     {
         theGlyph = seg->getFace()->glyphs().glyphSafe(glyphid);
         if (!theGlyph)
         {
             m_realglyphid = 0;
             m_advance = Position(0.,0.);
             return;
         }
     }
     m_realglyphid = theGlyph->attrs()[seg->silf()->aPseudo()];
+    if (m_realglyphid > seg->getFace()->glyphs().numGlyphs())
+        m_realglyphid = 0;
     const GlyphFace *aGlyph = theGlyph;
     if (m_realglyphid)
     {
         aGlyph = seg->getFace()->glyphs().glyphSafe(m_realglyphid);
         if (!aGlyph) aGlyph = theGlyph;
     }
     m_advance = Position(aGlyph->theAdvance().x, 0.);
     if (seg->silf()->aPassBits())
--- a/gfx/graphite2/src/Sparse.cpp
+++ b/gfx/graphite2/src/Sparse.cpp
@@ -25,17 +25,17 @@ License, as published by the Free Softwa
 of the License or (at your option) any later version.
 */
 #include <cassert>
 #include "inc/Sparse.h"
 #include "inc/bits.h"
 
 using namespace graphite2;
 
-sparse::chunk sparse::empty_chunk = {0,0};
+const sparse::chunk sparse::empty_chunk = {0,0};
 
 sparse::~sparse() throw()
 {
     if (m_array.map == &empty_chunk) return;
     free(m_array.values);
 }
 
 
--- a/gfx/graphite2/src/TtfUtil.cpp
+++ b/gfx/graphite2/src/TtfUtil.cpp
@@ -57,18 +57,20 @@ Description
     Forward declarations
 ***********************************************************************************************/
 
 /***********************************************************************************************
     Local Constants and static variables
 ***********************************************************************************************/
 namespace 
 {
+#ifdef ALL_TTFUTILS
     // max number of components allowed in composite glyphs
     const int kMaxGlyphComponents = 8;
+#endif
 
     template <int R, typename T>
     inline float fixed_to_float(const T f) {
         return float(f)/float(2^R);
     }
 
 /*----------------------------------------------------------------------------------------------
     Table of standard Postscript glyph names. From Martin Hosken. Disagress with ttfdump.exe
@@ -222,69 +224,79 @@ bool GetTableInfo(const Tag TableTag, co
 /*----------------------------------------------------------------------------------------------
     Check the specified table. Tests depend on the table type.
     Return true if successful, false otherwise.
 ----------------------------------------------------------------------------------------------*/
 bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
 {
     using namespace Sfnt;
     
-    if (pTable == 0) return false;
+    if (pTable == 0 || lTableSize < 4) return false;
 
     switch(TableId)
     {
     case Tag::cmap: // cmap
     {
         const Sfnt::CharacterCodeMap * const pCmap 
             = reinterpret_cast<const Sfnt::CharacterCodeMap *>(pTable);
+        if (lTableSize < sizeof(Sfnt::CharacterCodeMap))
+            return false;
         return be::swap(pCmap->version) == 0;
     }
 
     case Tag::head: // head
     {
         const Sfnt::FontHeader * const pHead 
             = reinterpret_cast<const Sfnt::FontHeader *>(pTable);
+        if (lTableSize < sizeof(Sfnt::FontHeader))
+            return false;
         bool r = be::swap(pHead->version) == OneFix
             && be::swap(pHead->magic_number) == FontHeader::MagicNumber
             && be::swap(pHead->glyph_data_format)
                     == FontHeader::GlypDataFormat 
             && (be::swap(pHead->index_to_loc_format)
                     == FontHeader::ShortIndexLocFormat 
                 || be::swap(pHead->index_to_loc_format)
                     == FontHeader::LongIndexLocFormat) 
             && sizeof(FontHeader) <= lTableSize;
         return r;
     }
 
     case Tag::post: // post
     {
         const Sfnt::PostScriptGlyphName * const pPost 
             = reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pTable);
+        if (lTableSize < sizeof(Sfnt::PostScriptGlyphName))
+            return false;
         const fixed format = be::swap(pPost->format);
         bool r = format == PostScriptGlyphName::Format1 
             || format == PostScriptGlyphName::Format2 
             || format == PostScriptGlyphName::Format3 
             || format == PostScriptGlyphName::Format25;
         return r;
     }
 
     case Tag::hhea: // hhea
     {
         const Sfnt::HorizontalHeader * pHhea = 
             reinterpret_cast<const Sfnt::HorizontalHeader *>(pTable);
+        if (lTableSize < sizeof(Sfnt::HorizontalHeader))
+            return false;
         bool r = be::swap(pHhea->version) == OneFix
             && be::swap(pHhea->metric_data_format) == 0
             && sizeof (Sfnt::HorizontalHeader) <= lTableSize;
         return r;
     }
 
     case Tag::maxp: // maxp
     {
         const Sfnt::MaximumProfile * pMaxp = 
             reinterpret_cast<const Sfnt::MaximumProfile *>(pTable);
+        if (lTableSize < sizeof(Sfnt::MaximumProfile))
+            return false;
         bool r = be::swap(pMaxp->version) == OneFix
             && sizeof(Sfnt::MaximumProfile) <= lTableSize;
         return r;
     }
 
     case Tag::OS_2: // OS/2
     {
         const Sfnt::Compatibility * pOs2 
@@ -319,16 +331,18 @@ bool CheckTable(const Tag TableId, const
             return false;
         break;
     }
 
     case Tag::name:
     {
         const Sfnt::FontNames * pName 
             = reinterpret_cast<const Sfnt::FontNames *>(pTable);
+        if (lTableSize < sizeof(Sfnt::FontNames))
+            return false;
         return be::swap(pName->format) == 0;
     }
 
     default:
         break;
     }
 
     return true;
@@ -791,27 +805,27 @@ bool HorMetrics(gid16 nGlyphId, const vo
         reinterpret_cast<const Sfnt::HorizontalMetric *>(pHmtx);
 
     const Sfnt::HorizontalHeader * phhea = 
         reinterpret_cast<const Sfnt::HorizontalHeader *>(pHhea);
 
     size_t cLongHorMetrics = be::swap(phhea->num_long_hor_metrics);
     if (nGlyphId < cLongHorMetrics) 
     {   // glyph id is acceptable
-        if (nGlyphId * sizeof(Sfnt::HorizontalMetric) >= lHmtxSize) return false;
+        if ((nGlyphId + 1) * sizeof(Sfnt::HorizontalMetric) > lHmtxSize) return false;
         nAdvWid = be::swap(phmtx[nGlyphId].advance_width);
         nLsb = be::swap(phmtx[nGlyphId].left_side_bearing);
     }
     else
     {
         // guard against bad glyph id
         size_t lLsbOffset = sizeof(Sfnt::HorizontalMetric) * cLongHorMetrics +
             sizeof(int16) * (nGlyphId - cLongHorMetrics); // offset in bytes
         // We test like this as LsbOffset is an offset not a length.
-        if (lLsbOffset > lHmtxSize - sizeof(int16))
+        if (lLsbOffset >= lHmtxSize - sizeof(int16) || cLongHorMetrics == 0)
         {
             nLsb = 0;
             return false;
         }
         nAdvWid = be::swap(phmtx[cLongHorMetrics - 1].advance_width);
         nLsb = be::peek<int16>(reinterpret_cast<const byte *>(phmtx) + lLsbOffset);
     }
 
@@ -868,25 +882,27 @@ const void * FindCmapSubtable(const void
     }
 
     return 0;
 }
 
 /*----------------------------------------------------------------------------------------------
     Check the Microsoft Unicode subtable for expected values
 ----------------------------------------------------------------------------------------------*/
-bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/)
+bool CheckCmapSubtable4(const void * pCmapSubtable4, size_t table_len /*, unsigned int maxgid*/)
 {
     if (!pCmapSubtable4) return false;
     const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);
     // Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF) 
     // so don't check subtable version. 21 Mar 2002 spec changes version to language.
     if (be::swap(pTable->format) != 4) return false;
     const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtable4);
     uint16 length = be::swap(pTable4->length);
+    if (length > table_len)
+        return false;
     if (length < sizeof(Sfnt::CmapSubTableFormat4))
         return false;
     uint16 nRanges = be::swap(pTable4->seg_count_x2) >> 1;
     if (length < sizeof(Sfnt::CmapSubTableFormat4) + 4 * nRanges * sizeof(uint16))
         return false;
     // check last range is properly terminated
     uint16 chEnd = be::peek<uint16>(pTable4->end_code + nRanges - 1);
     if (chEnd != 0xFFFF)
@@ -914,17 +930,17 @@ bool CheckCmapSubtable4(const void * pCm
                     return false;
             }
         }
         else if (((delta + end) & 0xFFFF) > maxgid)
             return false;
         lastend = end;
     }
 #endif
-    return true;;
+    return true;
 }
 
 /*----------------------------------------------------------------------------------------------
     Return the Glyph ID for the given Unicode ID in the Microsoft Unicode subtable.
     (Actually this code only depends on subtable being format 4.)
     Return 0 if the Unicode ID is not in the subtable.
 ----------------------------------------------------------------------------------------------*/
 gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey)
@@ -1057,24 +1073,26 @@ unsigned int CmapSubtable4NextCodepoint(
     if (pRangeKey)
         *pRangeKey = iRange + 1;
     return be::peek<uint16>(pStartCode + iRange + 1);
 }
 
 /*----------------------------------------------------------------------------------------------
     Check the Microsoft UCS-4 subtable for expected values.
 ----------------------------------------------------------------------------------------------*/
-bool CheckCmapSubtable12(const void *pCmapSubtable12 /*, unsigned int maxgid*/)
+bool CheckCmapSubtable12(const void *pCmapSubtable12, size_t table_len /*, unsigned int maxgid*/)
 {
     if (!pCmapSubtable12)  return false;
     const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable12);
     if (be::swap(pTable->format) != 12)
         return false;
     const Sfnt::CmapSubTableFormat12 * pTable12 = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmapSubtable12);
     uint32 length = be::swap(pTable12->length);
+    if (length > table_len)
+        return false;
     if (length < sizeof(Sfnt::CmapSubTableFormat12))
         return false;
     uint32 num_groups = be::swap(pTable12->num_groups);
     if (length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3))
         return false;
 #if 0
     for (unsigned int i = 0; i < num_groups; ++i)
     {
@@ -1216,17 +1234,17 @@ size_t LocaLookup(gid16 nGlyphId,
 
 /*----------------------------------------------------------------------------------------------
     Return a pointer into the glyf table based on the given offset (from LocaLookup).
     Return NULL on error.
 ----------------------------------------------------------------------------------------------*/
 void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen)
 {
     const uint8 * pByte = reinterpret_cast<const uint8 *>(pGlyf);
-        if (nGlyfOffset == size_t(-1) || nGlyfOffset == size_t(-2) || nGlyfOffset >= nTableLen)
+        if (nGlyfOffset + pByte < pByte || nGlyfOffset + sizeof(Sfnt::Glyph) >= nTableLen)
             return NULL;
     return const_cast<uint8 *>(pByte + nGlyfOffset);
 }
 
 /*----------------------------------------------------------------------------------------------
     Get the bounding box coordinates for a simple glyf entry (non-composite).
     Return true if successful, false otherwise.
 ----------------------------------------------------------------------------------------------*/
@@ -1828,17 +1846,16 @@ bool GlyfContourEndPoints(gid16 nGlyphId
     cnPoints - count of points from largest end point obtained from GlyfContourEndPoints
     prgnX & prgnY - should point to buffers large enough to hold cnPoints integers
         The ranges are parallel so that coordinates for point(n) are found at offset n in 
         both ranges. These points are in absolute coordinates.
     prgfOnCurve - should point to a buffer a large enough to hold cnPoints bytes (bool)
         This range is parallel to the prgnX & prgnY
     Return true if successful, false otherwise. On false, all points may be INT_MIN
         False may indicate a white space glyph, a multi-level composite, or a corrupt font
-    // TODO: doesn't support composite glyphs whose components are themselves components
         It's not clear from the TTF spec when the transforms should be applied. Should the 
         transform be done before or after attachment point calcs? (current code - before) 
         Should the transform be applied to other offsets? (currently - no; however commented 
         out code is in place so that if CompoundGlyph::UnscaledOffset on the MS rasterizer is 
         clear (typical) then yes, and if CompoundGlyph::ScaledOffset on the Apple rasterizer is 
         clear (typical?) then no). See GetComponentTransform.
         It's also unclear where point numbering with attachment poinst starts 
         (currently - first point number is relative to whole glyph, second point number is 
--- a/gfx/graphite2/src/call_machine.cpp
+++ b/gfx/graphite2/src/call_machine.cpp
@@ -65,57 +65,60 @@ using namespace graphite2;
 using namespace vm;
 
 struct regbank  {
     slotref         is;
     slotref *       map;
     SlotMap       & smap;
     slotref * const map_base;
     const instr * & ip;
+    uint8           direction;
     int8            flags;
 };
 
 typedef bool        (* ip_t)(registers);
 
 // Pull in the opcode definitions
 // We pull these into a private namespace so these otherwise common names dont
 // pollute the toplevel namespace.
 namespace {
 #define smap    reg.smap
 #define seg     smap.segment
 #define is      reg.is
 #define ip      reg.ip
 #define map     reg.map
 #define mapb    reg.map_base
 #define flags   reg.flags
+#define dir     reg.direction
 
 #include "inc/opcodes.h"
 
 #undef smap
 #undef seg
 #undef is
 #undef ip
 #undef map
 #undef mapb
 #undef flags
+#undef dir
 }
 
 Machine::stack_t  Machine::run(const instr   * program,
                                const byte    * data,
                                slotref     * & map)
 
 {
     assert(program != 0);
 
     // Declare virtual machine registers
     const instr   * ip = program-1;
     const byte    * dp = data;
     stack_t       * sp = _stack + Machine::STACK_GUARD,
             * const sb = sp;
-    regbank         reg = {*map, map, _map, _map.begin()+_map.context(), ip, 0};
+    regbank         reg = {*map, map, _map, _map.begin()+_map.context(), ip, _map.dir(), 0};
 
     // Run the program        
     while ((reinterpret_cast<ip_t>(*++ip))(dp, sp, sb, reg)) {}
     const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0;
 
     check_final_stack(sp);
     map = reg.map;
     *map = reg.is;
--- a/gfx/graphite2/src/direct_machine.cpp
+++ b/gfx/graphite2/src/direct_machine.cpp
@@ -56,16 +56,17 @@ using namespace vm;
 
 namespace {
 
 const void * direct_run(const bool          get_table_mode,
                         const instr       * program,
                         const byte        * data,
                         Machine::stack_t  * stack,
                         slotref         * & __map,
+                        uint8                _dir,
                         SlotMap           * __smap=0)
 {
     // We need to define and return to opcode table from within this function 
     // other inorder to take the addresses of the instruction bodies.
     #include "inc/opcode_table.h"
     if (get_table_mode)
         return opcode_table;
 
@@ -74,16 +75,17 @@ const void * direct_run(const bool      
     const byte        * dp = data;
     Machine::stack_t  * sp = stack + Machine::STACK_GUARD,
                 * const sb = sp;
     SlotMap         & smap = *__smap;
     Segment          & seg = smap.segment;
     slotref             is = *__map,
                      * map = __map,
               * const mapb = smap.begin()+smap.context();
+    uint8            dir = _dir;
     int8             flags = 0;
     
     // start the program
     goto **ip;
 
     // Pull in the opcode definitions
     #include "inc/opcodes.h"
     
@@ -104,14 +106,14 @@ const opcode_t * Machine::getOpcodeTable
 
 Machine::stack_t  Machine::run(const instr   * program,
                                const byte    * data,
                                slotref     * & is)
 {
     assert(program != 0);
     
     const stack_t *sp = static_cast<const stack_t *>(
-                direct_run(false, program, data, _stack, is, &_map));
+                direct_run(false, program, data, _stack, is, _map.dir(), &_map));
     const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0;
     check_final_stack(sp);
     return ret;
 }
 
--- a/gfx/graphite2/src/files.mk
+++ b/gfx/graphite2/src/files.mk
@@ -42,17 +42,16 @@
     $($(_NS)_BASE)/src/gr_char_info.cpp \
     $($(_NS)_BASE)/src/gr_face.cpp \
     $($(_NS)_BASE)/src/gr_features.cpp \
     $($(_NS)_BASE)/src/gr_font.cpp \
     $($(_NS)_BASE)/src/gr_logging.cpp \
     $($(_NS)_BASE)/src/gr_segment.cpp \
     $($(_NS)_BASE)/src/gr_slot.cpp \
     $($(_NS)_BASE)/src/json.cpp \
-    $($(_NS)_BASE)/src/Bidi.cpp \
     $($(_NS)_BASE)/src/CachedFace.cpp \
     $($(_NS)_BASE)/src/CmapCache.cpp \
     $($(_NS)_BASE)/src/Code.cpp \
     $($(_NS)_BASE)/src/Collider.cpp \
     $($(_NS)_BASE)/src/Decompressor.cpp \
     $($(_NS)_BASE)/src/Face.cpp \
     $($(_NS)_BASE)/src/FeatureMap.cpp \
     $($(_NS)_BASE)/src/FileFace.cpp \
@@ -73,23 +72,22 @@
     $($(_NS)_BASE)/src/Sparse.cpp \
     $($(_NS)_BASE)/src/TtfUtil.cpp \
     $($(_NS)_BASE)/src/UtfCodec.cpp
 
 $(_NS)_PRIVATE_HEADERS = \
     $($(_NS)_BASE)/src/inc/bits.h \
     $($(_NS)_BASE)/src/inc/debug.h \
     $($(_NS)_BASE)/src/inc/json.h \
-    $($(_NS)_BASE)/src/inc/locale2lcid.h \
-    $($(_NS)_BASE)/src/inc/Bidi.h \
     $($(_NS)_BASE)/src/inc/CachedFace.h \
     $($(_NS)_BASE)/src/inc/CharInfo.h \
     $($(_NS)_BASE)/src/inc/CmapCache.h \
     $($(_NS)_BASE)/src/inc/Code.h \
     $($(_NS)_BASE)/src/inc/Collider.h \
+    $($(_NS)_BASE)/src/inc/Compression.h \
     $($(_NS)_BASE)/src/inc/Decompressor.h \
     $($(_NS)_BASE)/src/inc/Endian.h \
     $($(_NS)_BASE)/src/inc/Error.h \
     $($(_NS)_BASE)/src/inc/Face.h \
     $($(_NS)_BASE)/src/inc/FeatureMap.h \
     $($(_NS)_BASE)/src/inc/FeatureVal.h \
     $($(_NS)_BASE)/src/inc/FileFace.h \
     $($(_NS)_BASE)/src/inc/Font.h \
@@ -105,17 +103,16 @@
     $($(_NS)_BASE)/src/inc/opcodes.h \
     $($(_NS)_BASE)/src/inc/Pass.h \
     $($(_NS)_BASE)/src/inc/Position.h \
     $($(_NS)_BASE)/src/inc/Rule.h \
     $($(_NS)_BASE)/src/inc/SegCache.h \
     $($(_NS)_BASE)/src/inc/SegCacheEntry.h \
     $($(_NS)_BASE)/src/inc/SegCacheStore.h \
     $($(_NS)_BASE)/src/inc/Segment.h \
-    $($(_NS)_BASE)/src/inc/Shrinker.h \
     $($(_NS)_BASE)/src/inc/Silf.h \
     $($(_NS)_BASE)/src/inc/Slot.h \
     $($(_NS)_BASE)/src/inc/Sparse.h \
     $($(_NS)_BASE)/src/inc/TtfTypes.h \
     $($(_NS)_BASE)/src/inc/TtfUtil.h \
     $($(_NS)_BASE)/src/inc/UtfCodec.h
 
 $(_NS)_PUBLIC_HEADERS = \
--- a/gfx/graphite2/src/gr_segment.cpp
+++ b/gfx/graphite2/src/gr_segment.cpp
@@ -43,17 +43,17 @@ namespace
       Segment* pRes=new Segment(nChars, face, script, dir);
 
       
       if (!pRes->read_text(face, pFeats, enc, pStart, nChars) || !pRes->runGraphite())
       {
         delete pRes;
         return NULL;
       }
-      pRes->finalise(font);
+      pRes->finalise(font, true);
 
       return static_cast<gr_segment*>(pRes);
   }
 
 
 }
 
 
--- a/gfx/graphite2/src/inc/Code.h
+++ b/gfx/graphite2/src/inc/Code.h
@@ -64,17 +64,16 @@ public:
         jump_past_end,
         arguments_exhausted,
         missing_return,
         nested_context_item,
         underfull_stack
     };
 
 private:
-    static byte * local_memory;
     class decoder;
 
     instr *     _code;
     byte  *     _data;
     size_t      _data_size,
                 _instr_count;
     byte        _max_ref;
     mutable status_t _status;
@@ -82,43 +81,51 @@ private:
                 _modify,
                 _delete;
     mutable bool _own;
 
     void release_buffers() throw ();
     void failure(const status_t) throw();
 
 public:
+    static size_t estimateCodeDataOut(size_t num_bytecodes);
+
     Code() throw();
     Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
          uint8 pre_context, uint16 rule_length, const Silf &, const Face &,
-         enum passtype pt, byte * & _out = local_memory);
+         enum passtype pt, byte * * const _out = 0);
     Code(const Machine::Code &) throw();
     ~Code() throw();
     
     Code & operator=(const Code &rhs) throw();
-    operator bool () const throw();
-    status_t      status() const throw();
-    bool          constraint() const throw();
-    size_t        dataSize() const throw();
-    size_t        instructionCount() const throw();
-    bool          immutable() const throw();
-    bool          deletes() const throw();
-    size_t        maxRef() const throw();
+    operator bool () const throw()                  { return _code && status() == loaded; }
+    status_t      status() const throw()            { return _status; }
+    bool          constraint() const throw()        { return _constraint; }
+    size_t        dataSize() const throw()          { return _data_size; }
+    size_t        instructionCount() const throw()  { return _instr_count; }
+    bool          immutable() const throw()         { return !(_delete || _modify); }
+    bool          deletes() const throw()           { return _delete; }
+    size_t        maxRef() const throw()            { return _max_ref; }
     void          externalProgramMoved(ptrdiff_t) throw();
 
     int32 run(Machine &m, slotref * & map) const;
     
     CLASS_NEW_DELETE;
 };
 
+inline
+size_t  Machine::Code::estimateCodeDataOut(size_t n_bc)
+{
+    return n_bc * (sizeof(instr)+sizeof(byte));
+}
+
 
 inline Machine::Code::Code() throw()
 : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0),
-  _status(loaded), _constraint(false), _modify(false),_delete(false),
+  _status(loaded), _constraint(false), _modify(false), _delete(false),
   _own(false)
 {
 }
 
 inline Machine::Code::Code(const Machine::Code &obj) throw ()
  :  _code(obj._code), 
     _data(obj._data), 
     _data_size(obj._data_size), 
@@ -144,51 +151,16 @@ inline Machine::Code & Machine::Code::op
     _constraint  = rhs._constraint;
     _modify      = rhs._modify;
     _delete      = rhs._delete;
     _own         = rhs._own; 
     rhs._own = false;
     return *this;
 }
 
-inline Machine::Code::operator bool () const throw () {
-    return _code && status() == loaded;
-}
-
-inline Machine::Code::status_t Machine::Code::status() const throw() {
-    return _status;
-}
-
-inline bool Machine::Code::constraint() const throw() {
-    return _constraint;
-}
-
-inline size_t Machine::Code::dataSize() const throw() {
-    return _data_size;
-}
-
-inline size_t Machine::Code::instructionCount() const throw() {
-    return _instr_count;
-}
-
-inline bool Machine::Code::immutable() const throw()
-{
-  return !(_delete || _modify);
-}
-
-inline bool Machine::Code::deletes() const throw()
-{
-  return _delete;
-}
-
-inline size_t Machine::Code::maxRef() const throw()
-{
-    return _max_ref;
-}
-
 inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw()
 {
     if (_code && !_own)
     {
         _code += dist / sizeof(instr);
         _data += dist;
     }
 }
--- a/gfx/graphite2/src/inc/Collider.h
+++ b/gfx/graphite2/src/inc/Collider.h
@@ -23,17 +23,17 @@ Alternatively, the contents of this file
 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
 License, as published by the Free Software Foundation, either version 2
 of the License or (at your option) any later version.
 */
 #pragma once
 
 #include <utility>
 #include "inc/List.h"
-#include "inc/Slot.h"
+//#include "inc/Slot.h"
 #include "inc/Position.h"
 #include "inc/Intervals.h"
 #include "inc/debug.h"
 //#include "inc/Segment.h"
 
 namespace graphite2 {
 
 class json;
@@ -113,33 +113,27 @@ private:
     uint16		_seqAboveWt;
     int16		_seqBelowXlim;
     uint16		_seqBelowWt;
     uint16		_seqValignHt;
     uint16		_seqValignWt;
 	
 };  // end of class SlotColllision
 
-class BBox;
-class SlantBox;
+struct BBox;
+struct SlantBox;
 
 class ShiftCollider
 {
 public:
     typedef std::pair<float, float> fpair;
     typedef Vector<fpair> vfpairs;
     typedef vfpairs::iterator ivfpairs;
 
-    ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
-    {
-#if !defined GRAPHITE2_NTRACING
-        for (int i = 0; i < 4; ++i)
-            _ranges[i].setdebug(dbgout);
-#endif
-    }
+    ShiftCollider(json *dbgout);
     ~ShiftCollider() throw() { };
 
     bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint,
                 float margin, float marginMin, const Position &currShift,
                 const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout);
     bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, bool isAfter, 
                 bool sameCluster, bool &hasCol, bool isExclusion, GR_MAYBE_UNUSED json * const dbgout);
     Position resolve(Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout);
@@ -171,20 +165,35 @@ protected:
     uint16  _seqClass;
 	uint16	_seqProxClass;
     uint16  _seqOrder;
     
 	//bool _scraping[4];
 
 };	// end of class ShiftCollider
 
+inline
+ShiftCollider::ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
+: _target(0),
+  _margin(0.0),
+  _marginWt(0.0),
+  _seqClass(0),
+  _seqProxClass(0),
+  _seqOrder(0)
+{
+#if !defined GRAPHITE2_NTRACING
+    for (int i = 0; i < 4; ++i)
+        _ranges[i].setdebug(dbgout);
+#endif
+}
+
 class KernCollider
 {
 public:
-    KernCollider(GR_MAYBE_UNUSED json *dbg) : _miny(-1e38f), _maxy(1e38f) { };
+    KernCollider(json *dbg);
     ~KernCollider() throw() { };
     bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, float margin,
             const Position &currShift, const Position &offsetPrev, int dir,
             float ymin, float ymax, json * const dbgout);
     bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, json * const dbgout);
     Position resolve(Segment *seg, Slot *slot, int dir, float margin, json * const dbgout);
     void shift(const Position &mv, int dir);
 
@@ -208,13 +217,29 @@ private:
     Segment * _seg;
     Vector<float> _nearEdges; // closest potential collision in each slice
     Vector<Slot*> _slotNear;
 #endif
 };	// end of class KernCollider
 
 
 inline
-float sqr(float x) { return x * x; }
+float sqr(float x) {
+    return x * x;
+}
 
+inline
+KernCollider::KernCollider(GR_MAYBE_UNUSED json *dbg)
+: _target(0),
+  _margin(0.0f),
+  _miny(-1e38f),
+  _maxy(1e38f),
+  _sliceWidth(0.0f),
+  _mingap(0.0f),
+  _xbound(0.0)
+{
+#if !defined GRAPHITE2_NTRACING
+    _seg = 0;
+#endif
+};
 
 };  // end of namespace graphite2
 
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/inc/Compression.h
@@ -0,0 +1,120 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2015, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street, 
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+#include <iterator>
+
+#if ((defined GCC_VERSION && GCC_VERSION >= 302) || (defined __INTEL_COMPILER && __INTEL_COMPILER >= 800) || defined(__clang__))
+    #define expect(expr,value)    (__builtin_expect ((expr),(value)) )
+#else
+    #define expect(expr,value)    (expr)
+#endif
+
+#define likely(expr)     expect((expr) != 0, 1)
+#define unlikely(expr)   expect((expr) != 0, 0)
+
+
+namespace
+{
+
+#if defined(_MSC_VER)
+typedef unsigned __int8 u8;
+typedef unsigned __int16 u16;
+typedef unsigned __int32 u32;
+typedef unsigned __int64 u64;
+#else
+#include <stdint.h>
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+#endif
+
+ptrdiff_t const     MINMATCH  = 4;
+
+template<int S>
+inline 
+void unaligned_copy(void * d, void const * s) {
+  ::memcpy(d, s, S);
+}
+
+inline
+size_t align(size_t p) {
+    return (p + sizeof(unsigned long)-1) & ~(sizeof(unsigned long)-1);
+}
+
+inline
+u8 * overrun_copy(u8 * d, u8 const * s, size_t n) {
+    size_t const WS = sizeof(unsigned long);
+    u8 const * e = s + n;
+    do 
+    {
+        unaligned_copy<WS>(d, s);
+        d += WS;
+        s += WS;
+    }
+    while (s < e);
+    d-=(s-e);
+    
+    return d;
+}
+
+
+inline
+u8 * fast_copy(u8 * d, u8 const * s, size_t n) {
+    size_t const WS = sizeof(unsigned long);
+    size_t wn = n/WS;
+    while (wn--) 
+    {
+        unaligned_copy<WS>(d, s);
+        d += WS;
+        s += WS;
+    }
+    n &= WS-1;
+    while (n--) {*d++ = *s++; }
+    
+    return d;
+}
+
+
+inline 
+u8 * copy(u8 * d, u8 const * s, size_t n) {
+    if (likely(d>s+sizeof(unsigned long)))
+        return overrun_copy(d,s,n);
+    else 
+        while (n--) *d++ = *s++;
+    return d;
+}
+
+} // end of anonymous namespace
+
+
--- a/gfx/graphite2/src/inc/Decompressor.h
+++ b/gfx/graphite2/src/inc/Decompressor.h
@@ -1,52 +1,40 @@
-/*  Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
-    Copyright (c) 2015, SIL International
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2015, SIL International
     All rights reserved.
-    
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions are met:
-    
-    1. Redistributions of source code must retain the above copyright notice,
-       this list of conditions and the following disclaimer.
-    
-    2. Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-    
-    3. Neither the name of the copyright holder nor the names of its
-       contributors may be used to endorse or promote products derived from
-       this software without specific prior written permission.
-    
-    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
-    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
-    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
-    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
-    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
-    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
-    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
-    POSSIBILITY OF SUCH DAMAGE.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street, 
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
 */
+
 #pragma once
 
 #include <cstddef>
 
-namespace shrinker
+namespace lz4
 {
 
+// return value is either decompressed size of -1
 int decompress(void const *in, size_t in_size, void *out, size_t out_size);
-/*
-in:     inbuf --- compressed data
-out:    outbuf --- decompressed data to place in
-size:   decompressed(original) data size should be
-
-return value:
-    positive integer means decompress success and it's the sizeof decompressed data,
-    which should be equal to size.
-    or -1 means decompress failed
-*/
 
 } // end of namespace shrinker
 
 
--- a/gfx/graphite2/src/inc/Face.h
+++ b/gfx/graphite2/src/inc/Face.h
@@ -38,16 +38,17 @@ of the License or (at your option) any l
 
 namespace graphite2 {
 
 class Cmap;
 class FileFace;
 class GlyphCache;
 class NameTable;
 class json;
+class Font;
 
 
 using TtfUtil::Tag;
 
 // These are the actual tags, as distinct from the consecutive IDs in TtfUtil.h
 
 class Face
 {
@@ -169,16 +170,18 @@ class Face::Table
 {
     const Face *            _f;
     mutable const byte *    _p;
     uint32                  _sz;
     bool                    _compressed;
 
     Error decompress();
 
+    void releaseBuffers();
+
 public:
     Table() throw();
     Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw();
     Table(const Table & rhs) throw();
     ~Table() throw();
 
     operator const byte * () const throw();
 
@@ -197,20 +200,17 @@ Face::Table::Table(const Table & rhs) th
 : _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed)
 {
     rhs._p = 0;
 }
 
 inline
 Face::Table::~Table() throw()
 {
-    if (_compressed)
-        free(const_cast<byte *>(_p));
-    else if (_p && _f->m_ops.release_table)
-        (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
+    releaseBuffers();
 }
 
 inline
 Face::Table::operator const byte * () const throw()
 {
     return _p;
 }
 
--- a/gfx/graphite2/src/inc/FeatureMap.h
+++ b/gfx/graphite2/src/inc/FeatureMap.h
@@ -51,17 +51,17 @@ private:
 };
 
 class FeatureRef
 {
     typedef uint32      chunk_t;
     static const uint8  SIZEOF_CHUNK = sizeof(chunk_t)*8;
 
 public:
-    FeatureRef() : m_nameValues(0) {}
+    FeatureRef();
     FeatureRef(const Face & face, unsigned short & bits_offset, uint32 max_val,
                uint32 name, uint16 uiName, uint16 flags,
                FeatureSetting *settings, uint16 num_set) throw();
     ~FeatureRef() throw();
 
     bool applyValToFeature(uint32 val, Features& pDest) const; //defined in GrFaceImp.h
     void maskFeature(Features & pDest) const {
     if (m_index < pDest.size())                 //defensive
@@ -94,16 +94,26 @@ private:
     byte    m_bits,             // how many bits to shift the value into place
             m_index;            // index into the array to find the ulong to mask
 
 private:        //unimplemented
     FeatureRef& operator=(const FeatureRef&);
 };
 
 
+inline
+FeatureRef::FeatureRef()
+: m_pFace(0), m_nameValues(0),
+  m_mask(0), m_max(0), m_id(0),
+  m_nameid(0), m_flags(0), m_numSet(0),
+  m_bits(0), m_index(0)
+{
+}
+
+
 class NameAndFeatureRef
 {
   public:
     NameAndFeatureRef(uint32 name = 0) : m_name(name) , m_pFRef(NULL){}
     NameAndFeatureRef(const FeatureRef* p/*not NULL*/) : m_name(p->getId()), m_pFRef(p) {}
 
     bool operator<(const NameAndFeatureRef& rhs) const //orders by m_name
         {   return m_name<rhs.m_name; }
--- a/gfx/graphite2/src/inc/GlyphCache.h
+++ b/gfx/graphite2/src/inc/GlyphCache.h
@@ -34,43 +34,41 @@ of the License or (at your option) any l
 
 namespace graphite2 {
 
 class Face;
 class FeatureVal;
 class Segment;
 
 
-class SlantBox
+struct SlantBox
 {
-public:
-    SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {}; 
+    static const SlantBox empty;
+
+//    SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
     float width() const { return sa - si; }
     float height() const { return da - di; }
     float si; // min
     float di; // min
     float sa; // max
     float da; // max
 };
 
-static SlantBox nullSlant(0, 0, 0, 0);
 
-class BBox
+struct BBox
 {
-public:
     BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {};
     float width() const { return xa - xi; }
     float height() const { return ya - yi; }
     float xi; // min
     float yi; // min
     float xa; // max
     float ya; // max
 };
 
-static BBox nullBBox(0, 0, 0, 0);
 
 class GlyphBox
 {
     GlyphBox(const GlyphBox &);
     GlyphBox & operator = (const GlyphBox &);
 
 public:
     GlyphBox(uint8 numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {}; 
@@ -90,41 +88,40 @@ private:
 
 class GlyphCache
 {
     class Loader;
 
     GlyphCache(const GlyphCache&);
     GlyphCache& operator=(const GlyphCache&);
 
-    static const Rect nullRect;
-
 public:
     GlyphCache(const Face & face, const uint32 face_options);
     ~GlyphCache();
 
     unsigned short  numGlyphs() const throw();
     unsigned short  numAttrs() const throw();
     unsigned short  unitsPerEm() const throw();
 
     const GlyphFace *glyph(unsigned short glyphid) const;      //result may be changed by subsequent call with a different glyphid
     const GlyphFace *glyphSafe(unsigned short glyphid) const;
     float            getBoundingMetric(unsigned short glyphid, uint8 metric) const;
     uint8            numSubBounds(unsigned short glyphid) const;
     float            getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const;
-    const Rect &     slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : nullRect; }
+    const Rect &     slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : _empty_slant_box; }
     const SlantBox & getBoundingSlantBox(unsigned short glyphid) const;
     const BBox &     getBoundingBBox(unsigned short glyphid) const;
     const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const;
     const BBox &     getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const;
     bool             check(unsigned short glyphid) const;
 
     CLASS_NEW_DELETE;
     
 private:
+    const Rect            _empty_slant_box;
     const Loader        * _glyph_loader;
     const GlyphFace *   * _glyphs;
     GlyphBox        *   * _boxes;
     unsigned short        _num_glyphs,
                           _num_attrs,
                           _upem;
 };
 
@@ -144,17 +141,17 @@ inline
 unsigned short  GlyphCache::unitsPerEm() const throw()
 {
     return _upem;
 }
 
 inline
 bool GlyphCache::check(unsigned short glyphid) const
 {
-    return glyphid < _num_glyphs;
+    return _boxes && glyphid < _num_glyphs;
 }
 
 inline
 const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const
 {
     return glyphid < _num_glyphs ? glyph(glyphid) : NULL;
 }
 
@@ -172,17 +169,17 @@ float GlyphCache::getBoundingMetric(unsi
         case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f);    // sum_max
         case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f);    // diff_max
         default: return 0.;
     }
 }
 
 inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const
 {
-    return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : nullSlant;
+    return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : SlantBox::empty;
 }
 
 inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
 {
     return *(BBox *)(&(glyph(glyphid)->theBBox()));
 }
 
 inline
@@ -202,24 +199,22 @@ float GlyphCache::getSubBoundingMetric(u
         case 7: return b->subVal(subindex, 1).tr.y;
         default: return 0.;
     }
 }
 
 inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const
 {
     GlyphBox *b = _boxes[glyphid];
-//    if (b == NULL || subindex >= b->num()) return nullSlant;
     return *(SlantBox *)(b->subs() + 2 * subindex + 1);
 }
 
 inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const
 {
     GlyphBox *b = _boxes[glyphid];
-//    if (b == NULL || subindex >= b->num()) return nullBBox;
     return *(BBox *)(b->subs() + 2 * subindex);
 }
 
 inline
 uint8 GlyphCache::numSubBounds(unsigned short glyphid) const
 {
     return _boxes[glyphid] ? _boxes[glyphid]->num() : 0;
 }
--- a/gfx/graphite2/src/inc/Intervals.h
+++ b/gfx/graphite2/src/inc/Intervals.h
@@ -139,16 +139,19 @@ private:
     const_iterator  find_exclusion_under(float x) const;
 };
 
 
 inline
 Zones::Zones()
 : _margin_len(0), _margin_weight(0), _pos(0), _posm(0)
 {
+#if !defined GRAPHITE2_NTRACING
+    _dbg = 0;
+#endif
     _exclusions.reserve(8);
 }
 
 inline
 Zones::Exclusion::Exclusion(float x_, float xm_, float smi, float smxi, float c_)
 : x(x_), xm(xm_), c(c_), sm(smi), smx(smxi), open(false)
 { }
 
--- a/gfx/graphite2/src/inc/List.h
+++ b/gfx/graphite2/src/inc/List.h
@@ -100,16 +100,17 @@ private:
 template <typename T>
 inline 
 void Vector<T>::reserve(size_t n)
 {
     if (n > capacity()) 
     {
         const ptrdiff_t sz = size();
         m_first = static_cast<T*>(realloc(m_first, n*sizeof(T)));
+        if (!m_first)   std::abort();
         m_last  = m_first + sz;
         m_end   = m_first + n;
     }
 }
 
 template <typename T>
 inline
 void Vector<T>::resize(size_t n, const T & v) {
--- a/gfx/graphite2/src/inc/Main.h
+++ b/gfx/graphite2/src/inc/Main.h
@@ -80,25 +80,25 @@ struct telemetry  {};
 // typesafe wrapper around malloc for simple types
 // use free(pointer) to deallocate
 
 template <typename T> T * gralloc(size_t n)
 {
 #ifdef GRAPHITE2_TELEMETRY
     telemetry::count_bytes(sizeof(T) * n);
 #endif
-    return reinterpret_cast<T*>(malloc(sizeof(T) * n));
+    return static_cast<T*>(malloc(sizeof(T) * n));
 }
 
 template <typename T> T * grzeroalloc(size_t n)
 {
 #ifdef GRAPHITE2_TELEMETRY
     telemetry::count_bytes(sizeof(T) * n);
 #endif
-    return reinterpret_cast<T*>(calloc(n, sizeof(T)));
+    return static_cast<T*>(calloc(n, sizeof(T)));
 }
 
 template <typename T>
 inline T min(const T a, const T b)
 {
     return a < b ? a : b;
 }
 
@@ -123,10 +123,10 @@ inline T max(const T a, const T b)
 #ifdef __GNUC__
 #define GR_MAYBE_UNUSED __attribute__((unused))
 #else
 #define GR_MAYBE_UNUSED
 #endif
 
 #ifdef _MSC_VER
 #pragma warning(disable: 4800)
-#pragma warning(once: 4355)
+#pragma warning(disable: 4355)
 #endif
--- a/gfx/graphite2/src/inc/Pass.h
+++ b/gfx/graphite2/src/inc/Pass.h
@@ -48,19 +48,20 @@ enum passtype;
 class Pass
 {   
 public:
     Pass();
     ~Pass();
     
     bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face,
         enum passtype pt, uint32 version, Error &e);
-    bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const;
+    bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const;
     void init(Silf *silf) { m_silf = silf; }
-    byte flags() const { return m_flags; }
+    byte collisionLoops() const { return m_numCollRuns; }
+    bool reverseDir() const { return m_isReverseDir; }
 
     CLASS_NEW_DELETE
 private:
     void    findNDoRule(Slot* & iSlot, vm::Machine &, FiniteStateMachine& fsm) const;
     int     doAction(const vm::Machine::Code* codeptr, Slot * & slot_out, vm::Machine &) const;
     bool    testPassConstraint(vm::Machine & m) const;
     bool    testConstraint(const Rule & r, vm::Machine &) const;
     bool    readRules(const byte * rule_map, const size_t num_entries,
@@ -68,17 +69,17 @@ private:
                      const uint16 * o_constraint, const byte *constraint_data, 
                      const uint16 * o_action, const byte * action_data,
                      Face &, enum passtype pt, Error &e);
     bool    readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e);
     bool    readRanges(const byte * ranges, size_t num_ranges, Error &e);
     uint16  glyphToCol(const uint16 gid) const;
     bool    runFSM(FiniteStateMachine & fsm, Slot * slot) const;
     void    dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const;
-    void    dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const;
+    void    dumpRuleEventOutput(const FiniteStateMachine & fsm, vm::Machine & m, const Rule & r, Slot * os) const;
     void    adjustSlot(int delta, Slot * & slot_out, SlotMap &) const;
     bool    collisionShift(Segment *seg, int dir, json * const dbgout) const;
     bool    collisionKern(Segment *seg, int dir, json * const dbgout) const;
     bool    collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const;
     bool    resolveCollisions(Segment *seg, Slot *slot, Slot *start, ShiftCollider &coll, bool isRev,
                      int dir, bool &moved, bool &hasCol, json * const dbgout) const;
     float   resolveKern(Segment *seg, Slot *slot, Slot *start, KernCollider &coll, int dir,
                      float &ymin, float &ymax, json *const dbgout) const;
@@ -88,28 +89,30 @@ private:
     Rule              * m_rules; // rules
     RuleEntry         * m_ruleMap;
     uint16            * m_startStates; // prectxt length
     uint16            * m_transitions;
     State             * m_states;
     vm::Machine::Code * m_codes;
     byte              * m_progs;
 
-    byte   m_flags;
+    byte   m_numCollRuns;
+    byte   m_kernColls;
     byte   m_iMaxLoop;
     uint16 m_numGlyphs;
     uint16 m_numRules;
     uint16 m_numStates;
     uint16 m_numTransition;
     uint16 m_numSuccess;
     uint16 m_successStart;
     uint16 m_numColumns;
     byte m_minPreCtxt;
     byte m_maxPreCtxt;
     byte m_colThreshold;
+    bool m_isReverseDir;
     vm::Machine::Code m_cPConstraint;
     
 private:        //defensive
     Pass(const Pass&);
     Pass& operator=(const Pass&);
 };
 
 } // namespace graphite2
--- a/gfx/graphite2/src/inc/Rule.h
+++ b/gfx/graphite2/src/inc/Rule.h
@@ -36,28 +36,36 @@ struct Rule {
   const vm::Machine::Code * constraint, 
                  * action;
   unsigned short   sort;
   byte             preContext;
 #ifndef NDEBUG
   uint16           rule_idx;
 #endif
 
-  Rule() : constraint(0), action(0), sort(0), preContext(0) {}
-  ~Rule();
+  Rule();
+  ~Rule() {}
 
   CLASS_NEW_DELETE;
 
 private:
   Rule(const Rule &);
   Rule & operator = (const Rule &);
 };
 
-inline Rule::~Rule()
+inline
+Rule::Rule()
+: constraint(0),
+  action(0),
+  sort(0),
+  preContext(0)
 {
+#ifndef NDEBUG
+  rule_idx = 0;
+#endif
 }
 
 
 struct RuleEntry
 {
   const Rule   * rule;
 
   inline
@@ -89,40 +97,43 @@ bool State::empty() const
     return rules_end == rules;
 }
 
 
 class SlotMap
 {
 public:
   enum {MAX_SLOTS=64};
-  SlotMap(Segment & seg);
+  SlotMap(Segment & seg, uint8 direction);
   
   Slot       * * begin();
   Slot       * * end();
   size_t         size() const;
   unsigned short context() const;
   void           reset(Slot &, unsigned short);
   
   Slot * const & operator[](int n) const;
   Slot       * & operator [] (int);
   void           pushSlot(Slot * const slot);
-  void           collectGarbage();
+  void           collectGarbage(Slot *& aSlot);
 
   Slot         * highwater() { return m_highwater; }
   void           highwater(Slot *s) { m_highwater = s; m_highpassed = false; }
   bool           highpassed() const { return m_highpassed; }
   void           highpassed(bool v) { m_highpassed = v; }
 
+  uint8          dir() const { return m_dir; }
+
   Segment &    segment;
 private:
   Slot         * m_slot_map[MAX_SLOTS+1];
   unsigned short m_size;
   unsigned short m_precontext;
   Slot         * m_highwater;
+  uint8          m_dir;
   bool           m_highpassed;
 };
 
 
 class FiniteStateMachine
 {
 public:
   enum {MAX_RULES=128};
@@ -226,18 +237,18 @@ void FiniteStateMachine::Rules::accumula
       return;
     }
   }
   while (rre != rrend && out != lrend) { *out++ = *rre++; }
   m_end = out;
 }
 
 inline
-SlotMap::SlotMap(Segment & seg)
-: segment(seg), m_size(0), m_precontext(0), m_highwater(0), m_highpassed(false)
+SlotMap::SlotMap(Segment & seg, uint8 direction)
+: segment(seg), m_size(0), m_precontext(0), m_highwater(0), m_dir(direction), m_highpassed(false)
 {
     m_slot_map[0] = 0;
 }
 
 inline
 Slot * * SlotMap::begin()
 {
   return &m_slot_map[1]; // allow map to go 1 before slot_map when inserting
--- a/gfx/graphite2/src/inc/SegCache.h
+++ b/gfx/graphite2/src/inc/SegCache.h
@@ -258,17 +258,17 @@ public:
 
     CLASS_NEW_DELETE
 private:
     void freeLevel(SegCacheStore * store, SegCachePrefixArray prefixes, size_t level);
     void purgeLevel(SegCacheStore * store, SegCachePrefixArray prefixes, size_t level,
                     unsigned long long minAccessCount, unsigned long long oldAccessTime);
 
     uint16 m_prefixLength;
-    uint16 m_maxCachedSegLength;
+//    uint16 m_maxCachedSegLength;
     size_t m_segmentCount;
     SegCachePrefixArray m_prefixes;
     Features m_features;
     mutable unsigned long long m_totalAccessCount;
     mutable unsigned long long m_totalMisses;
     float m_purgeFactor;
 };
 
--- a/gfx/graphite2/src/inc/Segment.h
+++ b/gfx/graphite2/src/inc/Segment.h
@@ -34,26 +34,26 @@ of the License or (at your option) any l
 #include "inc/Face.h"
 #include "inc/FeatureVal.h"
 #include "inc/GlyphCache.h"
 #include "inc/GlyphFace.h"
 //#include "inc/Silf.h"
 #include "inc/Slot.h"
 #include "inc/Position.h"
 #include "inc/List.h"
-#include "inc/Bidi.h"
+//#include "inc/Bidi.h"
 #include "inc/Collider.h"
 
 #define MAX_SEG_GROWTH_FACTOR  256
 
 namespace graphite2 {
 
 typedef Vector<Features>        FeatureList;
 typedef Vector<Slot *>          SlotRope;
-typedef Vector<int16 *>        AttributeRope;
+typedef Vector<int16 *>         AttributeRope;
 typedef Vector<SlotJustify *>   JustifyRope;
 
 #ifndef GRAPHITE2_NSEGCACHE
 class SegmentScopeState;
 #endif
 class Font;
 class Segment;
 class Silf;
@@ -96,17 +96,16 @@ public:
     void extendLength(int num) { m_numGlyphs += num; }
     Position advance() const { return m_advance; }
     bool runGraphite() { if (m_silf) return m_face->runGraphite(this, m_silf); else return true;};
     void chooseSilf(uint32 script) { m_silf = m_face->chooseSilf(script); }
     const Silf *silf() const { return m_silf; }
     unsigned int charInfoCount() const { return m_numCharinfo; }
     const CharInfo *charinfo(unsigned int index) const { return index < m_numCharinfo ? m_charinfo + index : NULL; }
     CharInfo *charinfo(unsigned int index) { return index < m_numCharinfo ? m_charinfo + index : NULL; }
-    int8 dir() const { return m_dir; }
 
     Segment(unsigned int numchars, const Face* face, uint32 script, int dir);
     ~Segment();
 #ifndef GRAPHITE2_NSEGCACHE
     SegmentScopeState setScope(Slot * firstSlot, Slot * lastSlot, size_t subLength);
     void removeScope(SegmentScopeState & state);
     void append(const Segment &other);
     void splice(size_t offset, size_t length, Slot * const startSlot,
@@ -119,56 +118,61 @@ public:
     void first(Slot *p) { m_first = p; }
     Slot *last() { return m_last; }
     void last(Slot *p) { m_last = p; }
     void appendSlot(int i, int cid, int gid, int fid, size_t coffset);
     Slot *newSlot();
     void freeSlot(Slot *);
     SlotJustify *newJustify();
     void freeJustify(SlotJustify *aJustify);
-    Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isFinal = true);
+    Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isRtl = false, bool isFinal = true);
     void associateChars(int offset, int num);
     void linkClusters(Slot *first, Slot *last);
     uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); }
     uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); }
     int addFeatures(const Features& feats) { m_feats.push_back(feats); return m_feats.size() - 1; }
     uint32 getFeature(int index, uint8 findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); }
     void setFeature(int index, uint8 findex, uint32 val) {
         const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); 
         if (pFR)
         {
             if (val > pFR->maxVal()) val = pFR->maxVal();
             pFR->applyValToFeature(val, m_feats[index]);
         } }
+    int8 dir() const { return m_dir; }
     void dir(int8 val) { m_dir = val; }
+    bool currdir() const { return ((m_dir >> 6) ^ m_dir) & 1; }
     unsigned int passBits() const { return m_passBits; }
     void mergePassBits(const unsigned int val) { m_passBits &= val; }
     int16 glyphAttr(uint16 gid, uint16 gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; }
-    int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const;
+    int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const;
     float glyphAdvance(uint16 gid) const { return m_face->glyphs().glyph(gid)->theAdvance().x; }
     const Rect &theGlyphBBoxTemporary(uint16 gid) const { return m_face->glyphs().glyph(gid)->theBBox(); }   //warning value may become invalid when another glyph is accessed
     Slot *findRoot(Slot *is) const { return is->attachedTo() ? findRoot(is->attachedTo()) : is; }
     int numAttrs() const { return m_silf->numUser(); }
     int defaultOriginal() const { return m_defaultOriginal; }
     const Face * getFace() const { return m_face; }
     const Features & getFeatures(unsigned int /*charIndex*/) { assert(m_feats.size() == 1); return m_feats[0]; }
-    void bidiPass(uint8 aBidi, int paradir, uint8 aMirror);
+    void bidiPass(int paradir, uint8 aMirror);
+    int8 getSlotBidiClass(Slot *s) const;
+    void doMirror(uint16 aMirror);
     Slot *addLineEnd(Slot *nSlot);
     void delLineEnd(Slot *s);
     bool hasJustification() const { return m_justifies.size() != 0; }
+    void reverseSlots();
 
     bool isWhitespace(const int cid) const;
     bool hasCollisionInfo() const { return m_collisions != 0; }
     SlotCollision *collisionInfo(const Slot *s) const { return m_collisions ? m_collisions + s->index() : NULL; }
 
     CLASS_NEW_DELETE
 
 public:       //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir);
     bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
-    void finalise(const Font *font);
+    void finalise(const Font *font, bool reverse=false);
     float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast);
     bool initCollisions();
   
 private:
     Position        m_advance;          // whole segment advance
     SlotRope        m_slots;            // Vector of slot buffers
     AttributeRope   m_userAttrs;        // Vector of userAttrs buffers
     JustifyRope     m_justifies;        // Slot justification info buffers
@@ -185,34 +189,44 @@ private:
                     m_numGlyphs,
                     m_numCharinfo,      // size of the array and number of input characters
                     m_passBits;         // if bit set then skip pass
     int             m_defaultOriginal;  // number of whitespace chars in the string
     int8            m_dir;
     uint8           m_flags;            // General purpose flags
 };
 
-
+inline
+int8 Segment::getSlotBidiClass(Slot *s) const
+{
+    int8 res = s->getBidiClass();
+    if (res != -1) return res;
+    res = glyphAttr(s->gid(), m_silf->aBidi());
+    s->setBidiClass(res);
+    return res;
+}
 
 inline
-void Segment::finalise(const Font *font)
+void Segment::finalise(const Font *font, bool reverse)
 {
     if (!m_first) return;
 
-    m_advance = positionSlots(font);
+    m_advance = positionSlots(font, m_first, m_last, m_silf->dir(), true);
     //associateChars(0, m_numCharinfo);
+    if (reverse && currdir() != (m_dir & 1))
+        reverseSlots();
     linkClusters(m_first, m_last);
 }
 
 inline
-int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const {
+int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const {
     if (attrLevel > 0)
     {
         Slot *is = findRoot(iSlot);
-        return is->clusterMetric(this, metric, attrLevel);
+        return is->clusterMetric(this, metric, attrLevel, rtl);
     }
     else
         return m_face->getGlyphMetric(iSlot->gid(), metric);
 }
 
 inline
 bool Segment::isWhitespace(const int cid) const
 {
--- a/gfx/graphite2/src/inc/Silf.h
+++ b/gfx/graphite2/src/inc/Silf.h
@@ -89,16 +89,17 @@ public:
     uint8 substitutionPass() const { return m_sPass; }
     uint8 positionPass() const { return m_pPass; }
     uint8 justificationPass() const { return m_jPass; }
     uint8 bidiPass() const { return m_bPass; }
     uint8 numPasses() const { return m_numPasses; }
     uint8 maxCompPerLig() const { return m_iMaxComp; }
     uint16 numClasses() const { return m_nClass; }
     byte  flags() const { return m_flags; }
+    byte  dir() const { return m_dir; }
     uint8 numJustLevels() const { return m_numJusts; }
     Justinfo *justAttrs() const { return m_justs; }
     uint16 endLineGlyphid() const { return m_gEndLine; }
     const gr_faceinfo *silfInfo() const { return &m_silfinfo; }
 
     CLASS_NEW_DELETE;
 
 private:
@@ -108,17 +109,17 @@ private:
     Pass          * m_passes;
     Pseudo        * m_pseudos;
     uint32        * m_classOffsets;
     uint16        * m_classData;
     Justinfo      * m_justs;
     uint8           m_numPasses;
     uint8           m_numJusts;
     uint8           m_sPass, m_pPass, m_jPass, m_bPass,
-                    m_flags;
+                    m_flags, m_dir;
 
     uint8       m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
                 m_iMaxComp, m_aCollision;
     uint16      m_aLig, m_numPseudo, m_nClass, m_nLinear,
                 m_gEndLine;
     gr_faceinfo m_silfinfo;
     
     void releaseBuffers() throw();
--- a/gfx/graphite2/src/inc/Slot.h
+++ b/gfx/graphite2/src/inc/Slot.h
@@ -27,25 +27,23 @@ of the License or (at your option) any l
 #pragma once
 
 #include "graphite2/Types.h"
 #include "graphite2/Segment.h"
 #include "inc/Main.h"
 #include "inc/Font.h"
 #include "inc/Position.h"
 
-
-
 namespace graphite2 {
 
 typedef gr_attrCode attrCode;
 
 class GlyphFace;
+class SegCacheEntry;
 class Segment;
-class SegCacheEntry;
 
 struct SlotJustify
 {
     static const int NUMJUSTPARAMS = 5;
 
     SlotJustify(const SlotJustify &);
     SlotJustify & operator = (const SlotJustify &);
 
@@ -77,43 +75,44 @@ public:
     float advance() const { return m_advance.x; }
     void advance(Position &val) { m_advance = val; }
     Position advancePos() const { return m_advance; }
     int before() const { return m_before; }
     int after() const { return m_after; }
     uint32 index() const { return m_index; }
     void index(uint32 val) { m_index = val; }
 
-    Slot();
+    Slot(int16 *m_userAttr = NULL);
     void set(const Slot & slot, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars);
     Slot *next() const { return m_next; }
     void next(Slot *s) { m_next = s; }
     Slot *prev() const { return m_prev; }
     void prev(Slot *s) { m_prev = s; }
     uint16 glyph() const { return m_realglyphid ? m_realglyphid : m_glyphid; }
     void setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph = NULL);
     void setRealGid(uint16 realGid) { m_realglyphid = realGid; }
     void adjKern(const Position &pos) { m_shift = m_shift + pos; m_advance = m_advance + pos; }
     void origin(const Position &pos) { m_position = pos + m_shift; }
     void originate(int ind) { m_original = ind; }
     int original() const { return m_original; }
     void before(int ind) { m_before = ind; }
     void after(int ind) { m_after = ind; }
     bool isBase() const { return (!m_parent); }
     void update(int numSlots, int numCharInfo, Position &relpos);
-    Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal);
+    Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal);
     bool isDeleted() const { return (m_flags & DELETED) ? true : false; }
     void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; }
     bool isCopied() const { return (m_flags & COPIED) ? true : false; }
     void markCopied(bool state) { if (state) m_flags |= COPIED; else m_flags &= ~COPIED; }
     bool isPositioned() const { return (m_flags & POSITIONED) ? true : false; }
     void markPositioned(bool state) { if (state) m_flags |= POSITIONED; else m_flags &= ~POSITIONED; }
     bool isInsertBefore() const { return !(m_flags & INSERTED); }
     uint8 getBidiLevel() const { return m_bidiLevel; }
     void setBidiLevel(uint8 level) { m_bidiLevel = level; }
+    int8 getBidiClass(const Segment *seg);
     int8 getBidiClass() const { return m_bidiCls; }
     void setBidiClass(int8 cls) { m_bidiCls = cls; }
     int16 *userAttrs() const { return m_userAttr; }
     void userAttrs(int16 *p) { m_userAttr = p; }
     void markInsertBefore(bool state) { if (!state) m_flags |= INSERTED; else m_flags &= ~INSERTED; }
     void setAttr(Segment* seg, attrCode ind, uint8 subindex, int16 val, const SlotMap & map);
     int getAttr(const Segment *seg, attrCode ind, uint8 subindex) const;
     int getJustify(const Segment *seg, uint8 level, uint8 subindex) const;
@@ -125,17 +124,17 @@ public:
     Slot* firstChild() const { return m_child; }
     void firstChild(Slot *ap) { m_child = ap; }
     bool child(Slot *ap);
     Slot* nextSibling() const { return m_sibling; }
     void nextSibling(Slot *ap) { m_sibling = ap; }
     bool sibling(Slot *ap);
     bool removeChild(Slot *ap);
     bool removeSibling(Slot *ap);
-    int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel);
+    int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel, bool rtl);
     void positionShift(Position a) { m_position += a; }
     void floodShift(Position adj);
     float just() const { return m_just; }
     void just(float j) { m_just = j; }
     Slot *nextInCluster(const Slot *s) const;
     bool isChildOf(const Slot *base) const;
 
     CLASS_NEW_DELETE
--- a/gfx/graphite2/src/inc/Sparse.h
+++ b/gfx/graphite2/src/inc/Sparse.h
@@ -51,17 +51,17 @@ private:
     static const unsigned char  SIZEOF_CHUNK = (sizeof(mask_t) - sizeof(key_type))*8;
 
     struct chunk
     {
         mask_t          mask:SIZEOF_CHUNK;
         key_type        offset;
     };
 
-    static chunk  empty_chunk;
+    static const chunk  empty_chunk;
     sparse(const sparse &);
     sparse & operator = (const sparse &);
 
 public:
     template<typename I>
     sparse(I first, const I last);
     sparse() throw();
     ~sparse() throw();
@@ -83,17 +83,17 @@ private:
     }           m_array;
     key_type    m_nchunks;
 };
 
 
 inline
 sparse::sparse() throw() : m_nchunks(0)
 {
-    m_array.map = &empty_chunk;
+    m_array.map = const_cast<graphite2::sparse::chunk *>(&empty_chunk);
 }
 
 
 template <typename I>
 sparse::sparse(I attr, const I last)
 : m_nchunks(0)
 {
     m_array.map = 0;
@@ -108,17 +108,17 @@ sparse::sparse(I attr, const I last)
         if (v.first <= lastkey) { m_nchunks = 0; return; }
 
         lastkey = v.first;
         const key_type k = v.first / SIZEOF_CHUNK;
         if (k >= m_nchunks) m_nchunks = k+1;
     }
     if (m_nchunks == 0)
     {
-        m_array.map=&empty_chunk;
+        m_array.map=const_cast<graphite2::sparse::chunk *>(&empty_chunk);
         return;
     }
 
     m_array.values = grzeroalloc<mapped_type>((m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)
                                                  / sizeof(mapped_type)
                                                  + n_values);
 
     if (m_array.values == 0)
--- a/gfx/graphite2/src/inc/TtfUtil.h
+++ b/gfx/graphite2/src/inc/TtfUtil.h
@@ -132,21 +132,21 @@ public:
     int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId,
         int *nameIdList, int cNameIds, short *langIdList);
     void SwapWString(void * pWStr, size_t nSize = 0); // throw (std::invalid_argument);
 #endif
 
     ////////////////////////////////// cmap lookup tools 
     const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3, 
         int nEncodingId = 1, size_t length = 0);
-    bool CheckCmapSubtable4(const void * pCmap31 /*, unsigned int maxgid*/);
+    bool CheckCmapSubtable4(const void * pCmap31, size_t table_len /*, unsigned int maxgid*/);
     gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0);
     unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId,
         int * pRangeKey = 0);
-    bool CheckCmapSubtable12(const void *pCmap310 /*, unsigned int maxgid*/);
+    bool CheckCmapSubtable12(const void *pCmap310, size_t table_len /*, unsigned int maxgid*/);
     gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0);
     unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId,
         int * pRangeKey = 0);
 
     ///////////////////////////////// horizontal metric data for a glyph
     bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, 
         const void * pHhea, int & nLsb, unsigned int & nAdvWid);
 
--- a/gfx/graphite2/src/inc/opcodes.h
+++ b/gfx/graphite2/src/inc/opcodes.h
@@ -56,16 +56,17 @@ of the License or (at your option) any l
 //                    pushed.
 //        seg       = A reference to the Segment this code is running over.
 //        is        = The current slot index
 //        isb       = The original base slot index at the start of this rule
 //        isf       = The first positioned slot
 //        isl       = The last positioned slot
 //        ip        = The current instruction pointer
 //        endPos    = Position of advance of last cluster
+//        dir       = writing system directionality of the font
      
 
 // #define NOT_IMPLEMENTED     assert(false)
 #define NOT_IMPLEMENTED
 
 #define binop(op)           const int32 a = pop(); *sp = int32(*sp) op a
 #define use_params(n)       dp += n
 
@@ -308,16 +309,18 @@ STARTOP(insert)
     {
         newSlot->originate(newSlot->prev()->original());
         newSlot->after(newSlot->prev()->after());
     }
     else
     {
         newSlot->originate(seg.defaultOriginal());
     }
+    if (is == smap.highwater())
+        smap.highpassed(false);
     is = newSlot;
     seg.extendLength(1);
     if (map != &smap[-1]) 
         --map;
 ENDOP
 
 STARTOP(delete_)
     if (!is || is->isDeleted()) DIE
@@ -384,30 +387,30 @@ STARTOP(attr_set)
 ENDOP
 
 STARTOP(attr_add)
     declare_params(1);
     const attrCode      slat = attrCode(uint8(*param));
     const          int  val  = int(pop());
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
         flags |= POSITIONED;
     }
     int res = is->getAttr(&seg, slat, 0);
     is->setAttr(&seg, slat, 0, val + res, smap);
 ENDOP
 
 STARTOP(attr_sub)
     declare_params(1);
     const attrCode      slat = attrCode(uint8(*param));
     const          int  val  = int(pop());
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
         flags |= POSITIONED;
     }
     int res = is->getAttr(&seg, slat, 0);
     is->setAttr(&seg, slat, 0, res - val, smap);
 ENDOP
 
 STARTOP(attr_set_slot)
     declare_params(1);
@@ -426,17 +429,17 @@ STARTOP(iattr_set_slot)
 ENDOP
 
 STARTOP(push_slot_attr)
     declare_params(2);
     const attrCode      slat     = attrCode(uint8(param[0]));
     const int           slot_ref = int8(param[1]);
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
         flags |= POSITIONED;
     }
     slotref slot = slotat(slot_ref);
     if (slot)
     {
         int res = slot->getAttr(&seg, slat, 0);
         push(res);
     }
@@ -453,17 +456,17 @@ ENDOP
 
 STARTOP(push_glyph_metric)
     declare_params(3);
     const unsigned int  glyph_attr  = uint8(param[0]);
     const int           slot_ref    = int8(param[1]);
     const signed int    attr_level  = uint8(param[2]);
     slotref slot = slotat(slot_ref);
     if (slot)
-        push(seg.getGlyphMetric(slot, glyph_attr, attr_level));
+        push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir));
 ENDOP
 
 STARTOP(push_feat)
     declare_params(2);
     const unsigned int  feat        = uint8(param[0]);
     const int           slot_ref    = int8(param[1]);
     slotref slot = slotat(slot_ref);
     if (slot)
@@ -491,28 +494,28 @@ STARTOP(push_att_to_glyph_metric)
     const unsigned int  glyph_attr  = uint8(param[0]);
     const int           slot_ref    = int8(param[1]);
     const signed int    attr_level  = uint8(param[2]);
     slotref slot = slotat(slot_ref);
     if (slot)
     {
         slotref att = slot->attachedTo();
         if (att) slot = att;
-        push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level)));
+        push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)));
     }
 ENDOP
 
 STARTOP(push_islot_attr)
     declare_params(3);
     const attrCode  slat     = attrCode(uint8(param[0]));
     const int           slot_ref = int8(param[1]),
                         idx      = uint8(param[2]);
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
         flags |= POSITIONED;
     }
     slotref slot = slotat(slot_ref);
     if (slot)
     {
         int res = slot->getAttr(&seg, slat, idx);
         push(res);
     }
@@ -547,31 +550,31 @@ ENDOP
 
 STARTOP(iattr_add)
     declare_params(2);
     const attrCode      slat = attrCode(uint8(param[0]));
     const size_t        idx  = uint8(param[1]);
     const          int  val  = int(pop());
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
         flags |= POSITIONED;
     }
     int res = is->getAttr(&seg, slat, idx);
     is->setAttr(&seg, slat, idx, val + res, smap);
 ENDOP
 
 STARTOP(iattr_sub)
     declare_params(2);
     const attrCode      slat = attrCode(uint8(param[0]));
     const size_t        idx  = uint8(param[1]);
     const          int  val  = int(pop());
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
         flags |= POSITIONED;
     }
     int res = is->getAttr(&seg, slat, idx);
     is->setAttr(&seg, slat, idx, res - val, smap);
 ENDOP
 
 STARTOP(push_proc_state)
     use_params(1);
--- a/gfx/layers/apz/test/helper_bug982141.html
+++ b/gfx/layers/apz/test/helper_bug982141.html
@@ -1,15 +1,16 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=982141
 -->
 <head>
   <meta charset="utf-8">
+  <meta name="viewport" content="user-scalable=no">
   <title>Test for Bug 982141, helper page</title>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <script type="application/javascript">
 
     // -------------------------------------------------------------------
     // Infrastructure to get the test assertions to run at the right time.
     // -------------------------------------------------------------------
     var SimpleTest = window.opener.SimpleTest;
@@ -82,17 +83,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       var dpHeight = dpFields[3];
       SimpleTest.ok(dpWidth >= 50 && dpHeight >= 50,
                     "expected a displayport at least as large as the scrollable element");
 
       window.opener.finishTest();
     }
   </script>
 </head>
-<body style="overflow: hidden;"><!-- Make sure the root frame is not scrollable -->
+<body style="overflow: hidden;"><!-- This combined with the user-scalable=no ensures the root frame is not scrollable -->
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
   <!-- A scrollable subframe, with enough content to make it have a nonzero scroll range -->
   <div style="height: 50px; width: 50px; overflow: scroll">
     Line 1<br>
     Line 2<br>
     Line 3<br>
     Line 4<br>
     Line 5<br>
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -782,23 +782,23 @@ APZCCallbackHelper::SendSetTargetAPZCNot
   }
 }
 
 void
 APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
         nsIWidget* aWidget,
         const WidgetTouchEvent& aEvent,
         uint64_t aInputBlockId,
-        const nsRefPtr<SetAllowedTouchBehaviorCallback>& aCallback)
+        const SetAllowedTouchBehaviorCallback& aCallback)
 {
   nsTArray<TouchBehaviorFlags> flags;
   for (uint32_t i = 0; i < aEvent.touches.Length(); i++) {
     flags.AppendElement(widget::ContentHelper::GetAllowedTouchBehavior(aWidget, aEvent.touches[i]->mRefPoint));
   }
-  aCallback->Run(aInputBlockId, flags);
+  aCallback(aInputBlockId, flags);
 }
 
 void
 APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
 {
   nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId);
   if (!targetContent) {
     return;
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -3,39 +3,33 @@
  * 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/. */
 
 #ifndef mozilla_layers_APZCCallbackHelper_h
 #define mozilla_layers_APZCCallbackHelper_h
 
 #include "FrameMetrics.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/Function.h"
 #include "mozilla/layers/APZUtils.h"
 #include "nsIDOMWindowUtils.h"
 
 class nsIContent;
 class nsIDocument;
 class nsIPresShell;
 class nsIWidget;
 template<class T> struct already_AddRefed;
 template<class T> class nsCOMPtr;
 template<class T> class nsRefPtr;
 
 namespace mozilla {
 namespace layers {
 
-/* A base class for callbacks to be passed to
- * APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification. */
-struct SetAllowedTouchBehaviorCallback {
-public:
-  NS_INLINE_DECL_REFCOUNTING(SetAllowedTouchBehaviorCallback)
-  virtual void Run(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aFlags) const = 0;
-protected:
-  virtual ~SetAllowedTouchBehaviorCallback() {}
-};
+typedef Function<void(uint64_t, const nsTArray<TouchBehaviorFlags>&)>
+        SetAllowedTouchBehaviorCallback;
 
 /* This class contains some helper methods that facilitate implementing the
    GeckoContentController callback interface required by the AsyncPanZoomController.
    Since different platforms need to implement this interface in similar-but-
    not-quite-the-same ways, this utility class provides some helpful methods
    to hold code that can be shared across the different platform implementations.
  */
 class APZCCallbackHelper
@@ -157,17 +151,17 @@ public:
                                               const ScrollableLayerGuid& aGuid,
                                               uint64_t aInputBlockId);
 
     /* Figure out the allowed touch behaviors of each touch point in |aEvent|
      * and send that information to the provided callback. */
     static void SendSetAllowedTouchBehaviorNotification(nsIWidget* aWidget,
                                                          const WidgetTouchEvent& aEvent,
                                                          uint64_t aInputBlockId,
-                                                         const nsRefPtr<SetAllowedTouchBehaviorCallback>& aCallback);
+                                                         const SetAllowedTouchBehaviorCallback& aCallback);
 
     /* Notify content of a mouse scroll testing event. */
     static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
 
     /* Notify content that the repaint flush is complete. */
     static void NotifyFlushComplete();
 
     /* Temporarily ignore the Displayport for better paint performance. */
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "APZEventState.h"
 
 #include "ActiveElementManager.h"
 #include "APZCCallbackHelper.h"
 #include "gfxPrefs.h"
 #include "mozilla/BasicEvents.h"
+#include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "nsCOMPtr.h"
 #include "nsDocShell.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIScrollableFrame.h"
@@ -86,20 +87,20 @@ WidgetModifiersToDOMModifiers(mozilla::M
 
 namespace mozilla {
 namespace layers {
 
 static int32_t sActiveDurationMs = 10;
 static bool sActiveDurationMsSet = false;
 
 APZEventState::APZEventState(nsIWidget* aWidget,
-                             const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback)
+                             ContentReceivedInputBlockCallback&& aCallback)
   : mWidget(nullptr)  // initialized in constructor body
   , mActiveElementManager(new ActiveElementManager())
-  , mContentReceivedInputBlockCallback(aCallback)
+  , mContentReceivedInputBlockCallback(Move(aCallback))
   , mPendingTouchPreventedResponse(false)
   , mPendingTouchPreventedBlockId(0)
   , mEndTouchIsClick(false)
   , mTouchEndCancelled(false)
   , mActiveAPZTransforms(0)
 {
   nsresult rv;
   mWidget = do_GetWeakReference(aWidget, &rv);
@@ -239,17 +240,17 @@ APZEventState::ProcessLongTap(const nsCO
     nsEventStatus status =
         APZCCallbackHelper::DispatchSynthesizedMouseEvent(eMouseLongTap, time,
                                                           currentPoint,
                                                           aModifiers, widget);
     eventHandled = (status == nsEventStatus_eConsumeNoDefault);
     APZES_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
   }
 
-  mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, eventHandled);
+  mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
 }
 
 void
 APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId,
                                  nsEventStatus aApzResponse)
 {
@@ -261,23 +262,23 @@ APZEventState::ProcessTouchEvent(const W
       aEvent.mFlags.mMultipleActionsPrevented;
   bool sentContentResponse = false;
   switch (aEvent.mMessage) {
   case NS_TOUCH_START: {
     mTouchEndCancelled = false;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
-      mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
+      mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
           mPendingTouchPreventedBlockId, false);
       sentContentResponse = true;
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
-      mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, isTouchPrevented);
+      mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented);
       sentContentResponse = true;
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
       mPendingTouchPreventedBlockId = aInputBlockId;
     }
     break;
   }
@@ -323,17 +324,17 @@ void
 APZEventState::ProcessWheelEvent(const WidgetWheelEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId)
 {
   // If this event starts a swipe, indicate that it shouldn't result in a
   // scroll by setting defaultPrevented to true.
   bool defaultPrevented =
     aEvent.mFlags.mDefaultPrevented || aEvent.TriggersSwipe();
-  mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, defaultPrevented);
+  mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
 }
 
 void
 APZEventState::ProcessAPZStateChange(const nsCOMPtr<nsIDocument>& aDocument,
                                      ViewID aViewId,
                                      APZStateChange aChange,
                                      int aArg)
 {
@@ -403,17 +404,17 @@ APZEventState::ProcessAPZStateChange(con
     break;
   }
 }
 
 bool
 APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault)
 {
   if (mPendingTouchPreventedResponse) {
-    mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
+    mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
         mPendingTouchPreventedBlockId, aPreventDefault);
     mPendingTouchPreventedResponse = false;
     return true;
   }
   return false;
 }
 
 already_AddRefed<nsIWidget>
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -6,52 +6,48 @@
 #ifndef mozilla_layers_APZEventState_h
 #define mozilla_layers_APZEventState_h
 
 #include <stdint.h>
 
 #include "FrameMetrics.h"     // for ScrollableLayerGuid
 #include "Units.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/Function.h"
 #include "mozilla/layers/GeckoContentController.h"  // for APZStateChange
+#include "mozilla/nsRefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"  // for NS_INLINE_DECL_REFCOUNTING
 #include "nsIWeakReferenceUtils.h"  // for nsWeakPtr
-#include "mozilla/nsRefPtr.h"
 
 template <class> class nsCOMPtr;
 class nsIDocument;
 class nsIPresShell;
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
 class ActiveElementManager;
 
-struct ContentReceivedInputBlockCallback {
-public:
-  NS_INLINE_DECL_REFCOUNTING(ContentReceivedInputBlockCallback);
-  virtual void Run(const ScrollableLayerGuid& aGuid,
-                   uint64_t aInputBlockId,
-                   bool aPreventDefault) const = 0;
-protected:
-  virtual ~ContentReceivedInputBlockCallback() {}
-};
+typedef Function<void(const ScrollableLayerGuid&,
+                      uint64_t /* input block id */,
+                      bool /* prevent default */)>
+        ContentReceivedInputBlockCallback;
 
 /**
  * A content-side component that keeps track of state for handling APZ
  * gestures and sending APZ notifications.
  */
 class APZEventState {
   typedef GeckoContentController::APZStateChange APZStateChange;
   typedef FrameMetrics::ViewID ViewID;
 public:
   APZEventState(nsIWidget* aWidget,
-                const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback);
+                ContentReceivedInputBlockCallback&& aCallback);
 
   NS_INLINE_DECL_REFCOUNTING(APZEventState);
 
   void ProcessSingleTap(const CSSPoint& aPoint,
                         Modifiers aModifiers,
                         const ScrollableLayerGuid& aGuid);
   void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
                       const CSSPoint& aPoint,
@@ -71,17 +67,17 @@ public:
                              int aArg);
 private:
   ~APZEventState();
   bool SendPendingTouchPreventedResponse(bool aPreventDefault);
   already_AddRefed<nsIWidget> GetWidget() const;
 private:
   nsWeakPtr mWidget;
   nsRefPtr<ActiveElementManager> mActiveElementManager;
-  nsRefPtr<ContentReceivedInputBlockCallback> mContentReceivedInputBlockCallback;
+  ContentReceivedInputBlockCallback mContentReceivedInputBlockCallback;
   bool mPendingTouchPreventedResponse;
   ScrollableLayerGuid mPendingTouchPreventedGuid;
   uint64_t mPendingTouchPreventedBlockId;
   bool mEndTouchIsClick;
   bool mTouchEndCancelled;
   int mActiveAPZTransforms;
 };
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -580,31 +580,55 @@ AdjustForClip(const Matrix4x4& asyncTran
   if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsLayerComposite()->GetShadowClipRect()) {
     if (shadowClipRect->TopLeft() != ParentLayerIntPoint()) {  // avoid a gratuitous change of basis
       result.ChangeBasis(shadowClipRect->x, shadowClipRect->y, 0);
     }
   }
   return result;
 }
 
+static void
+ExpandRootClipRect(Layer* aLayer, const ScreenMargin& aFixedLayerMargins)
+{
+  // For Fennec we want to expand the root scrollable layer clip rect based on
+  // the fixed position margins. In particular, we want this while the dynamic
+  // toolbar is in the process of sliding offscreen and the area of the
+  // LayerView visible to the user is larger than the viewport size that Gecko
+  // knows about (and therefore larger than the clip rect). We could also just
+  // clear the clip rect on aLayer entirely but this seems more precise.
+  Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+  if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
+#ifndef MOZ_WIDGET_ANDROID
+    // We should never enter here on anything other than Fennec, since
+    // aFixedLayerMargins should be empty everywhere else.
+    MOZ_ASSERT(false);
+#endif
+    ParentLayerRect rect(rootClipRect.value());
+    rect.Deflate(ViewAs<ParentLayerPixel>(aFixedLayerMargins,
+      PixelCastJustification::ScreenIsParentLayerForRoot));
+    aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
+  }
+}
+
 bool
-AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
+AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
+                                                          bool* aOutFoundRoot)
 {
   bool appliedTransform = false;
   for (Layer* child = aLayer->GetFirstChild();
       child; child = child->GetNextSibling()) {
     appliedTransform |=
-      ApplyAsyncContentTransformToTree(child);
+      ApplyAsyncContentTransformToTree(child, aOutFoundRoot);
   }
 
   Matrix4x4 oldTransform = aLayer->GetTransform();
 
   Matrix4x4 combinedAsyncTransform;
   bool hasAsyncTransform = false;
-  ScreenMargin fixedLayerMargins(0, 0, 0, 0);
+  ScreenMargin fixedLayerMargins;
 
   // Each layer has multiple clips. Its local clip, which must move with async
   // transforms, and its scrollframe clips, which are the clips between each
   // scrollframe and its ancestor scrollframe. Scrollframe clips include the
   // composition bounds and any other clips induced by layout.
   //
   // The final clip for the layer is the intersection of these clips.
   Maybe<ParentLayerIntRect> asyncClip = aLayer->GetClipRect();
@@ -637,44 +661,53 @@ AsyncCompositionManager::ApplyAsyncConte
     Matrix4x4 asyncTransform =
       Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform;
 
     if (!aLayer->IsScrollInfoLayer()) {
       controller->MarkAsyncTransformAppliedToContent();
     }
 
     const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
-    // TODO: When we enable APZ on Fennec, we'll need to call SyncFrameMetrics here.
-    // When doing so, it might be useful to look at how it was called here before
-    // bug 1036967 removed the (dead) call.
 
 #if defined(MOZ_ANDROID_APZ)
-    bool rootContentLayer = metrics.IsRootContent();
-#ifdef MOZ_B2GDROID
-    // B2GDroid is a special snowflake since it doesn't seem to have any root
-    // content document. However we still need to send a setFirstPaintViewport
-    // message, so we use the root of the layer tree as the root content layer
-    // instead. For the most part this should work fine; the Java code will just
-    // think the root layer is the "main" content, which in a manner of speaking,
-    // it is.
-    rootContentLayer = (aLayer->GetParent() == nullptr);
-#endif // MOZ_B2GDROID
-    if (rootContentLayer) {
-      if (mIsFirstPaint) {
+    // If we find a metrics which is the root content doc, use that. If not, use
+    // the root layer. Since this function recurses on children first we should
+    // only end up using the root layer if the entire tree was devoid of a
+    // root content metrics. This is a temporary solution; in the long term we
+    // should not need the root content metrics at all. See bug 1201529 comment
+    // 6 for details.
+    if (!(*aOutFoundRoot)) {
+      *aOutFoundRoot = metrics.IsRootContent() ||       /* RCD */
+            (aLayer->GetParent() == nullptr &&          /* rootmost metrics */
+             i + 1 >= aLayer->GetFrameMetricsCount());
+      if (*aOutFoundRoot) {
         CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
-        LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
-        mContentRect = metrics.GetScrollableRect();
-        SetFirstPaintViewport(scrollOffsetLayerPixels,
-                              geckoZoom,
-                              mContentRect);
+        if (mIsFirstPaint) {
+          LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
+          mContentRect = metrics.GetScrollableRect();
+          SetFirstPaintViewport(scrollOffsetLayerPixels,
+                                geckoZoom,
+                                mContentRect);
+        } else {
+          // Compute the painted displayport in document-relative CSS pixels.
+          CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
+              metrics.GetDisplayPort() :
+              metrics.GetCriticalDisplayPort());
+          displayPort += metrics.GetScrollOffset();
+          SyncFrameMetrics(scrollOffset,
+              geckoZoom * asyncTransformWithoutOverscroll.mScale,
+              metrics.GetScrollableRect(), displayPort, geckoZoom, mLayersUpdated,
+              mPaintSyncId, fixedLayerMargins);
+        }
+        mIsFirstPaint = false;
+        mLayersUpdated = false;
+        mPaintSyncId = 0;
       }
-      mIsFirstPaint = false;
-      mLayersUpdated = false;
     }
-#endif // MOZ_ANDROID_APZ
+#endif
 
     // Transform the current local clip by this APZC's async transform. If we're
     // using containerful scrolling, then the clip is not part of the scrolled
     // frame and should not be transformed.
     if (asyncClip && !metrics.UsesContainerScrolling()) {
       MOZ_ASSERT(asyncTransform.Is2D());
       asyncClip = Some(TransformTo<ParentLayerPixel>(asyncTransform, *asyncClip));
     }
@@ -740,16 +773,18 @@ AsyncCompositionManager::ApplyAsyncConte
     if (Layer* maskLayer = aLayer->GetMaskLayer()) {
       SetShadowTransform(maskLayer,
           maskLayer->GetLocalTransform() * combinedAsyncTransform);
     }
 
     appliedTransform = true;
   }
 
+  ExpandRootClipRect(aLayer, fixedLayerMargins);
+
   if (aLayer->GetScrollbarDirection() != Layer::NONE) {
     ApplyAsyncTransformToScrollbar(aLayer);
   }
   return appliedTransform;
 }
 
 static bool
 LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget, Layer* aScrollbar)
@@ -1098,34 +1133,17 @@ AsyncCompositionManager::TransformScroll
   }
   oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1);
 
   // Make sure fixed position layers don't move away from their anchor points
   // when we're asynchronously panning or zooming
   AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
                             aLayer->GetLocalTransform(), fixedLayerMargins);
 
-  // For Fennec we want to expand the root scrollable layer clip rect based on
-  // the fixed position margins. In particular, we want this while the dynamic
-  // toolbar is in the process of sliding offscreen and the area of the
-  // LayerView visible to the user is larger than the viewport size that Gecko
-  // knows about (and therefore larger than the clip rect). We could also just
-  // clear the clip rect on aLayer entirely but this seems more precise.
-  Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
-  if (rootClipRect && fixedLayerMargins != ScreenMargin()) {
-#ifndef MOZ_WIDGET_ANDROID
-    // We should never enter here on anything other than Fennec, since
-    // fixedLayerMargins should be empty everywhere else.
-    MOZ_ASSERT(false);
-#endif
-    ParentLayerRect rect(rootClipRect.value());
-    rect.Deflate(ViewAs<ParentLayerPixel>(fixedLayerMargins,
-      PixelCastJustification::ScreenIsParentLayerForRoot));
-    aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
-  }
+  ExpandRootClipRect(aLayer, fixedLayerMargins);
 }
 
 void
 AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
 {
   MOZ_ASSERT(CompositorParent::IsInCompositorThread());
   mLayerTransformRecorder.EndTest(aOutData);
 }
@@ -1155,17 +1173,22 @@ AsyncCompositionManager::TransformShadow
     // an async pan zoom controller (which means that it is rendered
     // async using Gecko). If this fails, fall back to transforming the
     // primary scrollable layer.  "Failing" here means that we don't
     // find a frame that is async scrollable.  Note that the fallback
     // code also includes Fennec which is rendered async.  Fennec uses
     // its own platform-specific async rendering that is done partially
     // in Gecko and partially in Java.
     wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), aCurrentFrame);
-    if (!ApplyAsyncContentTransformToTree(root)) {
+    bool foundRoot = false;
+    if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+#if defined(MOZ_ANDROID_APZ)
+      MOZ_ASSERT(foundRoot);
+#endif
+    } else {
       nsAutoTArray<Layer*,1> scrollableLayers;
 #ifdef MOZ_WIDGET_ANDROID
       mLayerManager->GetRootScrollableLayers(scrollableLayers);
 #else
       mLayerManager->GetScrollableLayers(scrollableLayers);
 #endif
 
       for (uint32_t i = 0; i < scrollableLayers.Length(); i++) {
@@ -1224,26 +1247,26 @@ AsyncCompositionManager::SyncViewportInf
                                             aScrollRect,
                                             aScale,
                                             aFixedLayerMargins);
 #endif
 }
 
 void
 AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
-                                          float aZoom,
+                                          const CSSToParentLayerScale& aZoom,
                                           const CSSRect& aCssPageRect,
+                                          const CSSRect& aDisplayPort,
+                                          const CSSToLayerScale& aPaintedResolution,
                                           bool aLayersUpdated,
-                                          const CSSRect& aDisplayPort,
-                                          const CSSToLayerScale& aDisplayResolution,
-                                          bool aIsFirstPaint,
+                                          int32_t aPaintSyncId,
                                           ScreenMargin& aFixedLayerMargins)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
-                                            aLayersUpdated, aDisplayPort,
-                                            aDisplayResolution, aIsFirstPaint,
+                                            aDisplayPort, aPaintedResolution,
+                                            aLayersUpdated, aPaintSyncId,
                                             aFixedLayerMargins);
 #endif
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -123,18 +123,21 @@ public:
 
   // GetFrameUniformity will return the frame uniformity for each layer attached to an APZ
   // from the recorded data in RecordShadowTransform
   void GetFrameUniformity(FrameUniformityData* aFrameUniformityData);
 
 private:
   void TransformScrollableLayer(Layer* aLayer);
   // Return true if an AsyncPanZoomController content transform was
-  // applied for |aLayer|.
-  bool ApplyAsyncContentTransformToTree(Layer* aLayer);
+  // applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
+  // one of the metrics on one of the layers was determined to be the "root"
+  // and its state was synced to the Java front-end. |aOutFoundRoot| must be
+  // non-null.
+  bool ApplyAsyncContentTransformToTree(Layer* aLayer, bool* aOutFoundRoot);
   /**
    * Update the shadow transform for aLayer assuming that is a scrollbar,
    * so that it stays in sync with the content that is being scrolled by APZ.
    */
   void ApplyAsyncTransformToScrollbar(Layer* aLayer);
 
   void SetFirstPaintViewport(const LayerIntPoint& aOffset,
                              const CSSToLayerScale& aZoom,
@@ -143,22 +146,22 @@ private:
   void SyncViewportInfo(const LayerIntRect& aDisplayPort,
                         const CSSToLayerScale& aDisplayResolution,
                         bool aLayersUpdated,
                         int32_t aPaintSyncId,
                         ParentLayerRect& aScrollRect,
                         CSSToParentLayerScale& aScale,
                         ScreenMargin& aFixedLayerMargins);
   void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
-                        float aZoom,
+                        const CSSToParentLayerScale& aZoom,
                         const CSSRect& aCssPageRect,
+                        const CSSRect& aDisplayPort,
+                        const CSSToLayerScale& aPaintedResolution,
                         bool aLayersUpdated,
-                        const CSSRect& aDisplayPort,
-                        const CSSToLayerScale& aDisplayResolution,
-                        bool aIsFirstPaint,
+                        int32_t aPaintSyncId,
                         ScreenMargin& aFixedLayerMargins);
 
   /**
    * Adds a translation to the transform of any fixed position (whose parent
    * layer is not fixed) or sticky position layer descendant of
    * aTransformedSubtreeRoot. The translation is chosen so that the layer's
    * anchor point relative to aTransformedSubtreeRoot's parent layer is the same
    * as it was when aTransformedSubtreeRoot's GetLocalTransform() was
--- a/gfx/tests/reftest/1143303-1.svg
+++ b/gfx/tests/reftest/1143303-1.svg
@@ -1,14 +1,19 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg"
      width="100%" height="100%">
+<!-- There is a bug with MultiTiledContentClient that causes improper painting;
+     bug 1204076 is on file for this issue. As a temporary workaround, we set
+     user-scalable=no here so that WantAsyncZoom() returns false and we don't
+     use a MultiTiledContentClient. Once bug 1204076 is fixed, we can remove this. -->
+  <meta xmlns="http://www.w3.org/1999/xhtml" name="viewport" content="user-scalable=no"/>
 
   <title>Testcase for small circles</title>
   <!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1143303 -->
 
   <rect width="100%" height="100%" fill="lime"/>
 
   <circle cx="200" cy="150" r="95" fill="red"/>
   <g transform="translate(200, 150)" fill="lime">
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -160,19 +160,20 @@ gfxGraphiteShaper::ShapeText(gfxContext 
                       mFont->GetFontEntry()->FamilyName(),
                       mFallbackToSmallCaps,
                       AddFeature,
                       &f);
 
     size_t numChars = gr_count_unicode_characters(gr_utf16,
                                                   aText, aText + aLength,
                                                   nullptr);
+    gr_bidirtl grBidi = gr_bidirtl(aShapedText->IsRightToLeft()
+                                   ? (gr_rtl | gr_nobidi) : gr_nobidi);
     gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
-                                  gr_utf16, aText, numChars,
-                                  aShapedText->IsRightToLeft());
+                                  gr_utf16, aText, numChars, grBidi);
 
     gr_featureval_destroy(grFeatures);
 
     if (!seg) {
         return false;
     }
 
     nsresult rv = SetGlyphsFromSegment(aContext, aShapedText, aOffset, aLength,
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1807,17 +1807,21 @@ bool DoesRenderTargetViewNeedsRecreating
     if (FAILED(hr)) {
         gfxCriticalNote << "DoesRecreatingCreateCPUTextureFailed";
         return false;
     }
 
     deviceContext->CopyResource(cpuTexture, offscreenTexture);
 
     D3D11_MAPPED_SUBRESOURCE mapped;
-    deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
+    hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
+    if (FAILED(hr)) {
+        gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr);
+        return false;
+    }
     int resultColor = *(int*)mapped.pData;
     deviceContext->Unmap(cpuTexture, 0);
     cpuTexture->Release();
 
     // XXX on some drivers resultColor will not have changed to
     // match the clear
     if (resultColor != 0xffffff00) {
         gfxCriticalNote << "RenderTargetViewNeedsRecreating";
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -57,16 +57,56 @@ extern MOZ_NORETURN MOZ_COLD JS_PUBLIC_A
 JS_Assert(const char* s, const char* file, int ln);
 
 /*
  * Custom allocator support for SpiderMonkey
  */
 #if defined JS_USE_CUSTOM_ALLOCATOR
 # include "jscustomallocator.h"
 #else
+
+namespace js {
+namespace oom {
+
+/*
+ * To make testing OOM in certain helper threads more effective,
+ * allow restricting the OOM testing to a certain helper thread
+ * type. This allows us to fail e.g. in off-thread script parsing
+ * without causing an OOM in the main thread first.
+ */
+enum ThreadType {
+    THREAD_TYPE_NONE,           // 0
+    THREAD_TYPE_MAIN,           // 1
+    THREAD_TYPE_ASMJS,          // 2
+    THREAD_TYPE_ION,            // 3
+    THREAD_TYPE_PARSE,          // 4
+    THREAD_TYPE_COMPRESS,       // 5
+    THREAD_TYPE_GCHELPER,       // 6
+    THREAD_TYPE_GCPARALLEL,     // 7
+    THREAD_TYPE_MAX             // Used to check shell function arguments
+};
+
+
+/*
+ * Getter/Setter functions to encapsulate mozilla::ThreadLocal,
+ * implementation is in jsutil.cpp.
+ */
+# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+extern bool InitThreadType(void);
+extern void SetThreadType(ThreadType);
+extern uint32_t GetThreadType(void);
+# else
+inline bool InitThreadType(void) { return true; }
+inline void SetThreadType(ThreadType t) {};
+inline uint32_t GetThreadType(void) { return 0; }
+# endif
+
+} /* namespace oom */
+} /* namespace js */
+
 # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 
 /*
  * In order to test OOM conditions, when the testing function
  * oomAfterAllocations COUNT is passed, we fail continuously after the NUM'th
  * allocation from now.
  */
 extern JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations; /* set in builtin/TestingFunctions.cpp */
@@ -78,26 +118,38 @@ static MOZ_NEVER_INLINE void js_failedAl
 #define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
 #else
 #define JS_OOM_CALL_BP_FUNC() do {} while(0)
 #endif
 
 namespace js {
 namespace oom {
 
+extern JS_PUBLIC_DATA(uint32_t) targetThread;
+
+static inline bool
+OOMThreadCheck()
+{
+    return (!js::oom::targetThread 
+            || js::oom::targetThread == js::oom::GetThreadType());
+}
+
 static inline bool
 IsSimulatedOOMAllocation()
 {
-    return OOM_counter == OOM_maxAllocations ||
-           (OOM_counter > OOM_maxAllocations && OOM_failAlways);
+    return OOMThreadCheck() && (OOM_counter == OOM_maxAllocations ||
+           (OOM_counter > OOM_maxAllocations && OOM_failAlways));
 }
 
 static inline bool
 ShouldFailWithOOM()
 {
+    if (!OOMThreadCheck())
+        return false;
+
     OOM_counter++;
     if (IsSimulatedOOMAllocation()) {
         JS_OOM_CALL_BP_FUNC();
         return true;
     }
     return false;
 }
 
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -1433,17 +1433,19 @@ AsmJSModule::CodeRange::CodeRange(AsmJSE
 
     MOZ_ASSERT(begin_ < profilingReturn_);
     MOZ_ASSERT(profilingReturn_ < end_);
 }
 
 void
 AsmJSModule::CodeRange::updateOffsets(jit::MacroAssembler& masm)
 {
-    uint32_t entryBefore, profilingJumpBefore, profilingEpilogueBefore;
+    uint32_t entryBefore = 0;
+    uint32_t profilingJumpBefore = 0;
+    uint32_t profilingEpilogueBefore = 0;
     if (isFunction()) {
         entryBefore = entry();
         profilingJumpBefore = profilingJump();
         profilingEpilogueBefore = profilingEpilogue();
     }
 
     begin_ = masm.actualOffset(begin_);
     profilingReturn_ = masm.actualOffset(profilingReturn_);
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -2694,17 +2694,17 @@ ExtractNumericNonFloatValue(ParseNode* p
     return NumberNodeValue(pn);
 }
 
 static AsmJSNumLit
 ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
 {
     MOZ_ASSERT(IsSimdLiteral(m, pn));
 
-    AsmJSSimdType type;
+    AsmJSSimdType type = AsmJSSimdType_int32x4;
     JS_ALWAYS_TRUE(IsSimdTuple(m, pn, &type));
 
     ParseNode* arg = CallArgList(pn);
     switch (type) {
       case AsmJSSimdType_int32x4: {
         MOZ_ASSERT(SimdTypeToLength(type) == 4);
         int32_t val[4];
         for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jscntxt.h"
 
 #include "builtin/Eval.h"
 #include "frontend/BytecodeCompiler.h"
+#include "jit/InlinableNatives.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
@@ -1011,17 +1012,17 @@ static const JSFunctionSpec object_stati
     JS_SELF_HOSTED_FN("assign",        "ObjectStaticAssign",        2, JSPROP_DEFINE_LATE),
     JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf",     1, JSPROP_DEFINE_LATE),
     JS_FN("setPrototypeOf",            obj_setPrototypeOf,          2, 0),
     JS_FN("getOwnPropertyDescriptor",  obj_getOwnPropertyDescriptor,2, 0),
     JS_FN("keys",                      obj_keys,                    1, 0),
     JS_FN("is",                        obj_is,                      2, 0),
     JS_FN("defineProperty",            obj_defineProperty,          3, 0),
     JS_FN("defineProperties",          obj_defineProperties,        2, 0),
-    JS_FN("create",                    obj_create,                  2, 0),
+    JS_INLINABLE_FN("create",          obj_create,                  2, 0, ObjectCreate),
     JS_FN("getOwnPropertyNames",       obj_getOwnPropertyNames,     1, 0),
     JS_FN("getOwnPropertySymbols",     obj_getOwnPropertySymbols,   1, 0),
     JS_SELF_HOSTED_FN("isExtensible",  "ObjectIsExtensible",        1, JSPROP_DEFINE_LATE),
     JS_FN("preventExtensions",         obj_preventExtensions,       1, 0),
     JS_FN("freeze",                    obj_freeze,                  1, 0),
     JS_FN("isFrozen",                  obj_isFrozen,                1, 0),
     JS_FN("seal",                      obj_seal,                    1, 0),
     JS_FN("isSealed",                  obj_isSealed,                1, 0),
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/IntegerTypeTraits.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
+#include "jit/InlinableNatives.h"
 #include "js/Value.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
@@ -239,17 +240,17 @@ const JSPropertySpec Float32x4Defn::Type
 
 const JSFunctionSpec Float32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
     JS_FS_END
 };
 
 const JSFunctionSpec Float32x4Defn::Methods[] = {
 #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands) \
-    JS_FN(#Name, js::simd_float32x4_##Name, Operands, 0),
+    JS_INLINABLE_FN(#Name, js::simd_float32x4_##Name, Operands, 0, SimdFloat32x4),
     FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM)
 #undef SIMD_FLOAT32x4_FUNCTION_ITEM
     JS_FS_END
 };
 
 const JSFunctionSpec Float64x2Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
@@ -339,17 +340,17 @@ const JSPropertySpec Int32x4Defn::TypedO
 
 const JSFunctionSpec Int32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
     JS_FS_END
 };
 
 const JSFunctionSpec Int32x4Defn::Methods[] = {
 #define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands) \
-    JS_FN(#Name, js::simd_int32x4_##Name, Operands, 0),
+    JS_INLINABLE_FN(#Name, js::simd_int32x4_##Name, Operands, 0, SimdInt32x4),
     INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM)
 #undef SIMD_INT32X4_FUNCTION_ITEM
     JS_FS_END
 };
 
 template<typename T>
 static JSObject*
 CreateAndBindSimdClass(JSContext* cx, Handle<GlobalObject*> global, HandleObject SIMD,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -14,16 +14,17 @@
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSLink.h"
 #include "asmjs/AsmJSValidate.h"
+#include "jit/InlinableNatives.h"
 #include "jit/JitFrameIterator.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeTraverse.h"
 #include "js/Vector.h"
 #include "vm/GlobalObject.h"
@@ -982,43 +983,73 @@ DisableTrackAllocations(JSContext* cx, u
     return true;
 }
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 static bool
 OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 1) {
+    if (args.length() < 1) {
         JS_ReportError(cx, "count argument required");
         return false;
     }
 
+    if (args.length() > 2) {
+        JS_ReportError(cx, "too many arguments");
+        return false;
+    }
+
+    uint32_t targetThread = 0;
+    if (!ToUint32(cx, args.get(1), &targetThread))
+        return false;
+
+    if (targetThread >= js::oom::THREAD_TYPE_MAX) {
+        JS_ReportError(cx, "invalid thread type specified");
+        return false;
+    }
+
     uint32_t count;
-    if (!JS::ToUint32(cx, args[0], &count))
+    if (!JS::ToUint32(cx, args.get(0), &count))
         return false;
 
+    js::oom::targetThread = targetThread;
     OOM_maxAllocations = OOM_counter + count;
     OOM_failAlways = true;
     return true;
 }
 
 static bool
 OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 1) {
+    if (args.length() < 1) {
         JS_ReportError(cx, "count argument required");
         return false;
     }
 
+    if (args.length() > 2) {
+        JS_ReportError(cx, "too many arguments");
+        return false;
+    }
+
+    uint32_t targetThread = 0;
+    if (!ToUint32(cx, args.get(1), &targetThread))
+        return false;
+
+    if (targetThread >= js::oom::THREAD_TYPE_MAX) {
+        JS_ReportError(cx, "invalid thread type specified");
+        return false;
+    }
+
     uint32_t count;
-    if (!JS::ToUint32(cx, args[0], &count))
+    if (!JS::ToUint32(cx, args.get(0), &count))
         return false;
 
+    js::oom::targetThread = targetThread;
     OOM_maxAllocations = OOM_counter + count;
     OOM_failAlways = false;
     return true;
 }
 
 static bool
 ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1414,28 +1445,28 @@ GetObjectMetadata(JSContext* cx, unsigne
         JS_ReportError(cx, "Argument must be an object");
         return false;
     }
 
     args.rval().setObjectOrNull(GetObjectMetadata(&args[0].toObject()));
     return true;
 }
 
-bool
-js::testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp)
+static bool
+testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // NOP when not in IonMonkey
     args.rval().setUndefined();
     return true;
 }
 
-bool
-js::testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp)
+static bool
+testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!jit::IsBaselineEnabled(cx)) {
         JSString* error = JS_NewStringCopyZ(cx, "Baseline is disabled.");
         if(!error)
             return false;
 
@@ -1452,18 +1483,18 @@ js::testingFunc_inJit(JSContext* cx, uns
         args.rval().setString(error);
         return true;
     }
 
     args.rval().setBoolean(cx->currentlyRunningInJit());
     return true;
 }
 
-bool
-js::testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp)
+static bool
+testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!jit::IsIonEnabled(cx)) {
         JSString* error = JS_NewStringCopyZ(cx, "Ion is disabled.");
         if (!error)
             return false;
 
@@ -2827,25 +2858,27 @@ static const JSFunctionSpecWithHelp Test
 "  object metadata callback that will override any other object metadata\n"
 "  callback that may be set."),
 
     JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
 "disableTrackAllocations()",
 "  Stop capturing the JS stack at every allocation."),
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
-    JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
-"oomAfterAllocations(count)",
+    JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
+"oomAfterAllocations(count [,threadType])",
 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
-"  (return NULL)."),
-
-    JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 1, 0,
-"oomAtAllocation(count)",
+"  (return nullptr). The optional thread type limits the effect to the\n"
+"  specified type of helper thread."),
+
+    JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
+"oomAtAllocation(count [,threadType])",
 "  After 'count' js_malloc memory allocations, fail the next allocation\n"
-"  (return NULL)."),
+"  (return nullptr). The optional thread type limits the effect to the\n"
+"  specified type of helper thread."),
 
     JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
 "resetOOMFailure()",
 "  Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
 "  oomAtAllocation() and return whether any allocation had been caused to fail."),
 #endif
 
     JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0,
@@ -3022,17 +3055,17 @@ gc::ZealModeHelpText),
     JS_FN_HELP("enableShellObjectMetadataCallback", EnableShellObjectMetadataCallback, 0, 0,
 "enableShellObjectMetadataCallback()",
 "  Use ShellObjectMetadataCallback to supply metadata for all newly created objects."),
 
     JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0,
 "getObjectMetadata(obj)",
 "  Get the metadata for an object."),
 
-    JS_FN_HELP("bailout", testingFunc_bailout, 0, 0,
+    JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
 "bailout()",
 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
 
     JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
 "inJit()",
 "  Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
 "  function returns an error string. This function returns false in all other cases.\n"
 "  Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
--- a/js/src/builtin/TestingFunctions.h
+++ b/js/src/builtin/TestingFunctions.h
@@ -10,25 +10,16 @@
 #include "NamespaceImports.h"
 
 namespace js {
 
 bool
 DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe);
 
 bool
-testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp);
-
-bool
 testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp);
 
 bool
 testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc, Value* vp);
 
-bool
-testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp);
-
-bool
-testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp);
-
 } /* namespace js */
 
 #endif /* builtin_TestingFunctions_h */
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1711,25 +1711,39 @@ enum FunctionSyntaxKind
 {
     Expression,
     Statement,
     Arrow,
     Method,
     ClassConstructor,
     DerivedClassConstructor,
     Getter,
-    Setter
+    GetterNoExpressionClosure,
+    Setter,
+    SetterNoExpressionClosure
 };
 
 static inline bool
 IsConstructorKind(FunctionSyntaxKind kind)
 {
     return kind == ClassConstructor || kind == DerivedClassConstructor;
 }
 
+static inline bool
+IsGetterKind(FunctionSyntaxKind kind)
+{
+    return kind == Getter || kind == GetterNoExpressionClosure;
+}
+
+static inline bool
+IsSetterKind(FunctionSyntaxKind kind)
+{
+    return kind == Setter || kind == SetterNoExpressionClosure;
+}
+
 static inline ParseNode*
 FunctionArgsList(ParseNode* fn, unsigned* numFormals)
 {
     MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
     ParseNode* argsBody = fn->pn_body;
     MOZ_ASSERT(argsBody->isKind(PNK_ARGSBODY));
     *numFormals = argsBody->pn_count;
     if (*numFormals > 0 && argsBody->last()->isKind(PNK_STATEMENTLIST))
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1426,20 +1426,22 @@ Parser<ParseHandler>::newFunction(Handle
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case ClassConstructor:
       case DerivedClassConstructor:
         flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Getter:
+      case GetterNoExpressionClosure:
         flags = JSFunction::INTERPRETED_GETTER;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Setter:
+      case SetterNoExpressionClosure:
         flags = JSFunction::INTERPRETED_SETTER;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       default:
         flags = (generatorKind == NotGenerator
                  ? JSFunction::INTERPRETED_NORMAL
                  : JSFunction::INTERPRETED_GENERATOR);
     }
@@ -1451,17 +1453,17 @@ Parser<ParseHandler>::newFunction(Handle
     if (options().selfHostingMode)
         fun->setIsSelfHostedBuiltin();
     return fun;
 }
 
 static bool
 MatchOrInsertSemicolon(TokenStream& ts, TokenStream::Modifier modifier = TokenStream::None)
 {
-    TokenKind tt;
+    TokenKind tt = TOK_EOF;
     if (!ts.peekTokenSameLine(&tt, modifier))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /* Advance the scanner for proper error location reporting. */
         ts.consumeKnownToken(tt, modifier);
         ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
@@ -1855,17 +1857,17 @@ Parser<ParseHandler>::functionArguments(
         if (!matched)
             hasArguments = true;
     }
     if (hasArguments) {
         bool hasDefaults = false;
         Node duplicatedArg = null();
         bool disallowDuplicateArgs = kind == Arrow || kind == Method || kind == ClassConstructor;
 
-        if (kind == Getter) {
+        if (IsGetterKind(kind)) {
             report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
             return false;
         }
 
         while (true) {
             if (*hasRest) {
                 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
                 return false;
@@ -1921,17 +1923,17 @@ Parser<ParseHandler>::functionArguments(
               case TOK_YIELD:
                 if (!checkYieldNameValidity())
                     return false;
                 MOZ_ASSERT(yieldHandling == YieldIsName);
                 goto TOK_NAME;
 
               case TOK_TRIPLEDOT:
               {
-                if (kind == Setter) {
+                if (IsSetterKind(kind)) {
                     report(ParseError, false, null(),
                            JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
                 *hasRest = true;
                 if (!tokenStream.getToken(&tt))
                     return false;
                 // FIXME: This fails to handle a rest parameter named |yield|
@@ -1998,44 +2000,44 @@ Parser<ParseHandler>::functionArguments(
                 }
                 Node def_expr = assignExprWithoutYield(yieldHandling, JSMSG_YIELD_IN_DEFAULT);
                 if (!def_expr)
                     return false;
                 if (!handler.setLastFunctionArgumentDefault(funcpn, def_expr))
                     return false;
             }
 
-            if (parenFreeArrow || kind == Setter)
+            if (parenFreeArrow || IsSetterKind(kind))
                 break;
 
             if (!tokenStream.matchToken(&matched, TOK_COMMA))
                 return false;
             if (!matched)
                 break;
         }
 
         if (!parenFreeArrow) {
             TokenKind tt;
             if (!tokenStream.getToken(&tt))
                 return false;
             if (tt != TOK_RP) {
-                if (kind == Setter) {
+                if (IsSetterKind(kind)) {
                     report(ParseError, false, null(),
                            JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
 
                 report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
                 return false;
             }
         }
 
         if (!hasDefaults)
             funbox->length = pc->numArgs() - *hasRest;
-    } else if (kind == Setter) {
+    } else if (IsSetterKind(kind)) {
         report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
         return false;
     }
 
     return true;
 }
 
 template <>
@@ -2776,17 +2778,19 @@ Parser<ParseHandler>::functionArgsAndBod
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_LC) {
-        if (funbox->isStarGenerator() || kind == Method || IsConstructorKind(kind)) {
+        if (funbox->isStarGenerator() || kind == Method ||
+            kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
+            IsConstructorKind(kind)) {
             report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
         if (kind != Arrow) {
 #if JS_HAS_EXPR_CLOSURES
             addTelemetry(JSCompartment::DeprecatedExpressionClosure);
 #else
@@ -3078,19 +3082,19 @@ Parser<ParseHandler>::statements(YieldHa
         return null();
 
     Node saveBlock = pc->blockNode;
     pc->blockNode = pn;
 
     bool canHaveDirectives = pc->atBodyLevel();
     bool afterReturn = false;
     bool warnedAboutStatementsAfterReturn = false;
-    uint32_t statementBegin;
+    uint32_t statementBegin = 0;
     for (;;) {
-        TokenKind tt;
+        TokenKind tt = TOK_EOF;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
             if (tokenStream.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (tt == TOK_EOF || tt == TOK_RC)
             break;
         if (afterReturn) {
@@ -3156,17 +3160,17 @@ Parser<ParseHandler>::condition(InHandli
     }
     return pn;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label)
 {
-    TokenKind tt;
+    TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
     if (tt == TOK_NAME) {
         tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
         MOZ_ASSERT_IF(tokenStream.currentName() == context->names().yield,
                       yieldHandling == YieldIsName);
         label.set(tokenStream.currentName());
@@ -5645,17 +5649,17 @@ Parser<ParseHandler>::switchStatement(Yi
         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
 
         Node body = handler.newStatementList(pc->blockid(), pos());
         if (!body)
             return null();
 
         bool afterReturn = false;
         bool warnedAboutStatementsAfterReturn = false;
-        uint32_t statementBegin;
+        uint32_t statementBegin = 0;
         while (true) {
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
             if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
                 break;
             if (afterReturn) {
                 TokenPos pos(0, 0);
                 if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
@@ -5808,17 +5812,17 @@ Parser<ParseHandler>::returnStatement(Yi
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->sc->isFunctionBox());
 
     // Parse an optional operand.
     //
     // This is ugly, but we don't want to require a semicolon.
     Node exprNode;
-    TokenKind tt;
+    TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     TokenStream::Modifier modifier = TokenStream::Operand;
     switch (tt) {
       case TOK_EOL:
       case TOK_EOF:
       case TOK_SEMI:
       case TOK_RC:
@@ -5888,17 +5892,17 @@ Parser<ParseHandler>::yieldExpression(In
       case StarGenerator:
       {
         MOZ_ASSERT(pc->sc->isFunctionBox());
 
         pc->lastYieldOffset = begin;
 
         Node exprNode;
         ParseNodeKind kind = PNK_YIELD;
-        TokenKind tt;
+        TokenKind tt = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
             return null();
         switch (tt) {
           // TOK_EOL is special; it implements the [no LineTerminator here]
           // quirk in the grammar.
           case TOK_EOL:
           // The rest of these make up the complete set of tokens that can
           // appear after any of the places where AssignmentExpression is used
@@ -5957,17 +5961,17 @@ Parser<ParseHandler>::yieldExpression(In
         // We are in a legacy generator: a function that has already seen a
         // yield, or in a legacy generator comprehension.
         MOZ_ASSERT(pc->sc->isFunctionBox());
 
         pc->lastYieldOffset = begin;
 
         // Legacy generators do not require a value.
         Node exprNode;
-        TokenKind tt;
+        TokenKind tt = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
             return null();
         switch (tt) {
           case TOK_EOF:
           case TOK_EOL:
           case TOK_SEMI:
           case TOK_RC:
           case TOK_RB:
@@ -6088,17 +6092,17 @@ Parser<ParseHandler>::labeledStatement(Y
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
     uint32_t begin = pos().begin;
 
     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
-    TokenKind tt;
+    TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
         report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
         return null();
     }
     if (tt == TOK_EOL) {
         report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
@@ -6314,18 +6318,20 @@ Parser<ParseHandler>::debuggerStatement(
     return handler.newDebuggerStatement(p);
 }
 
 static JSOp
 JSOpFromPropertyType(PropertyType propType)
 {
     switch (propType) {
       case PropertyType::Getter:
+      case PropertyType::GetterNoExpressionClosure:
         return JSOP_INITPROP_GETTER;
       case PropertyType::Setter:
+      case PropertyType::SetterNoExpressionClosure:
         return JSOP_INITPROP_SETTER;
       case PropertyType::Normal:
       case PropertyType::Method:
       case PropertyType::GeneratorMethod:
       case PropertyType::Constructor:
       case PropertyType::DerivedConstructor:
         return JSOP_INITPROP;
       default:
@@ -6334,18 +6340,22 @@ JSOpFromPropertyType(PropertyType propTy
 }
 
 static FunctionSyntaxKind
 FunctionSyntaxKindFromPropertyType(PropertyType propType)
 {
     switch (propType) {
       case PropertyType::Getter:
         return Getter;
+      case PropertyType::GetterNoExpressionClosure:
+        return GetterNoExpressionClosure;
       case PropertyType::Setter:
         return Setter;
+      case PropertyType::SetterNoExpressionClosure:
+        return SetterNoExpressionClosure;
       case PropertyType::Method:
         return Method;
       case PropertyType::GeneratorMethod:
         return Method;
       case PropertyType::Constructor:
         return ClassConstructor;
       case PropertyType::DerivedConstructor:
         return DerivedClassConstructor;
@@ -6469,16 +6479,20 @@ Parser<FullParseHandler>::classDefinitio
         if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
             propType != PropertyType::Method && propType != PropertyType::GeneratorMethod &&
             propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
         {
             report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
             return null();
         }
 
+        if (propType == PropertyType::Getter)
+            propType = PropertyType::GetterNoExpressionClosure;
+        if (propType == PropertyType::Setter)
+            propType = PropertyType::SetterNoExpressionClosure;
         if (!isStatic && propAtom == context->names().constructor) {
             if (propType != PropertyType::Method) {
                 report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
                 return null();
             }
             if (seenConstructor) {
                 report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "constructor");
                 return null();
@@ -6489,18 +6503,18 @@ Parser<FullParseHandler>::classDefinitio
             report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
             return null();
         }
 
         // FIXME: Implement ES6 function "name" property semantics
         // (bug 883377).
         RootedPropertyName funName(context);
         switch (propType) {
-          case PropertyType::Getter:
-          case PropertyType::Setter:
+          case PropertyType::GetterNoExpressionClosure:
+          case PropertyType::SetterNoExpressionClosure:
             funName = nullptr;
             break;
           case PropertyType::Constructor:
           case PropertyType::DerivedConstructor:
             funName = name;
             break;
           default:
             if (tokenStream.isCurrentTokenType(TOK_NAME))
@@ -7064,37 +7078,77 @@ Parser<ParseHandler>::assignExpr(InHandl
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    op = JSOP_MUL;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
       case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    op = JSOP_POW;    break;
 
       case TOK_ARROW: {
         // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
         tokenStream.ungetToken();
-        TokenKind next;
+        TokenKind next = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&next) || next != TOK_ARROW) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "expression", TokenKindToDesc(TOK_ARROW));
             return null();
         }
+        tokenStream.consumeKnownToken(TOK_ARROW);
+
+        bool isBlock = false;
+        if (!tokenStream.peekToken(&next, TokenStream::Operand))
+            return null();
+        if (next == TOK_LC)
+            isBlock = true;
 
         tokenStream.seek(start);
         if (!abortIfSyntaxParser())
             return null();
 
         TokenKind ignored;
         if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
             return null();
 
         if (pc->sc->isFunctionBox() && pc->sc->asFunctionBox()->isDerivedClassConstructor()) {
             report(ParseError, false, null(), JSMSG_DISABLED_DERIVED_CLASS, "arrow functions");
             return null();
         }
 
-        return functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator);
+        Node arrowFunc = functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator);
+        if (!arrowFunc)
+            return null();
+
+        if (isBlock) {
+            // This arrow function could be a non-trailing member of a comma
+            // expression or a semicolon terminating a full expression.  If so,
+            // the next token is that comma/semicolon, gotten with None:
+            //
+            //   a => {}, b; // as if (a => {}), b;
+            //   a => {};
+            //
+            // But if this arrow function ends a statement, ASI permits the
+            // next token to start an expression statement.  In that case the
+            // next token must be gotten as Operand:
+            //
+            //   a => {} // complete expression statement
+            //   /x/g;   // regular expression as a statement, *not* division
+            //
+            // Getting the second case right requires the first token-peek
+            // after the arrow function use Operand, and that peek must occur
+            // before Parser::expr() looks for a comma.  Do so here, then
+            // immediately add the modifier exception needed for the first
+            // case.
+            //
+            // Note that the second case occurs *only* if the arrow function
+            // has block body.  An arrow function not ending in such, ends in
+            // another AssignmentExpression that we can inductively assume was
+            // peeked consistently.
+            if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
+                return null();
+            tokenStream.addModifierException(TokenStream::NoneIsOperand);
+        }
+        return arrowFunc;
       }
 
       default:
         MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
         tokenStream.ungetToken();
         return lhs;
     }
 
@@ -7174,17 +7228,17 @@ Parser<ParseHandler>::reportIfNotValidSi
     if (handler.maybeNameAnyParentheses(target)) {
         // Use a special error if the target is arguments/eval.  This ensures
         // targeting these names is consistently a SyntaxError (which error numbers
         // below don't guarantee) while giving us a nicer error message.
         if (!reportIfArgumentsEvalTarget(target))
             return false;
     }
 
-    unsigned errnum;
+    unsigned errnum = 0;
     const char* extra = nullptr;
 
     switch (flavor) {
       case IncrementAssignment:
         errnum = JSMSG_BAD_OPERAND;
         extra = "increment";
         break;
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -353,17 +353,19 @@ struct BindData;
 class CompExprTransplanter;
 
 enum VarContext { HoistVars, DontHoistVars };
 enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody };
 enum class PropertyType {
     Normal,
     Shorthand,
     Getter,
+    GetterNoExpressionClosure,
     Setter,
+    SetterNoExpressionClosure,
     Method,
     GeneratorMethod,
     Constructor,
     DerivedConstructor
 };
 
 // Specify a value for an ES6 grammar parametrization.  We have no enum for
 // [Return] because its behavior is exactly equivalent to checking whether
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -116,24 +116,38 @@ struct Token
         //   var s = `Hello ${entity}!`;
         //                          ^ TemplateTail context
         TemplateTail,
     };
     enum ModifierException
     {
         NoException,
 
-        // After |yield| we look for a token on the same line that starts an
+        // Used in following 2 cases:
+        // a) After |yield| we look for a token on the same line that starts an
         // expression (Operand): |yield <expr>|.  If no token is found, the
         // |yield| stands alone, and the next token on a subsequent line must
         // be: a comma continuing a comma expression, a semicolon terminating
         // the statement that ended with |yield|, or the start of another
         // statement (possibly an expression statement).  The comma/semicolon
         // cases are gotten as operators (None), contrasting with Operand
         // earlier.
+        // b) After an arrow function with a block body in an expression
+        // statement, the next token must be: a colon in a conditional
+        // expression, a comma continuing a comma expression, a semicolon
+        // terminating the statement, or the token on a subsequent line that is
+        // the start of another statement (possibly an expression statement).
+        // Colon is gotten as operator (None), and it should only be gotten in
+        // conditional expression and missing it results in SyntaxError.
+        // Comma/semicolon cases are also gotten as operators (None), and 4th
+        // case is gotten after them.  If no comma/semicolon found but EOL,
+        // the next token should be gotten as operand in 4th case (especially if
+        // '/' is the first character).  So we should peek the token as
+        // operand before try getting colon/comma/semicolon.
+        // See also the comment in Parser::assignExpr().
         NoneIsOperand,
 
         // If a semicolon is inserted automatically, the next token is already
         // gotten with None, but we expect Operand.
         OperandIsNone,
 
         // If name of method definition is `get` or `set`, the next token is
         // already gotten with KeywordIsName, but we expect None.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1201459.js
@@ -0,0 +1,5 @@
+// |jit-test| error:ReferenceError
+function f() {
+        (x ? Math.fround(0) : x ? a : x) && b;
+}
+f(Math.fround);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/arrow-with-block.js
@@ -0,0 +1,92 @@
+load(libdir + "asserts.js");
+
+let x = 10;
+let g = 4;
+
+assertEq(eval(`
+a => {}
+/x/g;
+`).toString(), "/x/g");
+assertEq(eval(`
+a => {}
+/x/;
+`).toString(), "/x/");
+assertThrowsInstanceOf(() => eval(`
+a => {} /x/g;
+`), SyntaxError);
+
+assertEq(eval(`
+a => {},
+/x/;
+`).toString(), "/x/");
+assertEq(eval(`
+a => {}
+,
+/x/;
+`).toString(), "/x/");
+
+assertEq(eval(`
+false ?
+a => {} :
+/x/;
+`).toString(), "/x/");
+assertEq(eval(`
+false ?
+a => {}
+:
+/x/;
+`).toString(), "/x/");
+
+assertEq(eval(`
+a => {};
+/x/;
+`).toString(), "/x/");
+assertEq(eval(`
+a => {}
+;
+/x/;
+`).toString(), "/x/");
+
+assertEq(eval(`
+a => 200
+/x/g;
+`) instanceof Function, true);
+assertEq(eval(`
+a => 200
+/x/g;
+`)(), 5);
+assertEq(eval(`
+a => 200 /x/g;
+`)(), 5);
+
+assertEq(eval(`
+a => 1,
+/x/;
+`).toString(), "/x/");
+assertEq(eval(`
+a => 1
+,
+/x/;
+`).toString(), "/x/");
+
+assertEq(eval(`
+false ?
+a => 1 :
+/x/;
+`).toString(), "/x/");
+assertEq(eval(`
+false ?
+a => 1
+:
+/x/;
+`).toString(), "/x/");
+
+assertEq(eval(`
+a => 1;
+/x/;
+`).toString(), "/x/");
+assertEq(eval(`
+a => 1
+;
+/x/;
+`).toString(), "/x/");
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -3,16 +3,17 @@
  * 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/. */
 
 #ifndef jit_InlinableNatives_h
 #define jit_InlinableNatives_h
 
 #define INLINABLE_NATIVE_LIST(_)    \
+    _(Array)                        \
     _(ArrayIsArray)                 \
     _(ArrayPop)                     \
     _(ArrayShift)                   \
     _(ArrayPush)                    \
     _(ArrayConcat)                  \
     _(ArraySlice)                   \
     _(ArraySplice)                  \
                                     \
@@ -62,22 +63,34 @@
     _(MathACosH)                    \
     _(MathSign)                     \
     _(MathTrunc)                    \
     _(MathCbrt)                     \
                                     \
     _(RegExpExec)                   \
     _(RegExpTest)                   \
                                     \
+    _(String)                       \
     _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
     _(StringReplace)                \
                                     \
+    _(ObjectCreate)                 \
+                                    \
+    _(CallBoundFunction)            \
+                                    \
+    _(SimdInt32x4)                  \
+    _(SimdFloat32x4)                \
+                                    \
+    _(TestBailout)                  \
+    _(TestAssertFloat32)            \
+    _(TestAssertRecoveredOnBailout) \
+                                    \
     _(IntrinsicUnsafeSetReservedSlot) \
     _(IntrinsicUnsafeGetReservedSlot) \
     _(IntrinsicUnsafeGetObjectFromReservedSlot) \
     _(IntrinsicUnsafeGetInt32FromReservedSlot) \
     _(IntrinsicUnsafeGetStringFromReservedSlot) \
     _(IntrinsicUnsafeGetBooleanFromReservedSlot) \
                                     \
     _(IntrinsicIsCallable)          \
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1161,16 +1161,19 @@ TypeAnalyzer::insertConversions()
                 phi->type() == MIRType_MagicUninitializedLexical)
             {
                 replaceRedundantPhi(phi);
                 block->discardPhi(phi);
             } else {
                 adjustPhiInputs(phi);
             }
         }
+
+        // AdjustInputs can add/remove/mutate instructions before and after the
+        // current instruction. Only increment the iterator after it is finished.
         for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
             if (!adjustInputs(*iter))
                 return false;
         }
     }
     return true;
 }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4061,17 +4061,17 @@ IonBuilder::processCondSwitchCase(CFGSta
         return ControlStatus_Error;
 
     // Terminate the last case condition block by emitting the code
     // corresponding to JSOP_CASE bytecode.
     if (bodyBlock != caseBlock) {
         MDefinition* caseOperand = current->pop();
         MDefinition* switchOperand = current->peek(-1);
 
-        if (jsop_compare(JSOP_STRICTEQ, switchOperand, caseOperand))
+        if (!jsop_compare(JSOP_STRICTEQ, switchOperand, caseOperand))
             return ControlStatus_Error;
         MInstruction* cmpResult = current->pop()->toInstruction();
         MOZ_ASSERT(!cmpResult->isEffectful());
         current->end(newTest(cmpResult, bodyBlock, caseBlock));
 
         // Add last case as predecessor of the body if the body is aliasing
         // the previous case body.
         if (!bodyIsNew && !bodyBlock->addPredecessorPopN(alloc(), current, 1))
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -837,16 +837,19 @@ class IonBuilder
 
     //  helpers
     static MIRType SimdTypeDescrToMIRType(SimdTypeDescr::Type type);
     bool checkInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
                          unsigned numArgs, InlineTypedObject** templateObj);
     IonBuilder::InliningStatus boxSimd(CallInfo& callInfo, MInstruction* ins,
                                        InlineTypedObject* templateObj);
 
+    InliningStatus inlineSimdInt32x4(CallInfo& callInfo, JSNative native);
+    InliningStatus inlineSimdFloat32x4(CallInfo& callInfo, JSNative native);
+
     template <typename T>
     InliningStatus inlineBinarySimd(CallInfo& callInfo, JSNative native,
                                     typename T::Operation op, SimdTypeDescr::Type type);
     InliningStatus inlineCompSimd(CallInfo& callInfo, JSNative native,
                                   MSimdBinaryComp::Operation op, SimdTypeDescr::Type compType);
     InliningStatus inlineUnarySimd(CallInfo& callInfo, JSNative native,
                                    MSimdUnaryArith::Operation op, SimdTypeDescr::Type type);
     InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native,
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -34,209 +34,43 @@ using JS::TrackedTypeSite;
 
 namespace js {
 namespace jit {
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
 {
     MOZ_ASSERT(target->isNative());
-    JSNative native = target->native();
 
     if (!optimizationInfo().inlineNative()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
         return InliningStatus_NotInlined;
     }
 
+    if (!target->jitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) {
+        // Reaching here means we tried to inline a native for which there is no
+        // Ion specialization.
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization);
+        return InliningStatus_NotInlined;
+    }
+
     // Default failure reason is observing an unsupported type.
     trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
 
     if (shouldAbortOnPreliminaryGroups(callInfo.thisArg()))
         return InliningStatus_NotInlined;
     for (size_t i = 0; i < callInfo.argc(); i++) {
         if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i)))
             return InliningStatus_NotInlined;
     }
 
-    if (native == ArrayConstructor)
-        return inlineArray(callInfo);
-
-    if (native == StringConstructor)
-        return inlineStringObject(callInfo);
-
-    // Object natives.
-    if (native == obj_create)
-        return inlineObjectCreate(callInfo);
-
-    // Testing Functions
-    if (native == testingFunc_bailout)
-        return inlineBailout(callInfo);
-    if (native == testingFunc_assertFloat32)
-        return inlineAssertFloat32(callInfo);
-    if (native == testingFunc_assertRecoveredOnBailout)
-        return inlineAssertRecoveredOnBailout(callInfo);
-
-    // Bound function
-    if (native == js::CallOrConstructBoundFunction)
-        return inlineBoundFunction(callInfo, target);
-
-    // Simd functions
-#define INLINE_FLOAT32X4_SIMD_ARITH_(OP)                                                         \
-    if (native == js::simd_float32x4_##OP)                                                       \
-        return inlineBinarySimd<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_##OP,   \
-                                                  SimdTypeDescr::Float32x4);
-
-#define INLINE_INT32X4_SIMD_ARITH_(OP)                                                           \
-    if (native == js::simd_int32x4_##OP)                                                         \
-        return inlineBinarySimd<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_##OP,   \
-                                                  SimdTypeDescr::Int32x4);
-
-    ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_)
-    ARITH_COMMONX4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_)
-    BINARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_)
-#undef INLINE_SIMD_ARITH_
-#undef INLINE_FLOAT32X4_SIMD_ARITH_
-
-#define INLINE_SIMD_BITWISE_(OP)                                                                 \
-    if (native == js::simd_int32x4_##OP)                                                         \
-        return inlineBinarySimd<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::OP##_, \
-                                                    SimdTypeDescr::Int32x4);                     \
-    if (native == js::simd_float32x4_##OP)                                                       \
-        return inlineBinarySimd<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::OP##_, \
-                                                    SimdTypeDescr::Float32x4);
-
-    BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_)
-#undef INLINE_SIMD_BITWISE_
-
-    if (native == js::simd_int32x4_shiftLeftByScalar)
-        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4);
-    if (native == js::simd_int32x4_shiftRightArithmeticByScalar)
-        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4);
-    if (native == js::simd_int32x4_shiftRightLogicalByScalar)
-        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4);
-
-#define INLINE_SIMD_COMPARISON_(OP)                                                                \
-    if (native == js::simd_int32x4_##OP)                                                           \
-        return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4);      \
-    if (native == js::simd_float32x4_##OP)                                                         \
-        return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4);
-
-    COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_)
-#undef INLINE_SIMD_COMPARISON_
-
-    if (native == js::simd_int32x4_extractLane)
-        return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Int32x4);
-    if (native == js::simd_float32x4_extractLane)
-        return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Float32x4);
-
-    if (native == js::simd_int32x4_replaceLane)
-        return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4);
-    if (native == js::simd_float32x4_replaceLane)
-        return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Float32x4);
-
-    if (native == js::simd_int32x4_not)
-        return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4);
-    if (native == js::simd_int32x4_neg)
-        return inlineUnarySimd(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4);
-
-#define INLINE_SIMD_FLOAT32X4_UNARY_(OP)                                                           \
-    if (native == js::simd_float32x4_##OP)                                                         \
-        return inlineUnarySimd(callInfo, native, MSimdUnaryArith::OP, SimdTypeDescr::Float32x4);
-
-    UNARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_SIMD_FLOAT32X4_UNARY_)
-    INLINE_SIMD_FLOAT32X4_UNARY_(neg)
-#undef INLINE_SIMD_FLOAT32X4_UNARY_
-
-    if (native == js::simd_float32x4_not)
-        return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Float32x4);
-
-    typedef bool IsCast;
-    if (native == js::simd_float32x4_fromInt32x4)
-        return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4);
-    if (native == js::simd_int32x4_fromFloat32x4)
-        return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4);
-    if (native == js::simd_float32x4_fromInt32x4Bits)
-        return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4);
-    if (native == js::simd_int32x4_fromFloat32x4Bits)
-        return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4);
-
-    if (native == js::simd_int32x4_splat)
-        return inlineSimdSplat(callInfo, native, SimdTypeDescr::Int32x4);
-    if (native == js::simd_float32x4_splat)
-        return inlineSimdSplat(callInfo, native, SimdTypeDescr::Float32x4);
-
-    if (native == js::simd_int32x4_check)
-        return inlineSimdCheck(callInfo, native, SimdTypeDescr::Int32x4);
-    if (native == js::simd_float32x4_check)
-        return inlineSimdCheck(callInfo, native, SimdTypeDescr::Float32x4);
-
-    typedef bool IsElementWise;
-    if (native == js::simd_int32x4_select)
-        return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Int32x4);
-    if (native == js::simd_int32x4_selectBits)
-        return inlineSimdSelect(callInfo, native, IsElementWise(false), SimdTypeDescr::Int32x4);
-    if (native == js::simd_float32x4_select)
-        return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Float32x4);
-
-    if (native == js::simd_int32x4_swizzle)
-        return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 1, 4);
-    if (native == js::simd_float32x4_swizzle)
-        return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 1, 4);
-    if (native == js::simd_int32x4_shuffle)
-        return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 2, 4);
-    if (native == js::simd_float32x4_shuffle)
-        return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 2, 4);
-
-    if (native == js::simd_int32x4_load)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 4);
-    if (native == js::simd_int32x4_load1)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 1);
-    if (native == js::simd_int32x4_load2)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 2);
-    if (native == js::simd_int32x4_load3)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 3);
-
-    if (native == js::simd_float32x4_load)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 4);
-    if (native == js::simd_float32x4_load1)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 1);
-    if (native == js::simd_float32x4_load2)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 2);
-    if (native == js::simd_float32x4_load3)
-        return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 3);
-
-    if (native == js::simd_int32x4_store)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 4);
-    if (native == js::simd_int32x4_store1)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 1);
-    if (native == js::simd_int32x4_store2)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 2);
-    if (native == js::simd_int32x4_store3)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 3);
-    if (native == js::simd_float32x4_store)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 4);
-    if (native == js::simd_float32x4_store1)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 1);
-    if (native == js::simd_float32x4_store2)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 2);
-    if (native == js::simd_float32x4_store3)
-        return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 3);
-
-    if (native == js::simd_int32x4_bool)
-        return inlineSimdBool(callInfo, native, SimdTypeDescr::Int32x4);
-
-    if (!target->jitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) {
-        // Reaching here means we tried to inline a native for which there is no
-        // Ion specialization.
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization);
-        return InliningStatus_NotInlined;
-    }
-
     switch (InlinableNative inlNative = target->jitInfo()->inlinableNative) {
       // Array natives.
+      case InlinableNative::Array:
+        return inlineArray(callInfo);
       case InlinableNative::ArrayIsArray:
         return inlineArrayIsArray(callInfo);
       case InlinableNative::ArrayPop:
         return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
       case InlinableNative::ArrayShift:
         return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
       case InlinableNative::ArrayPush:
         return inlineArrayPush(callInfo);
@@ -341,27 +175,51 @@ IonBuilder::inlineNativeCall(CallInfo& c
 
       // RegExp natives.
       case InlinableNative::RegExpExec:
         return CallResultEscapes(pc) ? inlineRegExpExec(callInfo) : inlineRegExpTest(callInfo);
       case InlinableNative::RegExpTest:
         return inlineRegExpTest(callInfo);
 
       // String natives.
+      case InlinableNative::String:
+        return inlineStringObject(callInfo);
       case InlinableNative::StringSplit:
         return inlineStringSplit(callInfo);
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
       case InlinableNative::StringFromCharCode:
         return inlineStrFromCharCode(callInfo);
       case InlinableNative::StringCharAt:
         return inlineStrCharAt(callInfo);
       case InlinableNative::StringReplace:
         return inlineStrReplace(callInfo);
 
+      // Object natives.
+      case InlinableNative::ObjectCreate:
+        return inlineObjectCreate(callInfo);
+
+      // Bound function.
+      case InlinableNative::CallBoundFunction:
+        return inlineBoundFunction(callInfo, target);
+
+      // SIMD natives.
+      case InlinableNative::SimdInt32x4:
+        return inlineSimdInt32x4(callInfo, target->native());
+      case InlinableNative::SimdFloat32x4:
+        return inlineSimdFloat32x4(callInfo, target->native());
+
+      // Testing functions.
+      case InlinableNative::TestBailout:
+        return inlineBailout(callInfo);
+      case InlinableNative::TestAssertFloat32:
+        return inlineAssertFloat32(callInfo);
+      case InlinableNative::TestAssertRecoveredOnBailout:
+        return inlineAssertRecoveredOnBailout(callInfo);
+
       // Slot intrinsics.
       case InlinableNative::IntrinsicUnsafeSetReservedSlot:
         return inlineUnsafeSetReservedSlot(callInfo);
       case InlinableNative::IntrinsicUnsafeGetReservedSlot:
         return inlineUnsafeGetReservedSlot(callInfo, MIRType_Value);
       case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
         return inlineUnsafeGetReservedSlot(callInfo, MIRType_Object);
       case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
@@ -3161,16 +3019,192 @@ IonBuilder::inlineConstructTypedObject(C
                                                 templateObject->group()->initialHeap(constraints()));
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native)
+{
+#define INLINE_INT32X4_SIMD_ARITH_(OP)                                                           \
+    if (native == js::simd_int32x4_##OP)                                                         \
+        return inlineBinarySimd<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_##OP,   \
+                                                  SimdTypeDescr::Int32x4);
+
+    ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_)
+#undef INLINE_INT32X4_SIMD_ARITH_
+
+#define INLINE_SIMD_BITWISE_(OP)                                                                 \
+    if (native == js::simd_int32x4_##OP)                                                         \
+        return inlineBinarySimd<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::OP##_, \
+                                                    SimdTypeDescr::Int32x4);
+
+    BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_)
+#undef INLINE_SIMD_BITWISE_
+
+    if (native == js::simd_int32x4_shiftLeftByScalar)
+        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_shiftRightArithmeticByScalar)
+        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_shiftRightLogicalByScalar)
+        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4);
+
+#define INLINE_SIMD_COMPARISON_(OP)                                                                \
+    if (native == js::simd_int32x4_##OP)                                                           \
+        return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4);
+
+    COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_)
+#undef INLINE_SIMD_COMPARISON_
+
+    if (native == js::simd_int32x4_extractLane)
+        return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_replaceLane)
+        return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4);
+
+    if (native == js::simd_int32x4_not)
+        return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_neg)
+        return inlineUnarySimd(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4);
+
+    typedef bool IsCast;
+    if (native == js::simd_int32x4_fromFloat32x4)
+        return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_fromFloat32x4Bits)
+        return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4);
+
+    if (native == js::simd_int32x4_splat)