Merge inbound to central, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Mon, 10 Aug 2015 14:39:09 -0700
changeset 289674 22aeb4618c996c6e67aa930f54313ec1c0ba20e3
parent 289651 3f135a8ead221c5df23def486f9a1d31629b45bf (current diff)
parent 289673 8e1e749c21bff81f50cafc962523233c9b6ad959 (diff)
child 289687 ddb29452ced04ba0a5c790547f97c8cd0c7fc3df
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 inbound to central, a=merge CLOSED TREE
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1000,67 +1000,37 @@ nsContextMenu.prototype = {
       Social._getProviderFromOrigin(this.browser.getAttribute("origin")).reload();
     } else {
       BrowserReloadOrDuplicate(event);
     }
   },
 
   // View Partial Source
   viewPartialSource: function(aContext) {
-    var focusedWindow = document.commandDispatcher.focusedWindow;
-    if (focusedWindow == window)
-      focusedWindow = gBrowser.selectedBrowser.contentWindowAsCPOW;
-
-    var docCharset = null;
-    if (focusedWindow)
-      docCharset = "charset=" + focusedWindow.document.characterSet;
-
-    // "View Selection Source" and others such as "View MathML Source"
-    // are mutually exclusive, with the precedence given to the selection
-    // when there is one
-    var reference = null;
-    if (aContext == "selection")
-      reference = focusedWindow.getSelection();
-    else if (aContext == "mathml")
-      reference = this.target;
-    else
-      throw "not reached";
-
-    let inTab = Services.prefs.getBoolPref("view_source.tab");
-    if (inTab) {
+    let inWindow = !Services.prefs.getBoolPref("view_source.tab");
+    let openSelectionFn = inWindow ? null : function() {
       let tabBrowser = gBrowser;
       // In the case of sidebars and chat windows, gBrowser is defined but null,
       // because no #content element exists.  For these cases, we need to find
       // the most recent browser window.
       // In the case of popups, we need to find a non-popup browser window.
       if (!tabBrowser || !window.toolbar.visible) {
         // This returns only non-popup browser windows by default.
         let browserWindow = RecentWindow.getMostRecentBrowserWindow();
         tabBrowser = browserWindow.gBrowser;
       }
       let tab = tabBrowser.loadOneTab("about:blank", {
         relatedToCurrent: true,
         inBackground: false
       });
-      let viewSourceBrowser = tabBrowser.getBrowserForTab(tab);
-      if (aContext == "selection") {
-        top.gViewSourceUtils
-           .viewSourceFromSelectionInBrowser(reference, viewSourceBrowser);
-      } else {
-        top.gViewSourceUtils
-           .viewSourceFromFragmentInBrowser(reference, aContext,
-                                            viewSourceBrowser);
-      }
-    } else {
-      // unused (and play nice for fragments generated via XSLT too)
-      var docUrl = null;
-      window.openDialog("chrome://global/content/viewPartialSource.xul",
-                        "_blank", "scrollbars,resizable,chrome,dialog=no",
-                        docUrl, docCharset, reference, aContext);
+      return tabBrowser.getBrowserForTab(tab);
     }
+
+    let target = aContext == "mathml" ? this.target : null;
+    top.gViewSourceUtils.viewPartialSourceInBrowser(gBrowser.selectedBrowser, target, openSelectionFn);
   },
 
   // Open new "view source" window with the frame's URL.
   viewFrameSource: function() {
     BrowserViewSourceOfDocument({
       browser: this.browser,
       URL: gContextMenuContentData.docLocation,
       outerWindowID: gContextMenuContentData.frameOuterWindowID,
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -1,31 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test checks that a <select> with an <optgroup> opens and can be navigated
 // in a child process. This is different than single-process as a <menulist> is used
 // to implement the dropdown list.
 
 const PAGECONTENT =
-  "<html><body onload='gChangeEvents = 0; document.body.firstChild.focus()'><select onchange='gChangeEvents++'>" +
+  "<html xmlns='http://www.w3.org/1999/xhtml'>" + 
+  "<body onload='gChangeEvents = 0; document.body.firstChild.focus()'><select onchange='gChangeEvents++'>" +
   "  <optgroup label='First Group'>" +
-  "    <option value=One>One" +
-  "    <option value=Two>Two" +
+  "    <option value='One'>One</option>" +
+  "    <option value='Two'>Two</option>" +
   "  </optgroup>" +
-  "  <option value=Three>Three" +
+  "  <option value='Three'>Three</option>" +
   "  <optgroup label='Second Group' disabled='true'>" +
-  "    <option value=Four>Four" +
-  "    <option value=Five>Five" +
+  "    <option value='Four'>Four</option>" +
+  "    <option value='Five'>Five</option>" +
   "  </optgroup>" +
-  "  <option value=Six disabled='true'>Six" +
+  "  <option value='Six' disabled='true'>Six</option>" +
   "  <optgroup label='Third Group'>" +
-  "    <option value=Seven>Seven" +
-  "    <option value=Eight>Eight" +
-  "  </optgroup></select><input>" +
+  "    <option value='Seven'>Seven</option>" +
+  "    <option value='Eight'>Eight</option>" +
+  "  </optgroup></select><input />" +
   "</body></html>";
 
 function openSelectPopup(selectPopup, withMouse)
 {
   let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
 
   if (withMouse) {
     return Promise.all([popupShownPromise,
@@ -52,20 +53,21 @@ function hideSelectPopup(selectPopup, wi
 
 function getChangeEvents()
 {
   return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     return content.wrappedJSObject.gChangeEvents;
   });
 }
 
-add_task(function*() {
+function doSelectTests(contentType)
+{
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   let browser = gBrowser.getBrowserForTab(tab);
-  yield promiseTabLoadEvent(tab, "data:text/html," + escape(PAGECONTENT));
+  yield promiseTabLoadEvent(tab, "data:" + contentType + "," + escape(PAGECONTENT));
 
   yield SimpleTest.promiseFocus(browser.contentWindow);
 
   let menulist = document.getElementById("ContentSelectDropdown");
   let selectPopup = menulist.menupopup;
 
   yield openSelectPopup(selectPopup);
 
@@ -121,10 +123,19 @@ add_task(function*() {
   EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
   yield hideSelectPopup(selectPopup, true);
   is((yield getChangeEvents()), isWindows ? 2 : 1, "Open and close with change - number of change events");
   EventUtils.synthesizeKey("VK_TAB", { });
   EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
   is((yield getChangeEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of change events");
 
   gBrowser.removeCurrentTab();
+}
+
+add_task(function*() {
+  yield doSelectTests("text/html");
 });
 
+add_task(function*() {
+  yield doSelectTests("application/xhtml+xml");
+});
+
+
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -35,18 +35,19 @@ AppleVDADecoder::AppleVDADecoder(const V
                                layers::ImageContainer* aImageContainer)
   : mTaskQueue(aVideoTaskQueue)
   , mCallback(aCallback)
   , mImageContainer(aImageContainer)
   , mPictureWidth(aConfig.mImage.width)
   , mPictureHeight(aConfig.mImage.height)
   , mDisplayWidth(aConfig.mDisplay.width)
   , mDisplayHeight(aConfig.mDisplay.height)
+  , mUseSoftwareImages(false)
+  , mIs106(!nsCocoaFeatures::OnLionOrLater())
   , mDecoder(nullptr)
-  , mIs106(!nsCocoaFeatures::OnLionOrLater())
 {
   MOZ_COUNT_CTOR(AppleVDADecoder);
   // TODO: Verify aConfig.mime_type.
 
   mExtraData = aConfig.mExtraData;
   mMaxRefFrames = 4;
   // Retrieve video dimensions from H264 SPS NAL.
   mp4_demuxer::SPSData spsdata;
@@ -243,52 +244,111 @@ AppleVDADecoder::ClearReorderedFrames()
   }
 }
 
 // Copy and return a decoded frame.
 nsresult
 AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
                              nsAutoPtr<AppleVDADecoder::AppleFrameRef> aFrameRef)
 {
-  IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
-  MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
-
   LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
-    aFrameRef->byte_offset,
-    aFrameRef->decode_timestamp.ToMicroseconds(),
-    aFrameRef->composition_timestamp.ToMicroseconds(),
-    aFrameRef->duration.ToMicroseconds(),
-    aFrameRef->is_sync_point ? " keyframe" : ""
+      aFrameRef->byte_offset,
+      aFrameRef->decode_timestamp.ToMicroseconds(),
+      aFrameRef->composition_timestamp.ToMicroseconds(),
+      aFrameRef->duration.ToMicroseconds(),
+      aFrameRef->is_sync_point ? " keyframe" : ""
   );
 
-  nsRefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
+  // Where our resulting image will end up.
+  nsRefPtr<VideoData> data;
   // Bounds.
   VideoInfo info;
   info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
   gfx::IntRect visible = gfx::IntRect(0,
                                       0,
                                       mPictureWidth,
                                       mPictureHeight);
 
-  nsRefPtr<layers::Image> image =
-    mImageContainer->CreateImage(ImageFormat::MAC_IOSURFACE);
-  layers::MacIOSurfaceImage* videoImage =
-    static_cast<layers::MacIOSurfaceImage*>(image.get());
-  videoImage->SetSurface(macSurface);
+  if (mUseSoftwareImages) {
+    size_t width = CVPixelBufferGetWidth(aImage);
+    size_t height = CVPixelBufferGetHeight(aImage);
+    DebugOnly<size_t> planes = CVPixelBufferGetPlaneCount(aImage);
+    MOZ_ASSERT(planes == 2, "Likely not NV12 format and it must be.");
+
+    VideoData::YCbCrBuffer buffer;
 
-  nsRefPtr<VideoData> data;
-  data = VideoData::CreateFromImage(info,
-                                    mImageContainer,
-                                    aFrameRef->byte_offset,
-                                    aFrameRef->composition_timestamp.ToMicroseconds(),
-                                    aFrameRef->duration.ToMicroseconds(),
-                                    image.forget(),
-                                    aFrameRef->is_sync_point,
-                                    aFrameRef->decode_timestamp.ToMicroseconds(),
-                                    visible);
+    // Lock the returned image data.
+    CVReturn rv = CVPixelBufferLockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
+    if (rv != kCVReturnSuccess) {
+      NS_ERROR("error locking pixel data");
+      mCallback->Error();
+      return NS_ERROR_FAILURE;
+    }
+    // Y plane.
+    buffer.mPlanes[0].mData =
+      static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 0));
+    buffer.mPlanes[0].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 0);
+    buffer.mPlanes[0].mWidth = width;
+    buffer.mPlanes[0].mHeight = height;
+    buffer.mPlanes[0].mOffset = 0;
+    buffer.mPlanes[0].mSkip = 0;
+    // Cb plane.
+    buffer.mPlanes[1].mData =
+      static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 1));
+    buffer.mPlanes[1].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 1);
+    buffer.mPlanes[1].mWidth = (width+1) / 2;
+    buffer.mPlanes[1].mHeight = (height+1) / 2;
+    buffer.mPlanes[1].mOffset = 0;
+    buffer.mPlanes[1].mSkip = 1;
+    // Cr plane.
+    buffer.mPlanes[2].mData =
+      static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 1));
+    buffer.mPlanes[2].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 1);
+    buffer.mPlanes[2].mWidth = (width+1) / 2;
+    buffer.mPlanes[2].mHeight = (height+1) / 2;
+    buffer.mPlanes[2].mOffset = 1;
+    buffer.mPlanes[2].mSkip = 1;
+
+    // Copy the image data into our own format.
+    data =
+      VideoData::Create(info,
+                        mImageContainer,
+                        nullptr,
+                        aFrameRef->byte_offset,
+                        aFrameRef->composition_timestamp.ToMicroseconds(),
+                        aFrameRef->duration.ToMicroseconds(),
+                        buffer,
+                        aFrameRef->is_sync_point,
+                        aFrameRef->decode_timestamp.ToMicroseconds(),
+                        visible);
+    // Unlock the returned image data.
+    CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
+  } else {
+    IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
+    MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
+
+    nsRefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
+
+    nsRefPtr<layers::Image> image =
+      mImageContainer->CreateImage(ImageFormat::MAC_IOSURFACE);
+    layers::MacIOSurfaceImage* videoImage =
+      static_cast<layers::MacIOSurfaceImage*>(image.get());
+    videoImage->SetSurface(macSurface);
+
+    data =
+      VideoData::CreateFromImage(info,
+                                 mImageContainer,
+                                 aFrameRef->byte_offset,
+                                 aFrameRef->composition_timestamp.ToMicroseconds(),
+                                 aFrameRef->duration.ToMicroseconds(),
+                                 image.forget(),
+                                 aFrameRef->is_sync_point,
+                                 aFrameRef->decode_timestamp.ToMicroseconds(),
+                                 visible);
+  }
 
   if (!data) {
     NS_ERROR("Couldn't create VideoData for frame");
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
   // Frames come out in DTS order but we need to output them
@@ -454,37 +514,52 @@ AppleVDADecoder::CreateDecoderSpecificat
                             ArrayLength(decoderKeys),
                             &kCFTypeDictionaryKeyCallBacks,
                             &kCFTypeDictionaryValueCallBacks);
 }
 
 CFDictionaryRef
 AppleVDADecoder::CreateOutputConfiguration()
 {
+  // Output format type:
+  SInt32 PixelFormatTypeValue = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
+  AutoCFRelease<CFNumberRef> PixelFormatTypeNumber =
+    CFNumberCreate(kCFAllocatorDefault,
+                   kCFNumberSInt32Type,
+                   &PixelFormatTypeValue);
+
+  if (mUseSoftwareImages) {
+    const void* outputKeys[] = { kCVPixelBufferPixelFormatTypeKey };
+    const void* outputValues[] = { PixelFormatTypeNumber };
+    static_assert(ArrayLength(outputKeys) == ArrayLength(outputValues),
+                  "Non matching keys/values array size");
+
+    return CFDictionaryCreate(kCFAllocatorDefault,
+                              outputKeys,
+                              outputValues,
+                              ArrayLength(outputKeys),
+                              &kCFTypeDictionaryKeyCallBacks,
+                              &kCFTypeDictionaryValueCallBacks);
+  }
+
   // Construct IOSurface Properties
   const void* IOSurfaceKeys[] = { MacIOSurfaceLib::kPropIsGlobal };
   const void* IOSurfaceValues[] = { kCFBooleanTrue };
   static_assert(ArrayLength(IOSurfaceKeys) == ArrayLength(IOSurfaceValues),
                 "Non matching keys/values array size");
 
   // Contruct output configuration.
   AutoCFRelease<CFDictionaryRef> IOSurfaceProperties =
     CFDictionaryCreate(kCFAllocatorDefault,
                        IOSurfaceKeys,
                        IOSurfaceValues,
                        ArrayLength(IOSurfaceKeys),
                        &kCFTypeDictionaryKeyCallBacks,
                        &kCFTypeDictionaryValueCallBacks);
 
-  SInt32 PixelFormatTypeValue = kCVPixelFormatType_32BGRA;
-  AutoCFRelease<CFNumberRef> PixelFormatTypeNumber =
-    CFNumberCreate(kCFAllocatorDefault,
-                   kCFNumberSInt32Type,
-                   &PixelFormatTypeValue);
-
   const void* outputKeys[] = { kCVPixelBufferIOSurfacePropertiesKey,
                                kCVPixelBufferPixelFormatTypeKey,
                                kCVPixelBufferOpenGLCompatibilityKey };
   const void* outputValues[] = { IOSurfaceProperties,
                                  PixelFormatTypeNumber,
                                  kCFBooleanTrue };
   static_assert(ArrayLength(outputKeys) == ArrayLength(outputValues),
                 "Non matching keys/values array size");
--- a/dom/media/platforms/apple/AppleVDADecoder.h
+++ b/dom/media/platforms/apple/AppleVDADecoder.h
@@ -94,20 +94,21 @@ public:
   MediaDataDecoderCallback* mCallback;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   ReorderQueue mReorderQueue;
   uint32_t mPictureWidth;
   uint32_t mPictureHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   uint32_t mMaxRefFrames;
+  bool mUseSoftwareImages;
+  bool mIs106;
 
 private:
   VDADecoder mDecoder;
-  bool mIs106;
 
   // Method to pass a frame to VideoToolbox for decoding.
   nsresult SubmitFrame(MediaRawData* aSample);
   // Method to set up the decompression session.
   nsresult InitializeSession();
   CFDictionaryRef CreateDecoderSpecification();
 };
 
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
+++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
@@ -7,17 +7,16 @@
 #include <CoreFoundation/CFString.h>
 
 #include "AppleCMLinker.h"
 #include "AppleUtils.h"
 #include "AppleVTDecoder.h"
 #include "AppleVTLinker.h"
 #include "mp4_demuxer/H264.h"
 #include "MediaData.h"
-#include "MacIOSurfaceImage.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 #include "VideoUtils.h"
 #include "gfxPlatform.h"
 
 PRLogModuleInfo* GetAppleMediaLog();
@@ -345,33 +344,29 @@ AppleVTDecoder::CreateDecoderExtensions(
                        atomsValue,
                        ArrayLength(atomsKey),
                        &kCFTypeDictionaryKeyCallBacks,
                        &kCFTypeDictionaryValueCallBacks);
 
   const void* extensionKeys[] =
     { kCVImageBufferChromaLocationBottomFieldKey,
       kCVImageBufferChromaLocationTopFieldKey,
-      AppleCMLinker::skPropExtensionAtoms,
-      AppleCMLinker::skPropFullRangeVideo /* Not defined in 10.6 */ };
+      AppleCMLinker::skPropExtensionAtoms };
 
   const void* extensionValues[] =
     { kCVImageBufferChromaLocation_Left,
       kCVImageBufferChromaLocation_Left,
-      atoms,
-      kCFBooleanTrue };
+      atoms };
   static_assert(ArrayLength(extensionKeys) == ArrayLength(extensionValues),
                 "Non matching keys/values array size");
 
   return CFDictionaryCreate(kCFAllocatorDefault,
                             extensionKeys,
                             extensionValues,
-                            AppleCMLinker::skPropFullRangeVideo ?
-                              ArrayLength(extensionKeys) :
-                              ArrayLength(extensionKeys) - 1,
+                            ArrayLength(extensionKeys),
                             &kCFTypeDictionaryKeyCallBacks,
                             &kCFTypeDictionaryValueCallBacks);
 }
 
 CFDictionaryRef
 AppleVTDecoder::CreateDecoderSpecification()
 {
   if (!AppleVTLinker::skPropEnableHWAccel) {
--- a/gfx/2d/MacIOSurface.cpp
+++ b/gfx/2d/MacIOSurface.cpp
@@ -41,31 +41,32 @@ void*                         MacIOSurfa
 void*                         MacIOSurfaceLib::sOpenGLFramework;
 void*                         MacIOSurfaceLib::sCoreGraphicsFramework;
 void*                         MacIOSurfaceLib::sCoreVideoFramework;
 IOSurfaceCreateFunc           MacIOSurfaceLib::sCreate;
 IOSurfaceGetIDFunc            MacIOSurfaceLib::sGetID;
 IOSurfaceLookupFunc           MacIOSurfaceLib::sLookup;
 IOSurfaceGetBaseAddressFunc   MacIOSurfaceLib::sGetBaseAddress;
 IOSurfaceGetBaseAddressOfPlaneFunc  MacIOSurfaceLib::sGetBaseAddressOfPlane;
-IOSurfaceSizeTFunc            MacIOSurfaceLib::sWidth;
-IOSurfaceSizeTFunc            MacIOSurfaceLib::sHeight;
+IOSurfaceSizePlaneTFunc       MacIOSurfaceLib::sWidth;
+IOSurfaceSizePlaneTFunc       MacIOSurfaceLib::sHeight;
 IOSurfaceSizeTFunc            MacIOSurfaceLib::sPlaneCount;
-IOSurfaceSizeTFunc            MacIOSurfaceLib::sBytesPerRow;
+IOSurfaceSizePlaneTFunc       MacIOSurfaceLib::sBytesPerRow;
 IOSurfaceGetPropertyMaximumFunc   MacIOSurfaceLib::sGetPropertyMaximum;
 IOSurfaceVoidFunc             MacIOSurfaceLib::sIncrementUseCount;
 IOSurfaceVoidFunc             MacIOSurfaceLib::sDecrementUseCount;
 IOSurfaceLockFunc             MacIOSurfaceLib::sLock;
 IOSurfaceUnlockFunc           MacIOSurfaceLib::sUnlock;
 CGLTexImageIOSurface2DFunc    MacIOSurfaceLib::sTexImage;
 IOSurfaceContextCreateFunc    MacIOSurfaceLib::sIOSurfaceContextCreate;
 IOSurfaceContextCreateImageFunc   MacIOSurfaceLib::sIOSurfaceContextCreateImage;
 IOSurfaceContextGetSurfaceFunc    MacIOSurfaceLib::sIOSurfaceContextGetSurface;
 CVPixelBufferGetIOSurfaceFunc MacIOSurfaceLib::sCVPixelBufferGetIOSurface;
 unsigned int                  (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr;
+IOSurfacePixelFormatFunc      MacIOSurfaceLib::sPixelFormat;
 
 CFStringRef                   MacIOSurfaceLib::kPropWidth;
 CFStringRef                   MacIOSurfaceLib::kPropHeight;
 CFStringRef                   MacIOSurfaceLib::kPropBytesPerElem;
 CFStringRef                   MacIOSurfaceLib::kPropBytesPerRow;
 CFStringRef                   MacIOSurfaceLib::kPropIsGlobal;
 
 bool MacIOSurfaceLib::isInit() {
@@ -97,32 +98,36 @@ void* MacIOSurfaceLib::IOSurfaceGetBaseA
                                                       size_t planeIndex) {
   return sGetBaseAddressOfPlane(aIOSurfacePtr, planeIndex);
 }
 
 size_t MacIOSurfaceLib::IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr) {
   return sPlaneCount(aIOSurfacePtr);
 }
 
-size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr) {
-  return sWidth(aIOSurfacePtr);
+size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr, size_t plane) {
+  return sWidth(aIOSurfacePtr, plane);
 }
 
-size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) {
-  return sHeight(aIOSurfacePtr);
+size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane) {
+  return sHeight(aIOSurfacePtr, plane);
 }
 
-size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) {
-  return sBytesPerRow(aIOSurfacePtr);
+size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr, size_t plane) {
+  return sBytesPerRow(aIOSurfacePtr, plane);
 }
 
 size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
   return sGetPropertyMaximum(property);
 }
 
+OSType MacIOSurfaceLib::IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr) {
+  return sPixelFormat(aIOSurfacePtr);
+}
+
 IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
                                        uint32_t options, uint32_t* seed) {
   return sLock(aIOSurfacePtr, options, seed);
 }
 
 IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
                                          uint32_t options, uint32_t *seed) {
   return sUnlock(aIOSurfacePtr, options, seed);
@@ -213,31 +218,32 @@ void MacIOSurfaceLib::LoadLibrary() {
 
   kPropWidth = GetIOConst("kIOSurfaceWidth");
   kPropHeight = GetIOConst("kIOSurfaceHeight");
   kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
   kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
   kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
   sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
   sGetID  = GET_IOSYM(sGetID,  "IOSurfaceGetID");
-  sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth");
-  sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight");
-  sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow");
+  sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidthOfPlane");
+  sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeightOfPlane");
+  sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRowOfPlane");
   sGetPropertyMaximum = GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum");
   sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
   sLock = GET_IOSYM(sLock, "IOSurfaceLock");
   sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
   sIncrementUseCount =
     GET_IOSYM(sIncrementUseCount, "IOSurfaceIncrementUseCount");
   sDecrementUseCount =
     GET_IOSYM(sDecrementUseCount, "IOSurfaceDecrementUseCount");
   sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
   sGetBaseAddressOfPlane =
     GET_IOSYM(sGetBaseAddressOfPlane, "IOSurfaceGetBaseAddressOfPlane");
   sPlaneCount = GET_IOSYM(sPlaneCount, "IOSurfaceGetPlaneCount");
+  sPixelFormat = GET_IOSYM(sPixelFormat, "IOSurfaceGetPixelFormat");
 
   sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
   sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
 
   sCVPixelBufferGetIOSurface =
     GET_CVSYM(sCVPixelBufferGetIOSurface, "CVPixelBufferGetIOSurface");
 
   // Optional symbols
@@ -369,24 +375,24 @@ void* MacIOSurface::GetBaseAddress() {
 }
 
 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex)
 {
   return MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(mIOSurfacePtr,
                                                          aPlaneIndex);
 }
 
-size_t MacIOSurface::GetWidth() {
+size_t MacIOSurface::GetWidth(size_t plane) {
   size_t intScaleFactor = ceil(mContentsScaleFactor);
-  return GetDevicePixelWidth() / intScaleFactor;
+  return GetDevicePixelWidth(plane) / intScaleFactor;
 }
 
-size_t MacIOSurface::GetHeight() {
+size_t MacIOSurface::GetHeight(size_t plane) {
   size_t intScaleFactor = ceil(mContentsScaleFactor);
-  return GetDevicePixelHeight() / intScaleFactor;
+  return GetDevicePixelHeight(plane) / intScaleFactor;
 }
 
 size_t MacIOSurface::GetPlaneCount() {
   return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
 }
 
 /*static*/ size_t MacIOSurface::GetMaxWidth() {
   if (!MacIOSurfaceLib::isInit())
@@ -395,26 +401,30 @@ size_t MacIOSurface::GetPlaneCount() {
 }
 
 /*static*/ size_t MacIOSurface::GetMaxHeight() {
   if (!MacIOSurfaceLib::isInit())
     return -1;
   return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight);
 }
 
-size_t MacIOSurface::GetDevicePixelWidth() {
-  return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
+size_t MacIOSurface::GetDevicePixelWidth(size_t plane) {
+  return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr, plane);
 }
 
-size_t MacIOSurface::GetDevicePixelHeight() {
-  return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr);
+size_t MacIOSurface::GetDevicePixelHeight(size_t plane) {
+  return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr, plane);
 }
 
-size_t MacIOSurface::GetBytesPerRow() { 
-  return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr);
+size_t MacIOSurface::GetBytesPerRow(size_t plane) {
+  return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr, plane);
+}
+
+OSType MacIOSurface::GetPixelFormat() {
+  return MacIOSurfaceLib::IOSurfaceGetPixelFormat(mIOSurfacePtr);
 }
 
 void MacIOSurface::IncrementUseCount() {
   MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr);
 }
 
 void MacIOSurface::DecrementUseCount() {
   MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr);
@@ -455,27 +465,62 @@ MacIOSurface::GetAsSurface() {
                                       mozilla::gfx::SurfaceFormat::B8G8R8X8;
 
   RefPtr<SourceSurfaceRawData> surf = new SourceSurfaceRawData();
   surf->InitWrappingData(dataCpy, IntSize(ioWidth, ioHeight), bytesPerRow, format, true);
 
   return surf.forget();
 }
 
+SurfaceFormat
+MacIOSurface::GetFormat()
+{
+  OSType pixelFormat = GetPixelFormat();
+  if (pixelFormat == '420v') {
+    return SurfaceFormat::NV12;
+  } else  {
+    return HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8;
+  }
+}
+
 CGLError
-MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx)
+MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, size_t plane)
 {
-  return MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx,
+  MOZ_ASSERT(plane >= 0);
+  OSType pixelFormat = GetPixelFormat();
+
+  GLenum internalFormat;
+  GLenum format;
+  GLenum type;
+  if (pixelFormat == '420v') {
+    MOZ_ASSERT(GetPlaneCount() == 2);
+    MOZ_ASSERT(plane < 2);
+
+    if (plane == 0) {
+      internalFormat = format = GL_LUMINANCE;
+    } else {
+      internalFormat = format = GL_LUMINANCE_ALPHA;
+    }
+    type = GL_UNSIGNED_BYTE;
+  } else  {
+    MOZ_ASSERT(plane == 0);
+
+    internalFormat = HasAlpha() ? GL_RGBA : GL_RGB;
+    format = GL_BGRA;
+    type = GL_UNSIGNED_INT_8_8_8_8_REV;
+  }
+  CGLError temp =  MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx,
                                                 GL_TEXTURE_RECTANGLE_ARB,
-                                                HasAlpha() ? GL_RGBA : GL_RGB,
-                                                GetDevicePixelWidth(),
-                                                GetDevicePixelHeight(),
-                                                GL_BGRA,
-                                                GL_UNSIGNED_INT_8_8_8_8_REV,
-                                                mIOSurfacePtr, 0);
+                                                internalFormat,
+                                                GetDevicePixelWidth(plane),
+                                                GetDevicePixelHeight(plane),
+                                                format,
+                                                type,
+                                                mIOSurfacePtr, plane);
+  return temp;
 }
 
 static
 CGColorSpaceRef CreateSystemColorSpace() {
   CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
   if (!cspace) {
     cspace = ::CGColorSpaceCreateDeviceRGB();
   }
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -19,32 +19,35 @@ typedef void (*IOSurfaceVoidFunc)(IOSurf
 typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options,
                                       uint32_t *seed);
 typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface,
                                         uint32_t options, uint32_t *seed);
 typedef void* (*IOSurfaceGetBaseAddressFunc)(IOSurfacePtr io_surface);
 typedef void* (*IOSurfaceGetBaseAddressOfPlaneFunc)(IOSurfacePtr io_surface,
                                                     size_t planeIndex);
 typedef size_t (*IOSurfaceSizeTFunc)(IOSurfacePtr io_surface);
+typedef size_t (*IOSurfaceSizePlaneTFunc)(IOSurfacePtr io_surface, size_t plane);
 typedef size_t (*IOSurfaceGetPropertyMaximumFunc) (CFStringRef property);
 typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt,
                              GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height,
                              GLenum format, GLenum type,
                              IOSurfacePtr ioSurface, GLuint plane);
 typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface,
                              unsigned width, unsigned height,
                              unsigned bitsPerComponent, unsigned bytes,
                              CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo);
 typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
 typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
 
 typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
   CVPixelBufferRef pixelBuffer);
 
+typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
+
 #import <OpenGL/OpenGL.h>
 #include "2D.h"
 #include "mozilla/RefPtr.h"
 
 struct _CGLContextObject;
 
 typedef _CGLContextObject* CGLContextObj;
 typedef struct CGContext* CGContextRef;
@@ -80,34 +83,36 @@ public:
   explicit MacIOSurface(const void *aIOSurfacePtr,
                         double aContentsScaleFactor = 1.0,
                         bool aHasAlpha = true);
   virtual ~MacIOSurface();
   IOSurfaceID GetIOSurfaceID();
   void *GetBaseAddress();
   void *GetBaseAddressOfPlane(size_t planeIndex);
   size_t GetPlaneCount();
+  OSType GetPixelFormat();
   // GetWidth() and GetHeight() return values in "display pixels".  A
   // "display pixel" is the smallest fully addressable part of a display.
   // But in HiDPI modes each "display pixel" corresponds to more than one
   // device pixel.  Use GetDevicePixel**() to get device pixels.
-  size_t GetWidth();
-  size_t GetHeight();
+  size_t GetWidth(size_t plane = 0);
+  size_t GetHeight(size_t plane = 0);
   double GetContentsScaleFactor() { return mContentsScaleFactor; }
-  size_t GetDevicePixelWidth();
-  size_t GetDevicePixelHeight();
-  size_t GetBytesPerRow();
+  size_t GetDevicePixelWidth(size_t plane = 0);
+  size_t GetDevicePixelHeight(size_t plane = 0);
+  size_t GetBytesPerRow(size_t plane = 0);
   void Lock();
   void Unlock();
   void IncrementUseCount();
   void DecrementUseCount();
   bool HasAlpha() { return mHasAlpha; }
+  mozilla::gfx::SurfaceFormat GetFormat();
   // We would like to forward declare NSOpenGLContext, but it is an @interface
   // and this file is also used from c++, so we use a void *.
-  CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt);
+  CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, size_t plane = 0);
   already_AddRefed<SourceSurface> GetAsSurface();
   CGContextRef CreateIOSurfaceContext();
 
   // FIXME This doesn't really belong here
   static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
   static already_AddRefed<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext,
                                                                         double aContentsScaleFactor = 1.0,
                                                                         bool aHasAlpha = true);
@@ -134,43 +139,44 @@ public:
   static IOSurfaceLookupFunc          sLookup;
   static IOSurfaceGetBaseAddressFunc  sGetBaseAddress;
   static IOSurfaceGetBaseAddressOfPlaneFunc sGetBaseAddressOfPlane;
   static IOSurfaceSizeTFunc           sPlaneCount;
   static IOSurfaceLockFunc            sLock;
   static IOSurfaceUnlockFunc          sUnlock;
   static IOSurfaceVoidFunc            sIncrementUseCount;
   static IOSurfaceVoidFunc            sDecrementUseCount;
-  static IOSurfaceSizeTFunc           sWidth;
-  static IOSurfaceSizeTFunc           sHeight;
-  static IOSurfaceSizeTFunc           sBytesPerRow;
+  static IOSurfaceSizePlaneTFunc      sWidth;
+  static IOSurfaceSizePlaneTFunc      sHeight;
+  static IOSurfaceSizePlaneTFunc      sBytesPerRow;
   static IOSurfaceGetPropertyMaximumFunc  sGetPropertyMaximum;
   static CGLTexImageIOSurface2DFunc   sTexImage;
   static IOSurfaceContextCreateFunc   sIOSurfaceContextCreate;
   static IOSurfaceContextCreateImageFunc  sIOSurfaceContextCreateImage;
   static IOSurfaceContextGetSurfaceFunc   sIOSurfaceContextGetSurface;
   static CVPixelBufferGetIOSurfaceFunc    sCVPixelBufferGetIOSurface;
+  static IOSurfacePixelFormatFunc     sPixelFormat;
   static CFStringRef                  kPropWidth;
   static CFStringRef                  kPropHeight;
   static CFStringRef                  kPropBytesPerElem;
   static CFStringRef                  kPropBytesPerRow;
   static CFStringRef                  kPropIsGlobal;
 
   static bool isInit();
   static CFStringRef GetIOConst(const char* symbole);
   static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
   static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
   static IOSurfaceID  IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
   static void*        IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
   static void*        IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
                                                      size_t aPlaneIndex);
   static size_t       IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr);
-  static size_t       IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr);
-  static size_t       IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr);
-  static size_t       IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr);
+  static size_t       IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr, size_t plane);
+  static size_t       IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane);
+  static size_t       IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr, size_t plane);
   static size_t       IOSurfaceGetPropertyMaximum(CFStringRef property);
   static IOReturn     IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
                                     uint32_t options, uint32_t *seed);
   static IOReturn     IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
                                       uint32_t options, uint32_t *seed);
   static void         IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr);
   static void         IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr);
   static CGLError     CGLTexImageIOSurface2D(CGLContextObj ctxt,
@@ -180,16 +186,17 @@ public:
                              IOSurfacePtr ioSurface, GLuint plane);
   static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
                              unsigned aWidth, unsigned aHeight,
                              unsigned aBitsPerCompoent, unsigned aBytes,
                              CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo);
   static CGImageRef   IOSurfaceContextCreateImage(CGContextRef ref);
   static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref);
   static IOSurfacePtr CVPixelBufferGetIOSurface(CVPixelBufferRef apixelBuffer);
+  static OSType       IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr);
   static unsigned int (*sCGContextGetTypePtr) (CGContextRef);
   static void LoadLibrary();
   static void CloseLibrary();
 
   // Static deconstructor
   static class LibraryUnloader {
   public:
     ~LibraryUnloader() {
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -32,26 +32,28 @@ enum class SurfaceType : int8_t {
 enum class SurfaceFormat : int8_t {
   B8G8R8A8,
   B8G8R8X8,
   R8G8B8A8,
   R8G8B8X8,
   R5G6B5,
   A8,
   YUV,
+  NV12,
   UNKNOWN
 };
 
 inline bool IsOpaque(SurfaceFormat aFormat)
 {
   switch (aFormat) {
   case SurfaceFormat::B8G8R8X8:
   case SurfaceFormat::R8G8B8X8:
   case SurfaceFormat::R5G6B5:
   case SurfaceFormat::YUV:
+  case SurfaceFormat::NV12:
     return true;
   default:
     return false;
   }
 }
 
 enum class FilterType : int8_t {
   BLEND = 0,
--- 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 1efd96aeade9
+Current version derived from upstream changeset e6539b6769cf
 
 See gfx/graphite2/moz-gr-update.sh for update procedure.
 
--- a/gfx/graphite2/include/graphite2/Font.h
+++ b/gfx/graphite2/include/graphite2/Font.h
@@ -24,18 +24,18 @@
     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 "graphite2/Types.h"
 
 #define GR2_VERSION_MAJOR   1
-#define GR2_VERSION_MINOR   2
-#define GR2_VERSION_BUGFIX  4
+#define GR2_VERSION_MINOR   3
+#define GR2_VERSION_BUGFIX  0
 
 #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
@@ -115,23 +115,55 @@ enum gr_attrCode {
     gr_slatJStretch,        
     /// Amount this slot can shrink (not implemented)
     gr_slatJShrink,         
     /// Granularity by which this slot can stretch or shrink (not implemented)
     gr_slatJStep,           
     /// Justification weight for this glyph (not implemented)
     gr_slatJWeight,         
     /// Amount this slot mush shrink or stretch in design units
-    gr_slatJWidth,          
+    gr_slatJWidth = 29,
     /// SubSegment split point
     gr_slatSegSplit = gr_slatJStretch + 29,
     /// User defined attribute, see subattr for user attr number
     gr_slatUserDefn,
     /// Bidi level
-    gr_slatBidiLevel,
+    gr_slatBidiLevel = 56,
+    /// Collision flags
+    gr_slatColFlags,
+    /// Collision constraint rectangle left (bl.x)
+    gr_slatColLimitblx,
+    /// Collision constraint rectangle lower (bl.y)
+    gr_slatColLimitbly,
+    /// Collision constraint rectangle right (tr.x)
+    gr_slatColLimittrx,
+    /// Collision constraint rectangle upper (tr.y)
+    gr_slatColLimittry,
+    /// Collision shift x
+    gr_slatColShiftx,
+    /// Collision shift y
+    gr_slatColShifty,
+    /// Collision margin
+    gr_slatColMargin,
+    /// Margin cost weight
+    gr_slatColMarginWt,
+    // Additional glyph that excludes movement near this one:
+    gr_slatColExclGlyph,
+    gr_slatColExclOffx,
+    gr_slatColExclOffy,
+    // Collision sequence enforcing attributes:
+    gr_slatSeqClass,
+    gr_slatSeqProxClass,
+    gr_slatSeqOrder,
+    gr_slatSeqAboveXoff,
+    gr_slatSeqAboveWt,
+    gr_slatSeqBelowXlim,
+    gr_slatSeqBelowWt,
+    gr_slatSeqValignHt,
+    gr_slatSeqValignWt,
                             
     /// not implemented
     gr_slatMax,             
     /// not implemented
     gr_slatNoEffect = gr_slatMax + 1    
 };
 
 enum gr_bidirtl {
--- a/gfx/graphite2/include/graphite2/Types.h
+++ b/gfx/graphite2/include/graphite2/Types.h
@@ -53,17 +53,20 @@ enum gr_encform {
   #else
     #if defined __GNUC__
       #define GR2_API    __attribute__((dllimport))
     #else
       #define GR2_API    __declspec(dllimport)
     #endif
   #endif
   #define GR2_LOCAL
-#else
-  #if __GNUC__ >= 4
-    #define GR2_API      __attribute__ ((visibility("default")))
-    #define GR2_LOCAL       __attribute__ ((visibility("hidden")))
+#elif __GNUC__ >= 4
+  #if defined GRAPHITE2_STATIC
+    #define GR2_API      __attribute__ ((visibility("hidden")))
   #else
-    #define GR2_API
-    #define GR2_LOCAL
+    #define GR2_API      __attribute__ ((visibility("default")))
   #endif
+  #define GR2_LOCAL      __attribute__ ((visibility("hidden")))
+#else
+  #define GR2_API
+  #define GR2_LOCAL
 #endif
+
--- a/gfx/graphite2/src/Bidi.cpp
+++ b/gfx/graphite2/src/Bidi.cpp
@@ -57,18 +57,18 @@ enum DirCode {  // Hungarian: dirc
         PDI        = 20,   // pop isolate
         OPP        = 21,   // opening paired parenthesis
         CPP        = 22,   // closing paired parenthesis
 
         ON = N
 };
 
 enum DirMask {
-        WSflag = (1 << 7),     // keep track of WS for eos handling
-        WSMask = ~(1 << 7)
+        WSflag = int8(1 << 7),     // keep track of WS for eos handling
+        WSMask = int8(~(1 << 7))
 };
 
 inline uint8    BaseClass(Slot *s)   { return s->getBidiClass() & WSMask; }
 
 unsigned int bidi_class_map[] = { 0, 1, 2, 5, 4, 8, 9, 3, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0 };
 // Algorithms based on Unicode reference standard code. Thanks Asmus Freitag.
 
 void resolveWeak(Slot *start, int sos, int eos);
@@ -535,17 +535,17 @@ void resolveWeak(Slot *start, int sos, i
 
 void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack)
 {
     uint8 mask = 0;
     int8 lastDir = -1;
     BracketPair *p;
     for ( ; s; s = s->prev())       // walk the sequence
     {
-        uint16 ogid = seg->glyphAttr(s->gid(), aMirror);
+        uint16 ogid = seg->glyphAttr(s->gid(), aMirror) || s->gid();
         int cls = BaseClass(s);
         
         switch(cls)
         {
         case OPP :
             stack.orin(mask);
             stack.push(ogid, s, lastDir, lastDir != CPP);
             mask = 0;
@@ -566,45 +566,48 @@ void processParens(Slot *s, Segment *seg
         case R :
         case AL :
         case AN :
         case EN :
             lastDir = R;
             mask |= 2;
         }
     }
-    for (p = stack.start(); p; p =p->next())      // walk the stack
+    if (stack.size())
     {
-        if (p->close() && p->mask())
+        for (p = stack.start(); p; p =p->next())      // walk the stack
         {
-            int dir = (level & 1) + 1;
-            if (p->mask() & dir)
-            { }
-            else if (p->mask() & (1 << (~level & 1)))  // if inside has strong other embedding
+            if (p->close() && p->mask())
             {
-                int ldir = p->before();
-                if ((p->before() == OPP || p->before() == CPP) && p->prev())
+                int dir = (level & 1) + 1;
+                if (p->mask() & dir)
+                { }
+                else if (p->mask() & (1 << (~level & 1)))  // if inside has strong other embedding
                 {
-                    for (BracketPair *q = p->prev(); q; q = q->prev())
+                    int ldir = p->before();
+                    if ((p->before() == OPP || p->before() == CPP) && p->prev())
                     {
-                        ldir = q->open()->getBidiClass();
-                        if (ldir < 3) break;
-                        ldir = q->before();
-                        if (ldir < 3) break;
+                        for (BracketPair *q = p->prev(); q; q = q->prev())
+                        {
+                            ldir = q->open()->getBidiClass();
+                            if (ldir < 3) break;
+                            ldir = q->before();
+                            if (ldir < 3) break;
+                        }
+                        if (ldir > 2) ldir = 0;
                     }
-                    if (ldir > 2) ldir = 0;
+                    if (ldir > 0 && (ldir - 1) != (level & 1))     // is dir given opp. to level dir (ldir == R or L)
+                        dir = (~level & 1) + 1;
                 }
-                if (ldir > 0 && (ldir - 1) != (level & 1))     // is dir given opp. to level dir (ldir == R or L)
-                    dir = (~level & 1) + 1;
+                p->open()->setBidiClass(dir);
+                p->close()->setBidiClass(dir);
             }
-            p->open()->setBidiClass(dir);
-            p->close()->setBidiClass(dir);
         }
+        stack.clear();
     }
-    stack.clear();
 }
 
 int GetDeferredNeutrals(int action, int level)
 {
         action = (action >> 4) & 0xF;
         if (action == (En >> 4))
             return EmbeddingDirection(level);
         else
--- a/gfx/graphite2/src/CMakeLists.txt
+++ b/gfx/graphite2/src/CMakeLists.txt
@@ -73,25 +73,28 @@ add_library(graphite2 SHARED
     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
     GlyphFace.cpp
     GlyphCache.cpp
+    Intervals.cpp
     Justifier.cpp
     NameTable.cpp
     Pass.cpp
-    Rule.cpp
+    Position.cpp
     Segment.cpp
     Silf.cpp
     Slot.cpp
     Sparse.cpp
     TtfUtil.cpp
     UtfCodec.cpp
     ${FILEFACE}
     ${SEGCACHE}
@@ -107,17 +110,17 @@ set_target_properties(graphite2 PROPERTI
 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"
+        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"
         LINK_FLAGS      "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}" 
         LINKER_LANGUAGE C)
     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)
--- a/gfx/graphite2/src/Code.cpp
+++ b/gfx/graphite2/src/Code.cpp
@@ -72,16 +72,17 @@ 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;
@@ -89,107 +90,112 @@ public:
         byte      max_ref;
         
         analysis() : slotref(0), max_ref(0) {};
         void set_ref(int index) throw();
         void set_changed(int index) throw();
 
     };
     
-    decoder(const limits & lims, Code &code) 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);
     byte        max_ref() { return _analysis.max_ref; }
     int         pre_context() const { return _pre_context; }
     
 private:
     opcode      fetch_opcode(const byte * bc);
     void        analyse_opcode(const opcode, const int8 * const dp) throw();
     bool        emit_opcode(opcode opc, const byte * & bc);
     bool        validate_opcode(const opcode opc, const byte * const bc);
     bool        valid_upto(const uint16 limit, const uint16 x) const throw();
+    bool        test_context() const throw();
     void        failure(const status_t s) const throw() { _code.failure(s); }
     
     Code              & _code;
     int                 _pre_context;
     uint16              _rule_length;
     instr             * _instr;
     byte              * _data;
-    const limits      & _max;
+    limits            & _max;
     analysis            _analysis;
+    enum passtype       _passtype;
+    int                 _stack_depth;
 };
 
 
 struct Machine::Code::decoder::limits
 {
-  const byte * const bytecode;
+  const byte       * bytecode;
   const uint8        pre_context;
   const uint16       rule_length,
                      classes,
                      glyf_attrs,
                      features;
   const byte         attrid[gr_slatMax];
 };
    
-inline Machine::Code::decoder::decoder(const limits & lims, Code &code) throw()
+inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw()
 : _code(code),
   _pre_context(code._constraint ? 0 : lims.pre_context), 
   _rule_length(code._constraint ? 1 : lims.rule_length), 
-  _instr(code._code), _data(code._data), _max(lims)
+  _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)
+           uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
+           enum passtype pt, byte * & _out)
  :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
-    _constraint(is_constraint), _modify(false), _delete(false), _own(true)
+    _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();
+      // ::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 
     // estimate.  Once we know their real sizes the we'll shrink them.
-    _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
-                                             * sizeof(instr)));
-    _data = static_cast<byte *>(malloc((bytecode_end - bytecode_begin)
-                                             * sizeof(byte)));
+    if (_out)   _code = reinterpret_cast<instr *>(_out);
+    else        _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
+                                             * (sizeof(instr)+sizeof(byte))));
+    _data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
     
     if (!_code || !_data) {
         failure(alloc_failed);
         return;
     }
     
-    const decoder::limits lims = {
+    decoder::limits lims = {
         bytecode_end,
         pre_context,
         rule_length,
         silf.numClasses(),
         face.glyphs().numAttrs(),
         face.numFeatures(), 
         {1,1,1,1,1,1,1,1, 
          1,1,1,1,1,1,1,255,
          1,1,1,1,1,1,1,1, 
          1,1,1,1,1,1,0,0, 
          0,0,0,0,0,0,0,0, 
          0,0,0,0,0,0,0,0, 
          0,0,0,0,0,0,0, silf.numUser()}
     };
     
-    decoder dec(lims, *this);
+    decoder dec(lims, *this, pt);
     if(!dec.load(bytecode_begin, bytecode_end))
        return;
     
     // Is this an empty program?
     if (_instr_count == 0)
     {
       release_buffers();
       ::new (this) Code();
@@ -206,18 +212,23 @@ Machine::Code::Code(bool is_constraint, 
     dec.apply_analysis(_code, _code + _instr_count);
     _max_ref = dec.max_ref();
     
     // 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));
-    _code = static_cast<instr *>(realloc(_code, (_instr_count+1)*sizeof(instr)));
-    _data = static_cast<byte *>(realloc(_data, _data_size*sizeof(byte)));
+    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;
+    else
+        _code = static_cast<instr *>(realloc(_code, total_sz));
+   _data = reinterpret_cast<byte *>(_code + (_instr_count+1));
 
     if (!_code)
     {
         failure(alloc_failed);
         return;
     }
 
     // Make this RET_ZERO, we should never reach this but just in case ...
@@ -232,16 +243,17 @@ Machine::Code::~Code() throw ()
 {
     if (_own)
         release_buffers();
 }
 
 
 bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
 {
+    _max.bytecode = bc_end;
     while (bc < bc_end)
     {
         const opcode opc = fetch_opcode(bc++);
         if (opc == vm::MAX_OPCODE)
             return false;
         
         analyse_opcode(opc, reinterpret_cast<const int8 *>(bc));
         
@@ -261,141 +273,191 @@ opcode Machine::Code::decoder::fetch_opc
 
     // Do some basic sanity checks based on what we know about the opcode
     if (!validate_opcode(opc, bc))  return MAX_OPCODE;
 
     // And check it's arguments as far as possible
     switch (opc)
     {
         case NOP :
+            break;
         case PUSH_BYTE :
         case PUSH_BYTEU :
         case PUSH_SHORT :
         case PUSH_SHORTU :
         case PUSH_LONG :
+            ++_stack_depth;
+            break;
         case ADD :
         case SUB :
         case MUL :
         case DIV :
         case MIN_ :
         case MAX_ :
-        case NEG :
-        case TRUNC8 :
-        case TRUNC16 :
-        case COND :
         case AND :
         case OR :
-        case NOT :
         case EQUAL :
         case NOT_EQ :
         case LESS :
         case GTR :
         case LESS_EQ :
         case GTR_EQ :
+        case BITOR :
+        case BITAND :
+            if (--_stack_depth <= 0)
+                failure(underfull_stack);
+            break;
+        case NEG :
+        case TRUNC8 :
+        case TRUNC16 :
+        case NOT :
+        case BITNOT :
+        case BITSET :
+            if (_stack_depth <= 0)
+                failure(underfull_stack);
+            break;
+        case COND :
+            _stack_depth -= 2;
+            if (_stack_depth <= 0)
+                failure(underfull_stack);
             break;
         case NEXT :
         case NEXT_N :           // runtime checked
         case COPY_NEXT :
+            test_context();
             ++_pre_context;
             break;
         case PUT_GLYPH_8BIT_OBS :
             valid_upto(_max.classes, bc[0]);
+            test_context();
             break;
         case PUT_SUBS_8BIT_OBS :
             valid_upto(_rule_length, _pre_context + int8(bc[0]));
             valid_upto(_max.classes, bc[1]);
             valid_upto(_max.classes, bc[2]);
+            test_context();
             break;
         case PUT_COPY :
             valid_upto(_rule_length, _pre_context + int8(bc[0]));
+            test_context();
             break;
         case INSERT :
-            --_pre_context;
+            if (_passtype >= PASS_TYPE_POSITIONING)
+                failure(invalid_opcode);
+            else
+                --_pre_context;
             break;
         case DELETE :
+            if (_passtype >= PASS_TYPE_POSITIONING)
+                failure(invalid_opcode);
+            test_context();
             break;
         case ASSOC :
             for (uint8 num = bc[0]; num; --num)
                 valid_upto(_rule_length, _pre_context + int8(bc[num]));
+            test_context();
             break;
         case CNTXT_ITEM :
             valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
             if (bc + 2 + bc[1] >= _max.bytecode)  failure(jump_past_end);
             if (_pre_context != 0)                failure(nested_context_item);
             break;
         case ATTR_SET :
         case ATTR_ADD :
         case ATTR_SUB :
         case ATTR_SET_SLOT :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
             valid_upto(gr_slatMax, bc[0]);
+            test_context();
             break;
         case IATTR_SET_SLOT :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
             if (valid_upto(gr_slatMax, bc[0]))
                 valid_upto(_max.attrid[bc[0]], bc[1]);
+            test_context();
             break;
         case PUSH_SLOT_ATTR :
+            ++_stack_depth;
             valid_upto(gr_slatMax, bc[0]);
             valid_upto(_rule_length, _pre_context + int8(bc[1]));
             break;
         case PUSH_GLYPH_ATTR_OBS :
+            ++_stack_depth;
             valid_upto(_max.glyf_attrs, bc[0]);
             valid_upto(_rule_length, _pre_context + int8(bc[1]));
             break;
         case PUSH_GLYPH_METRIC :
+            ++_stack_depth;
             valid_upto(kgmetDescent, bc[0]);
             valid_upto(_rule_length, _pre_context + int8(bc[1]));
             // level: dp[2] no check necessary
             break;
         case PUSH_FEAT :
+            ++_stack_depth;
             valid_upto(_max.features, bc[0]);
             valid_upto(_rule_length, _pre_context + int8(bc[1]));
             break;
         case PUSH_ATT_TO_GATTR_OBS :
+            ++_stack_depth;
             valid_upto(_max.glyf_attrs, bc[0]);
             valid_upto(_rule_length, _pre_context + int8(bc[1]));
             break;
         case PUSH_ATT_TO_GLYPH_METRIC :
+            ++_stack_depth;
             valid_upto(kgmetDescent, bc[0]);
             valid_upto(_rule_length, _pre_context + int8(bc[1]));
             // level: dp[2] no check necessary
             break;
         case PUSH_ISLOT_ATTR :
+            ++_stack_depth;
             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;
         case POP_RET :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
         case RET_ZERO :
         case RET_TRUE :
             break;
         case IATTR_SET :
         case IATTR_ADD :
         case IATTR_SUB :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
             if (valid_upto(gr_slatMax, bc[0]))
                 valid_upto(_max.attrid[bc[0]], bc[1]);
+            test_context();
             break;
         case PUSH_PROC_STATE :  // dummy: dp[0] no check necessary
         case PUSH_VERSION :
+            ++_stack_depth;
             break;
         case PUT_SUBS :
             valid_upto(_rule_length, _pre_context + int8(bc[0]));
             valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
             valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
+            test_context();
             break;
         case PUT_SUBS2 :        // not implemented
         case PUT_SUBS3 :        // not implemented
             break;
         case PUT_GLYPH :
             valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
+            test_context();
             break;
         case PUSH_GLYPH_ATTR :
         case PUSH_ATT_TO_GLYPH_ATTR :
+            ++_stack_depth;
             valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
             valid_upto(_rule_length, _pre_context + int8(bc[2]));
             break;
         default:
             failure(invalid_opcode);
             break;
     }
 
@@ -501,26 +563,30 @@ bool Machine::Code::decoder::emit_opcode
         assert(_pre_context == 0);
         _pre_context = _max.pre_context + int8(_data[-2]);
         _rule_length = _max.rule_length;
 
         const size_t ctxt_start = _code._instr_count;
         byte & instr_skip = _data[-1];
         byte & data_skip  = *_data++;
         ++_code._data_size;
+        const byte *curr_end = _max.bytecode;
 
         if (load(bc, bc + instr_skip))
         {
             bc += instr_skip;
             data_skip  = instr_skip - (_code._instr_count - ctxt_start);
             instr_skip = _code._instr_count - ctxt_start;
+            _max.bytecode = curr_end;
 
             _rule_length = 1;
             _pre_context = 0;
         }
+        else
+            return false;
     }
     
     return bool(_code);
 }
 
 
 void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
 {
@@ -549,32 +615,41 @@ bool Machine::Code::decoder::validate_op
 {
     if (opc >= MAX_OPCODE)
     {
         failure(invalid_opcode);
         return false;
     }
     const opcode_t & op = Machine::getOpcodeTable()[opc];
     const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
-    if (bc + param_sz > _max.bytecode)
+    if (bc - 1 + param_sz > _max.bytecode)
     {
         failure(arguments_exhausted);
         return false;
     }
     return true;
 }
 
 
 bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw()
 {
     const bool t = x < limit;
     if (!t) failure(out_of_range_data);
     return t;
 }
 
+bool Machine::Code::decoder::test_context() const throw()
+{
+    if (_pre_context >= _rule_length)
+    {
+        failure(out_of_range_data);
+        return false;
+    }
+    return true;
+}
 
 inline 
 void Machine::Code::failure(const status_t s) throw() {
     release_buffers();
     _status = s;
 }
 
 
@@ -589,31 +664,30 @@ 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::release_buffers() throw()
 {
-    free(_code);
-    free(_data);
+    if (_own)
+        free(_code);
     _code = 0;
     _data = 0;
     _own  = false;
 }
 
 
 int32 Machine::Code::run(Machine & m, slotref * & map) const
 {
-    assert(_own);
+//    assert(_own);
     assert(*this);          // Check we are actually runnable
 
     if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()))
     {
         m._status = Machine::slot_offset_out_bounds;
-//        return (m.slotMap().end() - map);
         return 1;
     }
 
     return  m.run(_code, _data, map);
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/Collider.cpp
@@ -0,0 +1,1080 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, 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.
+*/
+#include <algorithm>
+#include <limits>
+#include <math.h>
+#include <string>
+#include <functional>
+#include "inc/Collider.h"
+#include "inc/Segment.h"
+#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
+
+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,
+    const Position &currShift, const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout)
+{
+    int i;
+    float max, min;
+    float a, shift;
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    unsigned short gid = aSlot->gid();
+    if (!gc.check(gid))
+        return false;
+    const BBox &bb = gc.getBoundingBBox(gid);
+    const SlantBox &sb = gc.getBoundingSlantBox(gid);
+    //float sx = aSlot->origin().x + currShift.x;
+    //float sy = aSlot->origin().y + currShift.y;
+    if (currOffset.x != 0.f || currOffset.y != 0.f)
+        _limit = Rect(limit.bl - currOffset, limit.tr - currOffset);
+    else
+        _limit = limit;
+    // For a ShiftCollider, these indices indicate which vector we are moving by:
+    // each _ranges represents absolute space with respect to the origin of the slot. Thus take into account true origins but subtract the vmin for the slot
+    for (i = 0; i < 4; ++i)
+    {
+        switch (i) {
+            case 0 :	// x direction
+                min = _limit.bl.x + currOffset.x;
+                max = _limit.tr.x + currOffset.x;
+                _len[i] = bb.xa - bb.xi;
+                a = currOffset.y + currShift.y;
+                _ranges[i].initialise<XY>(min, max, margin, marginWeight, a);
+                break;
+            case 1 :	// y direction
+                min = _limit.bl.y + currOffset.y;
+                max = _limit.tr.y + currOffset.y;
+                _len[i] = bb.ya - bb.yi;
+                a = currOffset.x + currShift.x;
+                _ranges[i].initialise<XY>(min, max, margin, marginWeight, a);
+                break;
+            case 2 :	// sum (negatively sloped diagonal boundaries)
+                // pick closest x,y limit boundaries in s direction
+                shift = currOffset.x + currOffset.y + currShift.x + currShift.y;
+                min = -2 * std::min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift;
+                max = 2 * std::min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift;
+                _len[i] = sb.sa - sb.si;
+                a = currOffset.x - currOffset.y + currShift.x - currShift.y;
+                _ranges[i].initialise<SD>(min, max, margin / ISQRT2, marginWeight, a);
+                break;
+            case 3 :	// diff (positively sloped diagonal boundaries)
+                // pick closest x,y limit boundaries in d direction
+                shift = currOffset.x - currOffset.y + currShift.x - currShift.y;
+                min = -2 * std::min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift;
+                max = 2 * std::min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift;
+                _len[i] = sb.da - sb.di;
+                a = currOffset.x + currOffset.y + currShift.x + currShift.y;
+                _ranges[i].initialise<SD>(min, max, margin / ISQRT2, marginWeight, a);
+                break;
+        }
+    }
+
+	_target = aSlot;
+    if ((dir & 1) == 0)
+    {
+        // For LTR, switch and negate x limits.
+        _limit.bl.x = -1 * limit.tr.x;
+        //_limit.tr.x = -1 * limit.bl.x;
+    }
+    _currOffset = currOffset;
+    _currShift = currShift;
+    _origin = aSlot->origin() - currOffset;     // the original anchor position of the glyph
+
+	_margin = margin;
+	_marginWt = marginWeight;
+    
+    SlotCollision *c = seg->collisionInfo(aSlot);
+    _seqClass = c->seqClass();
+	_seqProxClass = c->seqProxClass();
+    _seqOrder = c->seqOrder();
+    return true;
+}
+
+template <class O>
+float sdm(float vi, float va, float mx, float my, O op)
+{
+    float res = 2 * mx - vi;
+    if (op(res, vi + 2 * my))
+    {
+        res = va + 2 * my;
+        if (op(res, 2 * mx - va))
+            res = mx + my;
+    }
+    return res;
+}
+
+// Mark an area with a cost that can vary along the x or y axis. The region is expressed in terms of the centre of the target glyph in each axis
+void ShiftCollider::addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int axis)
+{
+    float a, c;
+    switch (axis) {
+        case 0 :
+             if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
+            {
+                a = org.y + 0.5f * (bb.yi + bb.ya);
+                c = 0.5f * (bb.xi + bb.xa);
+                if (isx)
+                    _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, m,
+                                                (minright ? box.tr.x : box.bl.x) - c, a, 0, false);
+                else
+                    _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y,
+                                                m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false);
+            }
+            break;
+        case 1 :
+            if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
+            {
+                a = org.x + 0.5f * (bb.xi + bb.xa);
+                c = 0.5f * (bb.yi + bb.ya);
+                if (isx)
+                    _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x,
+                                                m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false);
+                else
+                    _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, m, 
+                                                (minright ? box.tr.y : box.bl.y) - c, a, 0, false);
+            }
+            break;
+        case 2 :
+            if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di)
+            {
+                float d = org.x - org.y + 0.5f * (sb.di + sb.da);
+                c = 0.5f * (sb.si + sb.sa);
+                float smax = std::min(2 * box.tr.x - d, 2 * box.tr.y + d);
+                float smin = std::max(2 * box.bl.x - d, 2 * box.bl.y + d);
+                if (smin > smax) return;
+                float si;
+                a = d;
+                if (isx)
+                    si = 2 * (minright ? box.tr.x : box.bl.x) - a;
+                else
+                    si = 2 * (minright ? box.tr.y : box.bl.y) + a;
+                _ranges[axis].weighted<SD>(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx);
+            }
+            break;
+        case 3 :
+            if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si)
+            {
+                float s = org.x + org.y + 0.5f * (sb.si + sb.sa);
+                c = 0.5f * (sb.di + sb.da);
+                float dmax = std::min(2 * box.tr.x - s, s - 2 * box.bl.y);
+                float dmin = std::max(2 * box.bl.x - s, s - 2 * box.tr.y);
+                if (dmin > dmax) return;
+                float di;
+                a = s;
+                if (isx)
+                    di = 2 * (minright ? box.tr.x : box.bl.x) - a;
+                else
+                    di = 2 * (minright ? box.tr.y : box.bl.y) + a;
+                _ranges[axis].weighted<SD>(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx);
+            }
+            break;
+        default :
+            break;
+    }
+    return;
+}
+
+// Mark an area with an absolute cost, making it completely inaccessible.
+inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis)
+{
+    float c;
+    switch (axis) {
+        case 0 :
+            if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
+            {
+                c = 0.5f * (bb.xi + bb.xa);
+                _ranges[axis].exclude(box.bl.x - c, box.tr.x - c);
+            }
+            break;
+        case 1 :
+            if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
+            {
+                c = 0.5f * (bb.yi + bb.ya);
+                _ranges[axis].exclude(box.bl.y - c, box.tr.y - c);
+            }
+            break;
+        case 2 :
+            if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di 
+                && box.width() > 0 && box.height() > 0)
+            {
+                float di = org.x - org.y + sb.di;
+                float da = org.x - org.y + sb.da;
+                float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater<float>());
+                float smin = sdm(da, di, box.bl.x, box.bl.y, std::less<float>());
+                c = 0.5f * (sb.si + sb.sa);
+                _ranges[axis].exclude(smin - c, smax - c);
+            }
+            break;
+        case 3 :
+            if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si 
+                && box.width() > 0 && box.height() > 0)
+            {
+                float si = org.x + org.y + sb.si;
+                float sa = org.x + org.y + sb.sa;
+                float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater<float>());
+                float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less<float>());
+                c = 0.5f * (sb.di + sb.da);
+                _ranges[axis].exclude(dmin - c, dmax - c);
+            }
+            break;
+        default :
+            break;
+    }
+    return;
+}
+
+// Adjust the movement limits for the target to avoid having it collide
+// with the given neighbor slot. Also determine if there is in fact a collision
+// between the target and the given slot.
+bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift,
+		bool isAfter,  // slot is logically after _target
+		bool sameCluster, bool &hasCol, bool isExclusion,
+        GR_MAYBE_UNUSED json * const dbgout )
+{
+    bool isCol = false;
+    const float tx = _currOffset.x + _currShift.x;
+    const float ty = _currOffset.y + _currShift.y;
+    const float td = tx - ty;
+    const float ts = tx + ty;
+    const float sx = slot->origin().x - _origin.x + currShift.x;
+    const float sy = slot->origin().y - _origin.y + currShift.y;
+    const float sd = sx - sy;
+    const float ss = sx + sy;
+    float vmin, vmax;
+    float omin, omax, otmin, otmax;
+    float cmin, cmax;   // target limits
+    float torg;
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    const unsigned short gid = slot->gid();
+    if (!gc.check(gid))
+        return false;
+    const unsigned short tgid = _target->gid();
+    const BBox &bb = gc.getBoundingBBox(gid);
+    const SlantBox &sb = gc.getBoundingSlantBox(gid);
+    const BBox &tbb = gc.getBoundingBBox(tgid);
+    const SlantBox &tsb = gc.getBoundingSlantBox(tgid);
+
+    SlotCollision * cslot = seg->collisionInfo(slot);
+    int orderFlags = 0;
+    bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass;
+    if (sameCluster && _seqClass 
+        && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass)))
+		// Force the target glyph to be in the specified direction from the slot we're testing.
+        orderFlags = _seqOrder;
+    float seq_above_wt = cslot->seqAboveWt();
+    float seq_below_wt = cslot->seqBelowWt();
+    float seq_valign_wt = cslot->seqValignWt();
+
+    // if isAfter, invert orderFlags for diagonal orders.
+    if (isAfter)
+    {
+        // invert appropriate bits
+        orderFlags ^= (sameClass ? 0x3F : 0x3);
+        // consider 2 bits at a time, non overlapping. If both bits set, clear them
+        orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3);
+    }
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        dbgout->setenv(0, slot);
+#endif
+
+    // Process main bounding octabox.
+    for (int i = 0; i < 4; ++i)
+    {
+        switch (i) {
+            case 0 :	// x direction
+                vmin = std::max(std::max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss);
+                vmax = std::min(std::min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss);
+                otmin = tbb.yi + ty;
+                otmax = tbb.ya + ty;
+                omin = bb.yi + sy;
+                omax = bb.ya + sy;
+                torg = _currOffset.x;
+                cmin = _limit.bl.x + torg;
+                cmax = _limit.tr.x - tbb.xi + tbb.xa + torg;
+                break;
+            case 1 :	// y direction
+                vmin = std::max(std::max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss);
+                vmax = std::min(std::min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss);
+                otmin = tbb.xi + tx;
+                otmax = tbb.xa + tx;
+                omin = bb.xi + sx;
+                omax = bb.xa + sx;
+                torg = _currOffset.y;
+                cmin = _limit.bl.y + torg;
+                cmax = _limit.tr.y - tbb.yi + tbb.ya + torg;
+                break;
+            case 2 :    // sum - moving along the positively-sloped vector, so the boundaries are the
+                        // negatively-sloped boundaries.
+                vmin = std::max(std::max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td);
+                vmax = std::min(std::min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td);
+                otmin = tsb.di + td;
+                otmax = tsb.da + td;
+                omin = sb.di + sd;
+                omax = sb.da + sd;
+                torg = _currOffset.x + _currOffset.y;
+                cmin = _limit.bl.x + _limit.bl.y + torg;
+                cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg;
+                break;
+            case 3 :    // diff - moving along the negatively-sloped vector, so the boundaries are the
+                        // positively-sloped boundaries.
+                vmin = std::max(std::max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts);
+                vmax = std::min(std::min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts);
+                otmin = tsb.si + ts;
+                otmax = tsb.sa + ts;
+                omin = sb.si + ss;
+                omax = sb.sa + ss;
+                torg = _currOffset.x - _currOffset.y;
+                cmin = _limit.bl.x - _limit.tr.y + torg;
+                cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg;
+                break;
+            default :
+                continue;
+        }
+        
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            dbgout->setenv(1, reinterpret_cast<void *>(-1));
+#define DBGTAG(x) if (dbgout) dbgout->setenv(1, reinterpret_cast<void *>(-x));
+#else
+#define DBGTAG(x)
+#endif
+
+        if (orderFlags)
+        {
+            Position org(tx, ty);
+            float xminf = _limit.bl.x + _currOffset.x + tbb.xi;
+            float xpinf = _limit.tr.x + _currOffset.x + tbb.xa;
+            float ypinf = _limit.tr.y + _currOffset.y + tbb.ya;
+            float yminf = _limit.bl.y + _currOffset.y + tbb.yi;
+            switch (orderFlags) {
+                case SlotCollision::SEQ_ORDER_RIGHTUP :
+                {
+                    float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx;
+                    float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi);
+                    float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
+                    
+                    // DBGTAG(1x) means the regions are up and right
+                    // region 1
+                    DBGTAG(11)
+                    addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)),
+                                    tbb, tsb, org, 0, seq_above_wt, true, i);
+                    // region 2
+                    DBGTAG(12)
+                    removeBox(Rect(Position(xminf, yminf), Position(r3Xedge, r2Yedge)), tbb, tsb, org, i);
+                    // region 3, which end is zero is irrelevant since m weight is 0
+                    DBGTAG(13)
+                    addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())),
+                                    tbb, tsb, org, seq_below_wt, 0, true, i);
+                    // region 4
+                    DBGTAG(14)
+                    addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())),
+                                    tbb, tsb, org, 0, seq_valign_wt, true, i);
+                    // region 5
+                    DBGTAG(15)
+                    addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)),
+                                    tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
+                    break;
+                }
+                case SlotCollision::SEQ_ORDER_LEFTDOWN :
+                {
+                    float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx;
+                    float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi);
+                    float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
+                    // DBGTAG(2x) means the regions are up and right
+                    // region 1
+                    DBGTAG(21)
+                    addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)),
+                                    tbb, tsb, org, 0, seq_above_wt, false, i);
+                    // region 2
+                    DBGTAG(22)
+                    removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i);
+                    // region 3
+                    DBGTAG(23)
+                    addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)),
+                                    tbb, tsb, org, seq_below_wt, 0, false, i);
+                    // region 4
+                    DBGTAG(24)
+                    addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())),
+                                    tbb, tsb, org, 0, seq_valign_wt, true, i);
+                    // region 5
+                    DBGTAG(25)
+                    addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()),
+                                    Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
+                    break;
+                }
+                case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above
+                    DBGTAG(31);
+                    removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya), 
+                                    Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i);
+                    break;
+                case SlotCollision::SEQ_ORDER_NOBELOW :	// enforce neighboring glyph being below
+                    DBGTAG(32);
+                    removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf),
+                                    Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i);
+                    break;
+                case SlotCollision::SEQ_ORDER_NOLEFT :  // enforce neighboring glyph being to the left
+                    DBGTAG(33)
+                    removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy),
+                                    Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
+                    break;
+                case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right
+                    DBGTAG(34)
+                    removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy),
+                                    Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
+                    break;
+                default :
+                    break;
+            }
+        }
+
+        if (vmax < cmin - _margin || vmin > cmax + _margin || omax < otmin - _margin || omin > otmax + _margin)
+            continue;
+
+        // Process sub-boxes that are defined for this glyph.
+        // We only need to do this if there was in fact a collision with the main octabox.
+        uint8 numsub = gc.numSubBounds(gid);
+        if (numsub > 0)
+        {
+            bool anyhits = false;
+            for (int j = 0; j < numsub; ++j)
+            {
+                const BBox &sbb = gc.getSubBoundingBBox(gid, j);
+                const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j);
+                switch (i) {
+                    case 0 :    // x
+                        vmin = std::max(std::max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty);
+                        vmax = std::min(std::min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty);
+                        omin = sbb.yi + sy;
+                        omax = sbb.ya + sy;
+                        break;
+                    case 1 :    // y
+                        vmin = std::max(std::max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx);
+                        vmax = std::min(std::min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx);
+                        omin = sbb.xi + sx;
+                        omax = sbb.xa + sx;
+                        break;
+                    case 2 :    // sum
+                        vmin = std::max(std::max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td);
+                        vmax = std::min(std::min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td);
+                        omin = ssb.di + sd;
+                        omax = ssb.da + sd;
+                        break;
+                    case 3 :    // diff
+                        vmin = std::max(std::max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts);
+                        vmax = std::min(std::min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts);
+                        omin = ssb.si + ss;
+                        omax = ssb.sa + ss;
+                        break;
+                }
+                if (vmax < cmin - _margin || vmin > cmax + _margin || omax < otmin - _margin || omin > otmax + _margin)
+                    continue;
+
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    dbgout->setenv(1, reinterpret_cast<void *>(j));
+#endif
+                if (omin > otmax)
+                    _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0,
+                                            sqr(_margin - omin + otmax) * _marginWt, false);
+                else if (omax < otmin)
+                    _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);
+                anyhits = true;
+            }
+            if (anyhits)
+                isCol = true;
+        }
+        else // no sub-boxes
+        {
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    dbgout->setenv(1, reinterpret_cast<void *>(-1));
+#endif
+            isCol = true;
+            if (omin > otmax)
+                _ranges[i].weightedAxis(i, vmin - _margin, vmax + _margin, 0, 0, 0, 0, 0,
+                                        sqr(_margin - omin + otmax) * _marginWt, false);
+            else if (omax < otmin)
+                _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)
+    {
+        // 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);
+    }
+    hasCol |= isCol;
+    return res;
+    
+}   // end of ShiftCollider::mergeSlot
+
+
+// Figure out where to move the target glyph to, and return the amount to shift by.
+Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout)
+{
+    float tbase;
+    float totalCost = (float)(std::numeric_limits<float>::max() / 2);
+    Position resultPos = Position(0, 0);
+#if !defined GRAPHITE2_NTRACING
+	int bestAxis = -1;
+    if (dbgout)
+    {
+		outputJsonDbgStartSlot(dbgout, seg);
+        *dbgout << "vectors" << json::array;
+    }
+#endif
+    isCol = true;
+    for (int i = 0; i < 4; ++i)
+    {
+        float bestCost = -1;
+        float bestPos;
+        // Calculate the margin depending on whether we are moving diagonally or not:
+        switch (i) {
+            case 0 :	// x direction
+                tbase = _currOffset.x;
+                break;
+            case 1 :	// y direction
+                tbase = _currOffset.y;
+                break;
+            case 2 :	// sum (negatively-sloped diagonals)
+                tbase = _currOffset.x + _currOffset.y;
+                break;
+            case 3 :	// diff (positively-sloped diagonals)
+                tbase = _currOffset.x - _currOffset.y;
+                break;
+        }
+        Position testp;
+        bestPos = _ranges[i].closest(0, bestCost) - tbase;     // Get the best relative position
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            outputJsonDbgOneVector(dbgout, seg, i, tbase, bestCost, bestPos) ;
+#endif
+        if (bestCost >= 0.0f)
+        {
+            isCol = false;
+            switch (i) {
+                case 0 : testp = Position(bestPos, _currShift.y); break;
+                case 1 : testp = Position(_currShift.x, bestPos); break;
+                case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break;
+                case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break;
+            }
+            if (bestCost < totalCost - 0.01f)
+            {
+                totalCost = bestCost;
+                resultPos = testp;
+#if !defined GRAPHITE2_NTRACING
+                bestAxis = i;
+#endif
+            }
+        }
+    }  // end of loop over 4 directions
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        outputJsonDbgEndSlot(dbgout, resultPos, bestAxis, isCol);
+#endif
+
+    return resultPos;
+
+}   // end of ShiftCollider::resolve
+
+
+#if !defined GRAPHITE2_NTRACING
+
+void ShiftCollider::outputJsonDbg(json * const dbgout, Segment *seg, int axis)
+{
+    int axisMax = axis;
+    if (axis < 0) // output all axes
+    {
+        *dbgout << "gid" << _target->gid()
+            << "limit" << _limit
+            << "target" << json::object
+                << "origin" << _target->origin()
+                << "margin" << _margin
+                << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
+                << "slantbox" << seg->getFace()->glyphs().slant(_target->gid())
+                << json::close; // target object
+        *dbgout << "ranges" << json::array;
+        axis = 0;
+        axisMax = 3;
+    }
+    for (int iAxis = axis; iAxis <= axisMax; ++iAxis)
+    {
+        *dbgout << json::flat << json::array << _ranges[iAxis].position();
+        for (Zones::const_iterator s = _ranges[iAxis].begin(), e = _ranges[iAxis].end(); s != e; ++s)
+            *dbgout << json::flat << json::array 
+                        << Position(s->x, s->xm) << s->sm << s->smx << s->c
+                    << json::close;
+        *dbgout << json::close;
+    }
+    if (axis < axisMax) // looped through the _ranges array for all axes
+        *dbgout << json::close; // ranges array
+}
+
+void ShiftCollider::outputJsonDbgStartSlot(json * const dbgout, Segment *seg)
+{
+        *dbgout << json::object // slot - not closed till the end of the caller method
+                << "slot" << objectid(dslot(seg, _target))
+				<< "gid" << _target->gid()
+                << "limit" << _limit
+                << "target" << json::object
+                    << "origin" << _origin
+                    << "currShift" << _currShift
+                    << "currOffset" << seg->collisionInfo(_target)->offset()
+                    << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
+                    << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
+                    << "fix" << "shift";
+        *dbgout     << json::close; // target object
+}
+
+void ShiftCollider::outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout,
+	 Position resultPos, int bestAxis, bool isCol)
+{
+    *dbgout << json::close // vectors array
+    << "result" << resultPos
+	//<< "scraping" << _scraping[bestAxis]
+	<< "bestAxis" << bestAxis
+    << "stillBad" << isCol
+    << json::close; // slot object
+}
+
+void ShiftCollider::outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis,
+	float tleft, float bestCost, float bestVal) 
+{
+	const char * label;
+	switch (axis)
+	{
+		case 0:	label = "x";			break;
+		case 1:	label = "y";			break;
+		case 2:	label = "sum (NE-SW)";	break;
+		case 3:	label = "diff (NW-SE)";	break;
+		default: label = "???";			break;
+	}
+
+	*dbgout << json::object // vector
+		<< "direction" << label
+		<< "targetMin" << tleft;
+            
+	outputJsonDbgRemovals(dbgout, axis, seg);
+    	
+    *dbgout << "ranges";
+    outputJsonDbg(dbgout, seg, axis);
+
+    *dbgout << "bestCost" << bestCost
+        << "bestVal" << bestVal + tleft
+        << json::close; // vectors object
+}
+
+void ShiftCollider::outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg)
+{
+    *dbgout << "removals" << json::array;
+    _ranges[axis].jsonDbgOut(seg);
+    *dbgout << json::close; // removals array
+}
+
+#endif // !defined GRAPHITE2_NTRACING
+
+
+////    KERN-COLLIDER    ////
+
+inline
+static float localmax (float al, float au, float bl, float bu, float x)
+{
+    if (al < bl)
+    { if (au < bu) return au < x ? au : x; }
+    else if (au > bu) return bl < x ? bl : x;
+    return x;
+}
+
+inline
+static float localmin(float al, float au, float bl, float bu, float x)
+{
+    if (bl > al)
+    { if (bu > au) return bl > x ? bl : x; }
+    else if (au > bu) return al > x ? al : x;
+    return x;        
+}
+
+// Return the given edge of the glyph at height y, taking any slant box into account.
+static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, bool isRight)
+{
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    unsigned short gid = s->gid();
+    float sx = s->origin().x + shift.x;
+    float sy = s->origin().y + shift.y;
+    uint8 numsub = gc.numSubBounds(gid);
+    float res = isRight ? (float)-1e38 : (float)1e38;
+
+    if (numsub > 0)
+    {
+        for (int i = 0; i < numsub; ++i)
+        {
+            const BBox &sbb = gc.getSubBoundingBBox(gid, i);
+            const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i);
+            if (sy + sbb.yi > y + width / 2 || sy + sbb.ya < y - width / 2)
+                continue;
+            if (isRight)
+            {
+                float x = sx + sbb.xa;
+                if (x > res)
+                {
+                    float td = sx - sy + ssb.da + y;
+                    float ts = sx + sy + ssb.sa - y;
+                    x = localmax(td - width / 2, td + width / 2,  ts - width / 2, ts + width / 2, x);
+                    if (x > res)
+                        res = x;
+                }
+            }
+            else
+            {
+                float x = sx + sbb.xi;
+                if (x < res)
+                {
+                    float td = sx - sy + ssb.di + y;
+                    float ts = sx + sy + ssb.si - y;
+                    x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
+                    if (x < res)
+                        res = x;
+                }
+            }
+        }
+    }
+    else
+    {
+        const BBox &bb = gc.getBoundingBBox(gid);
+        const SlantBox &sb = gc.getBoundingSlantBox(gid);
+        float td = sx - sy + y;
+        float ts = sx + sy - y;
+        if (isRight)
+            res = localmax(td + sb.da - width / 2, td + sb.da + width / 2, ts + sb.sa - width / 2, ts + sb.sa + width / 2, sx + bb.xa);
+        else
+            res = localmin(td + sb.di - width / 2, td + sb.di + width / 2, ts + sb.si - width / 2, ts + sb.si + width / 2, sx + bb.xi);
+    }
+    return res;
+}
+
+
+bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin,
+    const Position &currShift, const Position &offsetPrev, int dir,
+    float ymin, float ymax, GR_MAYBE_UNUSED json * const dbgout)
+{
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    const Slot *base = aSlot;
+    // const Slot *last = aSlot;
+    const Slot *s;
+    int numSlices;
+    while (base->attachedTo())
+        base = base->attachedTo();
+    if (margin < 10) margin = 10;
+
+    _limit = limit;
+    _offsetPrev = offsetPrev; // kern from a previous pass
+    
+    // Calculate the height of the glyph and how many horizontal slices to use.
+    if (_maxy >= 1e37f)
+    {
+        _maxy = ymax;
+        _miny = ymin;
+        _sliceWidth = margin / 1.5f;
+        numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f);  // +2 helps with rounding errors
+        _edges.clear();
+        _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f);
+        _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f;
+    }
+    else if (_maxy != ymax || _miny != ymin)
+    {
+        if (_miny != ymin)
+        {
+            numSlices = int((ymin - _miny) / _sliceWidth - 1);
+            _miny += numSlices * _sliceWidth;
+            if (numSlices < 0)
+                _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f);
+            else if ((unsigned)numSlices < _edges.size())    // this shouldn't fire since we always grow the range
+            {
+                Vector<float>::iterator e = _edges.begin();
+                while (numSlices--)
+                    ++e;
+                _edges.erase(_edges.begin(), e);
+            }
+        }
+        if (_maxy != ymax)
+        {
+            numSlices = int((ymax - _miny) / _sliceWidth + 1);
+            _maxy = numSlices * _sliceWidth + _miny;
+            if (numSlices > (int)_edges.size())
+                _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f);
+            else if (numSlices < (int)_edges.size())   // this shouldn't fire since we always grow the range
+            {
+                while ((int)_edges.size() > numSlices)
+                    _edges.pop_back();
+            }
+        }
+    }
+    numSlices = _edges.size();
+
+#if !defined GRAPHITE2_NTRACING
+    // Debugging
+    _seg = seg;
+    _slotNear.clear();
+    _slotNear.insert(_slotNear.begin(), numSlices, NULL);
+    _nearEdges.clear();
+    _nearEdges.insert(_nearEdges.begin(), numSlices, (dir & 1) ? -1e38f : +1e38f);
+#endif
+    
+    // Determine the trailing edge of each slice (ie, left edge for a RTL glyph).
+    for (s = base; s; s = s->nextInCluster(s))
+    {
+        SlotCollision *c = seg->collisionInfo(s);
+        if (!gc.check(s->gid()))
+            return false;
+        const BBox &bs = gc.getBoundingBBox(s->gid());
+        float x = s->origin().x + c->shift().x + ((dir & 1) ? bs.xi : bs.xa);
+        // Loop over slices.
+        // Note smin might not be zero if glyph s is not at the bottom of the cluster; similarly for smax.
+        float toffset = c->shift().y - _miny + 1 + s->origin().y;
+        int smin = std::max(0, int((bs.yi + toffset) / _sliceWidth));
+        int smax = std::min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1));
+        for (int i = smin; i <= smax; ++i)
+        {
+            float t;
+            float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice
+            if ((dir & 1) && x < _edges[i])
+            {
+                t = get_edge(seg, s, currShift, y, _sliceWidth, false);
+                if (t < _edges[i])
+                {
+                    _edges[i] = t;
+                    if (t < _xbound)
+                        _xbound = t;
+                }
+            }
+            else if (!(dir & 1) && x > _edges[i])
+            {
+                t = get_edge(seg, s, currShift, y, _sliceWidth, true);
+                if (t > _edges[i])
+                {
+                    _edges[i] = t;
+                    if (t > _xbound)
+                        _xbound = t;
+                }
+            }
+        }
+    }
+    _mingap = (float)1e38;
+    _target = aSlot;
+    _margin = margin;
+    _currShift = currShift;
+    return true;
+}   // end of KernCollider::initSlot
+
+
+// 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;
+    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;
+    int smin = std::max(0, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1));
+    int smax = std::min((int)_edges.size() - 1, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1));
+    bool collides = false;
+
+    for (int i = smin; i <= smax; ++i)
+    {
+        float t;
+        float y = (float)(_miny - 1 + (i + .5f) * _sliceWidth);  // vertical center of slice
+        if (x * rtl > _edges[i] * rtl - _mingap - currSpace)
+        {
+            // 2 * currSpace to account for the space that is already separating them and the space we want to add
+            float m = get_edge(seg, slot, currShift, y, _sliceWidth, rtl > 0) + 2 * rtl * currSpace;
+            t = rtl * (_edges[i] - m);
+            // Check slices above and below (if any).
+            if (i < (int)_edges.size() - 1) t = std::min(t, rtl * (_edges[i+1] - m));
+            if (i > 0) t = std::min(t, rtl * (_edges[i-1] - m));
+            // _mingap is positive to shrink
+            if (t < _mingap)
+            {
+                _mingap = t;
+                collides = true;
+            }
+#if !defined GRAPHITE2_NTRACING
+            // Debugging - remember the closest neighboring edge for this slice.
+            if (rtl * m > rtl * _nearEdges[i])
+            {
+                _slotNear[i] = slot;
+                _nearEdges[i] = m;
+            }
+#endif
+        }
+    }
+    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)
+    {
+        *dbgout << json::object // slot
+                << "slot" << objectid(dslot(seg, _target))
+				<< "gid" << _target->gid()
+                << "margin" << _margin
+                << "limit" << _limit
+                << "target" << json::object
+                    << "origin" << _target->origin()
+                    //<< "currShift" << _currShift
+                    << "offsetPrev" << _offsetPrev
+                    << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
+                    << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
+                    << "fix" << "kern"
+                    << "slices" << _edges.size()
+                    << "sliceWidth" << _sliceWidth
+                    << json::close; // target object
+        
+        *dbgout << "slices" << json::array;
+        for (int is = 0; is < (int)_edges.size(); is++)
+        {
+            *dbgout << json::flat << json::object 
+                << "i" << is 
+                << "targetEdge" << _edges[is]
+                << "neighbor" << objectid(dslot(seg, _slotNear[is]))
+                << "nearEdge" << _nearEdges[is] 
+                << json::close;
+        }
+        *dbgout << json::close; // slices array
+            
+        *dbgout
+            << "xbound" << _xbound
+            << "minGap" << _mingap
+            << "needed" << resultNeeded
+            << "result" << result
+            << "stillBad" << (result != resultNeeded)
+            << json::close; // slot object
+    }
+#endif
+
+    return Position(result, 0.);
+    
+}   // end of KernCollider::resolve
+
+void KernCollider::shift(const Position &mv, int dir)
+{
+    for (Vector<float>::iterator e = _edges.begin(); e != _edges.end(); ++e)
+        *e += mv.x;
+    _xbound += (1 - 2 * (dir & 1)) * mv.x;
+}
+
+////    SLOT-COLLISION    ////
+
+// Initialize the collision attributes for the given slot.
+SlotCollision::SlotCollision(Segment *seg, Slot *slot)
+{
+    initFromSlot(seg, slot);
+}
+
+void SlotCollision::initFromSlot(Segment *seg, Slot *slot)
+{
+    // Initialize slot attributes from glyph attributes.
+	// The order here must match the order in the grcompiler code, 
+	// GrcSymbolTable::AssignInternalGlyphAttrIDs.
+    uint16 gid = slot->gid();
+    uint16 aCol = seg->silf()->aCollision(); // flags attr ID
+    const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid);
+    if (!glyphFace)
+        return;
+    const sparse &p = glyphFace->attrs();
+    _flags = p[aCol];
+    _limit = Rect(Position(p[aCol+1], p[aCol+2]),
+                  Position(p[aCol+3], p[aCol+4]));
+    _margin = p[aCol+5];
+    _marginWt = p[aCol+6];
+
+    _seqClass = p[aCol+7];
+	_seqProxClass = p[aCol+8];
+    _seqOrder = p[aCol+9];
+	_seqAboveXoff = p[aCol+10];
+	_seqAboveWt = p[aCol+11];
+	_seqBelowXlim = p[aCol+12];
+	_seqBelowWt = p[aCol+13];
+	_seqValignHt = p[aCol+14];
+	_seqValignWt = p[aCol+15];    
+
+    // These attributes do not have corresponding glyph attribute:
+    _exclGlyph = 0;
+    _exclOffset = Position(0, 0);
+}
+
+float SlotCollision::getKern(int dir) const
+{
+    if ((_flags & SlotCollision::COLL_KERN) != 0)
+        return float(_shift.x * ((dir & 1) ? -1 : 1));
+    else
+    	return 0;
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/Decompressor.cpp
@@ -0,0 +1,102 @@
+/*  Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
+    Copyright (c) 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.
+*/
+#include <cassert>
+
+#include "inc/Decompressor.h"
+#include "inc/Shrinker.h"
+
+using namespace shrinker;
+
+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))
+    {
+        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)
+{
+    u8 const flag = *src++;
+    
+    literal_len = read_literal<7>(src, end, flag >> 5);
+    match_len = read_literal<15>(src, end, flag & MATCH_LEN);
+    
+    match_dist = *src++;
+    if (flag & LONG_DIST) 
+        match_dist |= ((*src++) << 8);
+    
+    return match_dist != 0xffff;
+}
+
+}
+
+int shrinker::decompress(void const *in, size_t in_size, void *out, size_t out_size)
+{
+    u8 const *       src     = static_cast<u8 const *>(in),
+             * 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))
+    {
+        // 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, 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);
+    }
+    
+    if (unlikely(dst + literal_len > dst_end)) return -1;
+    dst = memcpy_nooverlap_surpass(dst, src, literal_len);
+    
+    return dst - (u8*)out;
+}
+
--- a/gfx/graphite2/src/Face.cpp
+++ b/gfx/graphite2/src/Face.cpp
@@ -23,28 +23,39 @@ 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.
 */
 #include <cstring>
 #include "graphite2/Segment.h"
 #include "inc/CmapCache.h"
 #include "inc/debug.h"
+#include "inc/Decompressor.h"
 #include "inc/Endian.h"
 #include "inc/Face.h"
 #include "inc/FileFace.h"
 #include "inc/GlyphFace.h"
 #include "inc/json.h"
 #include "inc/SegCacheStore.h"
 #include "inc/Segment.h"
 #include "inc/NameTable.h"
 #include "inc/Error.h"
 
 using namespace graphite2;
 
+namespace
+{
+enum compression
+{
+    NONE,
+    SHRINKER
+};
+
+}
+
 Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
 : m_appFaceHandle(appFaceHandle),
   m_pFileFace(NULL),
   m_pGlyphFaceCache(NULL),
   m_cmap(NULL),
   m_pNames(NULL),
   m_logger(NULL),
   m_error(0), m_errcntxt(0),
@@ -79,29 +90,31 @@ float Face::default_glyph_advance(const 
 
 bool Face::readGlyphs(uint32 faceOptions)
 {
     Error e;
 #ifdef GRAPHITE2_TELEMETRY
     telemetry::category _glyph_cat(tele.glyph);
 #endif
     error_context(EC_READGLYPHS);
+    m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
+
+    if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
+        || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
+        || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
+    {
+        return error(e);
+    }
+
     if (faceOptions & gr_face_cacheCmap)
         m_cmap = new CachedCmap(*this);
     else
         m_cmap = new DirectCmap(*this);
-
-    m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
-    if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
-        || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
-        || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM)
-        || e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
-    {
+    if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
         return error(e);
-    }
 
     if (faceOptions & gr_face_preloadGlyphs)
         nameTable();        // preload the name table along with the glyphs.
 
     return true;
 }
 
 bool Face::readGraphite(const Table & silf)
@@ -114,20 +127,22 @@ bool Face::readGraphite(const Table & si
     const byte * p = silf;
     if (e.test(!p, E_NOSILF)) 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
 
     bool havePasses = false;
     m_silfs = new Silf[m_numSilf];
+    if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
     for (int i = 0; i < m_numSilf; i++)
     {
         error_context(EC_ASILF + (i << 8));
         const uint32 offset = be::read<uint32>(p),
                      next   = i == m_numSilf - 1 ? silf.size() : be::peek<uint32>(p);
         if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
             return error(e);
 
@@ -153,19 +168,24 @@ bool Face::runGraphite(Segment *seg, con
     if (dbgout)
     {
         *dbgout << json::object
                     << "id"         << objectid(seg)
                     << "passes"     << json::array;
     }
 #endif
 
-    bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass(), true);
+    bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
     if (res)
-        res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
+    {
+        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)
 {
         *dbgout             << json::item
                             << json::close // Close up the passes array
                 << "output" << json::array;
         for(Slot * s = seg->first(); s; s = s->next())
@@ -240,30 +260,84 @@ NameTable * Face::nameTable() const
 uint16 Face::languageForLocale(const char * locale) const
 {
     nameTable();
     if (m_pNames)
         return m_pNames->getLanguageId(locale);
     return 0;
 }
 
-Face::Table::Table(const Face & face, const Tag n) throw()
-: _f(&face)
+
+
+Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
+: _f(&face), _compressed(false)
 {
     size_t sz = 0;
-    _p = reinterpret_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz));
+    _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();
 }
 
 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;
+    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:
+    {
+        uncompressed_size  = hdr & 0x07ffffff;
+        uncompressed_table = gralloc<byte>(uncompressed_size);
+        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);
+        break;
+    }
+    default:
+        e.error(E_BADSCHEME);
+    };
+
+    // Check the uncompressed version number against the original.
+    if (!e)
+        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();
+
+    if (e)
+    {
+        free(uncompressed_table);
+        uncompressed_table = 0;
+        uncompressed_size  = 0;
+    }
+
+    _p = uncompressed_table;
+    _sz = uncompressed_size + sizeof(uint32);
+    _compressed = true;
+
+    return e;
+}
--- a/gfx/graphite2/src/FeatureMap.cpp
+++ b/gfx/graphite2/src/FeatureMap.cpp
@@ -160,26 +160,26 @@ bool FeatureMap::readFeats(const Face & 
             maxVal = 0xffffffff;
             defVals[i] = 0;
         }
 
         ::new (m_feats + i) FeatureRef (face, bits, maxVal,
                                        label, uiName, flags,
                                        uiSet, num_settings);
     }
-    m_defaultFeatures = new Features(bits/(sizeof(uint32)*8) + 1, *this);
+    new (&m_defaultFeatures) Features(bits/(sizeof(uint32)*8) + 1, *this);
     m_pNamedFeats = new NameAndFeatureRef[m_numFeats];
-    if (!m_defaultFeatures || !m_pNamedFeats)
+    if (!m_pNamedFeats)
     {
         free(defVals);
         return false;
     }
     for (int i = 0; i < m_numFeats; ++i)
     {
-        m_feats[i].applyValToFeature(defVals[i], *m_defaultFeatures);
+        m_feats[i].applyValToFeature(defVals[i], m_defaultFeatures);
         m_pNamedFeats[i] = m_feats+i;
     }
     
     free(defVals);
 
     qsort(m_pNamedFeats, m_numFeats, sizeof(NameAndFeatureRef), &cmpNameAndFeatures);
 
     return true;
@@ -209,17 +209,17 @@ bool SillMap::readSill(const Face & face
     if (sill.size() < m_numLanguages * 8U + 12) return false;
 
     for (int i = 0; i < m_numLanguages; i++)
     {
         uint32 langid = be::read<uint32>(p);
         uint16 numSettings = be::read<uint16>(p);
         uint16 offset = be::read<uint16>(p);
         if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false;
-        Features* feats = new Features(*m_FeatureMap.m_defaultFeatures);
+        Features* feats = new Features(m_FeatureMap.m_defaultFeatures);
         if (!feats) return false;
         const byte *pLSet = sill + offset;
 
         // Apply langauge specific settings
         for (int j = 0; j < numSettings; j++)
         {
             uint32 name = be::read<uint32>(pLSet);
             uint16 val = be::read<uint16>(pLSet);
@@ -245,17 +245,17 @@ Features* SillMap::cloneFeatures(uint32 
         // the number of languages in a font is usually small e.g. 8 in Doulos
         // so this loop is not very expensive
         for (uint16 i = 0; i < m_numLanguages; i++)
         {
             if (m_langFeats[i].m_lang == langname)
                 return new Features(*m_langFeats[i].m_pFeatures);
         }
     }
-    return new Features (*m_FeatureMap.m_defaultFeatures);
+    return new Features (m_FeatureMap.m_defaultFeatures);
 }
 
 
 
 const FeatureRef *FeatureMap::findFeatureRef(uint32 name) const
 {
     NameAndFeatureRef *it;
     
--- a/gfx/graphite2/src/GlyphCache.cpp
+++ b/gfx/graphite2/src/GlyphCache.cpp
@@ -26,16 +26,17 @@ of the License or (at your option) any l
 */
 #include "graphite2/Font.h"
 
 #include "inc/Main.h"
 #include "inc/Face.h"     //for the tags
 #include "inc/GlyphCache.h"
 #include "inc/GlyphFace.h"
 #include "inc/Endian.h"
+#include "inc/bits.h"
 
 using namespace graphite2;
 
 namespace
 {
     // Iterator over version 1 or 2 glat entries which consist of a series of
     //    +-+-+-+-+-+-+-+-+-+-+                +-+-+-+-+-+-+-+-+-+-+-+-+
     // v1 |k|n|v1 |v2 |...|vN |     or    v2   | k | n |v1 |v2 |...|vN |
@@ -65,90 +66,117 @@ namespace
         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;
-        ptrdiff_t        _n;
+        size_t        _n;
     };
 
     typedef _glat_iterator<uint8>   glat_iterator;
     typedef _glat_iterator<uint16>  glat2_iterator;
 }
 
+const Rect GlyphCache::nullRect = Rect();
 
 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();
     unsigned short int num_glyphs() const throw();
     unsigned short int num_attrs() const throw();
+    bool has_boxes() const throw();
 
-    const GlyphFace * read_glyph(unsigned short gid, GlyphFace &) const throw();
+    const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw();
+    GlyphBox * read_box(uint16 gid, GlyphBox *curr, const GlyphFace & face) const throw();
 
     CLASS_NEW_DELETE;
 private:
     Face::Table _head,
                 _hhea,
                 _hmtx,
                 _glyf,
                 _loca,
                 m_pGlat,
                 m_pGloc;
 
     bool            _long_fmt;
+    bool            _has_boxes;
     unsigned short  _num_glyphs_graphics,        //i.e. boundary box and advance
                     _num_glyphs_attributes,
                     _num_attrs;                    // number of glyph attributes per glyph
 };
 
 
 
 GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
 : _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))),
   _glyphs(_glyph_loader && *_glyph_loader ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
+  _boxes(_glyph_loader && _glyph_loader->has_boxes() ? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),
   _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0),
   _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0),
   _upem(_glyphs ? _glyph_loader->units_per_em() : 0)
 {
     if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs)
     {
+        int numsubs = 0;
         GlyphFace * const glyphs = new GlyphFace [_num_glyphs];
         if (!glyphs)
             return;
 
         // The 0 glyph is definately required.
-        _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0]);
+        _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs);
 
         // glyphs[0] has the same address as the glyphs array just allocated,
         //  thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points
         //  to the entire array.
         const GlyphFace * loaded = _glyphs[0];
         for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid)
-            _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid]);
+            _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs);
 
         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 * 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)
+            {
+                free(boxes);
+                _boxes[0] = 0;
+            }
+        }
         delete _glyph_loader;
         _glyph_loader = 0;
     }
 
     if (_glyphs && glyph(0) == 0)
     {
         free(_glyphs);
         _glyphs = 0;
+        if (_boxes)
+        {
+            free(_boxes);
+            _boxes = 0;
+        }
         _num_glyphs = _num_attrs = _upem = 0;
     }
 }
 
 
 GlyphCache::~GlyphCache()
 {
     if (_glyphs)
@@ -158,91 +186,123 @@ GlyphCache::~GlyphCache()
             const GlyphFace *  * g = _glyphs;
             for(unsigned short n = _num_glyphs; n; --n, ++g)
                 delete *g;
         }
         else
             delete [] _glyphs[0];
         free(_glyphs);
     }
+    if (_boxes)
+    {
+        if (_glyph_loader)
+        {
+            GlyphBox *  * g = _boxes;
+            for (uint16 n = _num_glyphs; n; --n, ++g)
+                free(*g);
+        }
+        else
+            free(_boxes[0]);
+        free(_boxes);
+    }
     delete _glyph_loader;
 }
 
 const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const      //result may be changed by subsequent call with a different glyphid
 { 
     const GlyphFace * & p = _glyphs[glyphid];
     if (p == 0 && _glyph_loader)
     {
+        int numsubs = 0;
         GlyphFace * g = new GlyphFace();
-        if (g)  p = _glyph_loader->read_glyph(glyphid, *g);
+        if (g)  p = _glyph_loader->read_glyph(glyphid, *g, &numsubs);
         if (!p)
         {
             delete g;
             return *_glyphs;
         }
+        if (_boxes)
+        {
+            _boxes[glyphid] = (GlyphBox *)gralloc<char>(sizeof(GlyphBox) + 8 * numsubs * sizeof(float));
+            if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid]))
+            {
+                free(_boxes[glyphid]);
+                _boxes[glyphid] = 0;
+            }
+        }
     }
     return p;
 }
 
 
 
 GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
 : _head(face, Tag::head),
   _hhea(face, Tag::hhea),
   _hmtx(face, Tag::hmtx),
   _glyf(face, Tag::glyf),
   _loca(face, Tag::loca),
   _long_fmt(false),
+  _has_boxes(false),
   _num_glyphs_graphics(0),
   _num_glyphs_attributes(0),
   _num_attrs(0)
 {
     if (!operator bool())
         return;
 
     const Face::Table maxp = Face::Table(face, Tag::maxp);
     if (!maxp) { _head = Face::Table(); return; }
 
     _num_glyphs_graphics = TtfUtil::GlyphCount(maxp);
     // This will fail if the number of glyphs is wildly out of range.
-    if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1))
+    if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2))
     {
         _head = Face::Table();
         return;
     }
 
     if (!dumb_font)
     {
-        if ((m_pGlat = Face::Table(face, Tag::Glat)) == NULL
+        if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL
             || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL
             || m_pGloc.size() < 6)
         {
             _head = Face::Table();
             return;
         }
         const byte    * p = m_pGloc;
-        const int       version = be::read<uint32>(p);
+        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()
                                    - (p - m_pGloc)
                                    - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
                                        / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
 
-        if (version != 0x00010000
+        if (version >= 0x00020000
             || _num_attrs == 0 || _num_attrs > 0x3000  // is this hard limit appropriate?
             || _num_glyphs_graphics > _num_glyphs_attributes)
         {
             _head = Face::Table();
             return;
         }
+
+        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);
     }
 }
 
 inline
 GlyphCache::Loader::operator bool () const throw()
 {
     return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca));
 }
@@ -260,17 +320,23 @@ unsigned short int GlyphCache::Loader::n
 }
 
 inline
 unsigned short int GlyphCache::Loader::num_attrs() const throw()
 {
     return _num_attrs;
 }
 
-const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph) const throw()
+inline
+bool GlyphCache::Loader::has_boxes () const throw()
+{
+    return _has_boxes;
+}
+
+const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw()
 {
     Rect        bbox;
     Position    advance;
 
     if (glyphid < _num_glyphs_graphics)
     {
         int nLsb;
         unsigned int nAdvWid;
@@ -307,35 +373,94 @@ const GlyphFace * GlyphCache::Loader::re
             glocs = be::read<uint16>(gloc);
             gloce = be::peek<uint16>(gloc);
         }
 
         if (glocs >= m_pGlat.size() || gloce > m_pGlat.size())
             return 0;
 
         const uint32 glat_version = be::peek<uint32>(m_pGlat);
+        if (glat_version == 0x00030000)
+        {
+            const byte * p = m_pGlat + glocs;
+            uint16 bmap = be::read<uint16>(p);
+            int num = bit_set_count((uint32)bmap);
+            if (numsubs) *numsubs += num;
+            glocs += 6 + 8 * num;
+            if (glocs > gloce)
+                return 0;
+        }
         if (glat_version < 0x00020000)
         {
             if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16)
                 || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16)))
-            {
-                return 0;
-            }
-
+                    return 0;
             new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce));
         }
         else
         {
-            if (gloce - glocs < 3*sizeof(uint16)
+            if (gloce - glocs < 3*sizeof(uint16)        // can a glyph have no attributes? why not?
                 || gloce - glocs > _num_attrs*3*sizeof(uint16))
-            {
-                return 0;
-            }
-
+                    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;
 }
+
+inline float scale_to(uint8 t, float zmin, float zmax)
+{
+    return (zmin + t * (zmax - zmin) / 255);
+}
+
+Rect readbox(Rect &b, uint8 zxmin, uint8 zymin, uint8 zxmax, uint8 zymax)
+{
+    return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)),
+                Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y)));
+}
+
+GlyphBox * GlyphCache::Loader::read_box(uint16 gid, GlyphBox *curr, const GlyphFace & glyph) const throw()
+{
+    if (gid >= _num_glyphs_attributes) return 0;
+
+    const byte * gloc = m_pGloc;
+    size_t      glocs = 0, gloce = 0;
+
+    be::skip<uint32>(gloc);
+    be::skip<uint16>(gloc,2);
+    if (_long_fmt)
+    {
+        be::skip<uint32>(gloc, gid);
+        glocs = be::read<uint32>(gloc);
+        gloce = be::peek<uint32>(gloc);
+    }
+    else
+    {
+        be::skip<uint16>(gloc, gid);
+        glocs = be::read<uint16>(gloc);
+        gloce = be::peek<uint16>(gloc);
+    }
+
+    if (glocs >= m_pGlat.size() || gloce > m_pGlat.size())
+        return 0;
+
+    const byte * p = m_pGlat + glocs;
+    uint16 bmap = be::read<uint16>(p);
+    int num = bit_set_count((uint32)bmap);
+
+    Rect bbox = glyph.theBBox();
+    Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y),
+                Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y));
+    Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]);
+    ::new (curr) GlyphBox(num, bmap, &diabound);
+    be::skip<uint8>(p, 4);
+
+    for (int i = 0; i < num * 2; ++i)
+    {
+        Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]);
+        curr->addSubBox(i >> 1, i & 1, &box);
+        be::skip<uint8>(p, 4);
+    } 
+    return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect));
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/Intervals.cpp
@@ -0,0 +1,292 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, 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.
+*/
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "inc/Intervals.h"
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/debug.h"
+#include "inc/bits.h"
+
+using namespace graphite2;
+
+#include <cmath>
+
+inline
+Zones::Exclusion  Zones::Exclusion::split_at(float p) {
+    Exclusion r(*this);
+    r.xm = x = p;
+    return r;
+}
+
+inline
+void Zones::Exclusion::left_trim(float p) {
+    x = p;
+}
+
+inline
+Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
+    c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
+    return *this;
+}
+
+inline
+uint8 Zones::Exclusion::outcode(float val) const {
+    float p = val;
+    return ((p >= xm) << 1) | (p < x);
+}
+
+void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
+    remove(xmin, xmax);
+    weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
+    weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
+}
+
+namespace
+{
+
+inline
+bool separated(float a, float b) {
+    return a != b;
+    //return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
+    //return std::fabs(a-b) > 0.5f;
+}
+
+}
+
+void Zones::insert(Exclusion e)
+{
+#if !defined GRAPHITE2_NTRACING
+    addDebug(&e);
+#endif
+    e.x = std::max(e.x, _pos);
+    e.xm = std::min(e.xm, _posm);
+    if (e.x >= e.xm) return;
+
+    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
+    {
+        const uint8 oca = e.outcode(i->x),
+                    ocb = e.outcode(i->xm);
+        if ((oca & ocb) != 0) continue;
+
+        switch (oca ^ ocb)  // What kind of overlap?
+        {
+        case 0:     // e completely covers i
+            // split e at i.x into e1,e2
+            // split e2 at i.mx into e2,e3
+            // drop e1 ,i+e2, e=e3
+            *i += e;
+            e.left_trim(i->xm);
+            break;
+        case 1:     // e overlaps on the rhs of i
+            // split i at e->x into i1,i2
+            // split e at i.mx into e1,e2
+            // trim i1, insert i2+e1, e=e2
+            if (!separated(i->xm, e.x)) break;
+            if (separated(i->x,e.x))   { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
+            *i += e;
+            e.left_trim(i->xm);
+            break;
+        case 2:     // e overlaps on the lhs of i
+            // split e at i->x into e1,e2
+            // split i at e.mx into i1,i2
+            // drop e1, insert e2+i1, trim i2
+            if (!separated(e.xm, i->x)) return;
+            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
+            *i += e;
+            return;
+        case 3:     // i completely covers e
+            // split i at e.x into i1,i2
+            // split i2 at e.mx into i2,i3
+            // insert i1, insert e+i2
+            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
+            i = _exclusions.insert(i, i->split_at(e.x));
+            *++i += e;
+            return;
+        }
+
+        ie = _exclusions.end();
+    }
+}
+
+
+void Zones::remove(float x, float xm)
+{
+#if !defined GRAPHITE2_NTRACING
+    removeDebug(x, xm);
+#endif
+    x = std::max(x, _pos);
+    xm = std::min(xm, _posm);
+    if (x >= xm) return;
+
+    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
+    {
+        const uint8 oca = i->outcode(x),
+                    ocb = i->outcode(xm);
+        if ((oca & ocb) != 0)   continue;
+
+        switch (oca ^ ocb)  // What kind of overlap?
+        {
+        case 0:     // i completely covers e
+            if (separated(i->x, x))  { i = _exclusions.insert(i,i->split_at(x)); ++i; }
+            // no break
+        case 1:     // i overlaps on the rhs of e
+            i->left_trim(xm);
+            return;
+        case 2:     // i overlaps on the lhs of e
+            i->xm = x;
+            if (separated(i->x, i->xm)) break;
+            // no break
+        case 3:     // e completely covers i
+            i = _exclusions.erase(i);
+            --i;
+            break;
+        }
+
+        ie = _exclusions.end();
+    }
+}
+
+
+Zones::const_iterator Zones::find_exclusion_under(float x) const
+{
+    int l = 0, h = _exclusions.size();
+
+    while (l < h)
+    {
+        int const p = (l+h) >> 1;
+        switch (_exclusions[p].outcode(x))
+        {
+        case 0 : return _exclusions.begin()+p;
+        case 1 : h = p; break;
+        case 2 : 
+        case 3 : l = p+1; break;
+        }
+    }
+
+    return _exclusions.begin()+l;
+}
+
+
+float Zones::closest(float origin, float & cost) const
+{
+    float best_c = std::numeric_limits<float>::max(),
+          best_x = 0;
+
+    const const_iterator start = find_exclusion_under(origin);
+
+    // Forward scan looking for lowest cost
+    for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
+        if (i->track_cost(best_c, best_x, origin)) break;
+
+    // Backward scan looking for lowest cost
+    //  We start from the exclusion to the immediate left of start since we've
+    //  already tested start with the right most scan above.
+    for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
+        if (i->track_cost(best_c, best_x, origin)) break;
+
+    cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
+    return best_x;
+}
+
+
+// Cost and test position functions
+
+bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
+    const float p = test_position(origin),
+                localc = cost(p - origin);
+    if (open && localc > best_cost) return true;
+
+    if (localc < best_cost)
+    {
+        best_cost = localc;
+        best_pos = p;
+    }
+    return false;
+}
+
+inline
+float Zones::Exclusion::cost(float p) const {
+    return (sm * p - 2 * smx) * p + c;
+}
+
+
+float Zones::Exclusion::test_position(float origin) const {
+    if (sm < 0)
+    {
+        // sigh, test both ends and perhaps the middle too!
+        float res = x;
+        float cl = cost(x);
+        if (x < origin && xm > origin)
+        {
+            float co = cost(origin);
+            if (co < cl)
+            {
+                cl = co;
+                res = origin;
+            }
+        }
+        float cr = cost(xm);
+        return cl > cr ? xm : res;
+    }
+    else
+    {
+        float zerox = smx / sm + origin;
+        if (zerox < x) return x;
+        else if (zerox > xm) return xm;
+        else return zerox;
+    }
+}
+
+
+#if !defined GRAPHITE2_NTRACING
+
+void Zones::jsonDbgOut(Segment *seg) const {
+
+    if (_dbg)
+    {
+        for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
+        {
+            *_dbg << json::flat << json::array
+                << objectid(dslot(seg, (Slot *)(s->_env[0])))
+                << reinterpret_cast<ptrdiff_t>(s->_env[1]);
+            if (s->_isdel)
+                *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
+            else
+                *_dbg << "exclude" << json::flat << json::array
+                    << s->_excl.x << s->_excl.xm 
+                    << s->_excl.sm << s->_excl.smx << s->_excl.c
+                    << json::close;
+            *_dbg << json::close;
+        }
+    }
+}
+
+#endif
+
--- a/gfx/graphite2/src/Justifier.cpp
+++ b/gfx/graphite2/src/Justifier.cpp
@@ -55,37 +55,37 @@ void JustifyTotal::accumulate(Slot *s, S
 {
     ++m_numGlyphs;
     m_tStretch += s->getJustify(seg, level, 0);
     m_tShrink += s->getJustify(seg, level, 1);
     m_tStep += s->getJustify(seg, level, 2);
     m_tWeight += s->getJustify(seg, level, 3);
 }
 
-float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast)
+float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)
 {
     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 (!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 ((flags & gr_justEndInline) == 0)
+    if ((jflags & gr_justEndInline) == 0)
     {
         do {
             Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
-            if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.)
+            if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
                 break;
             pLast = pLast->prev();
         } while (pLast != pFirst);
     }
 
     end = pLast->nextSibling();
     pFirst = pFirst->nextSibling();
 
@@ -111,28 +111,27 @@ float Segment::justify(Slot *pSlot, cons
                 s->setJustify(this, 0, 3, 1);
                 s->setJustify(this, 0, 2, 1);
                 s->setJustify(this, 0, 0, -1);
             }
         }
         ++numLevels;
     }
 
-    JustifyTotal *stats = new JustifyTotal[numLevels];
-    if (!stats) return -1.0;
+    Vector<JustifyTotal> stats(numLevels);
     for (s = pFirst; s != end; s = s->nextSibling())
     {
         float w = s->origin().x / scale + s->advance() - base;
         if (w > currWidth) currWidth = w;
         for (int j = 0; j < numLevels; ++j)
             stats[j].accumulate(s, this, j);
         s->just(0);
     }
 
-    for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i)
+    for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
     {
         float diff;
         float error = 0.;
         float diffpw;
         int tWeight = stats[i].weight();
 
         do {
             error = 0.;
@@ -192,17 +191,17 @@ float Segment::justify(Slot *pSlot, cons
 #if !defined GRAPHITE2_NTRACING
     json * const dbgout = m_face->logger();
     if (dbgout)
         *dbgout << json::object
                     << "justifies"  << objectid(this)
                     << "passes"     << json::array;
 #endif
 
-    if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1)))
+    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);
         Slot *lEnd = pLast->nextSibling();
--- a/gfx/graphite2/src/Pass.cpp
+++ b/gfx/graphite2/src/Pass.cpp
@@ -26,34 +26,38 @@ of the License or (at your option) any l
 */
 #include "inc/Main.h"
 #include "inc/debug.h"
 #include "inc/Endian.h"
 #include "inc/Pass.h"
 #include <cstring>
 #include <cstdlib>
 #include <cassert>
+#include <cmath>
 #include "inc/Segment.h"
 #include "inc/Code.h"
 #include "inc/Rule.h"
 #include "inc/Error.h"
+#include "inc/Collider.h"
 
 using namespace graphite2;
 using vm::Machine;
 typedef Machine::Code  Code;
 
 
 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_iMaxLoop(0),
   m_numGlyphs(0),
   m_numRules(0),
   m_numStates(0),
   m_numTransition(0),
   m_numSuccess(0),
   m_numColumns(0),
@@ -65,48 +69,54 @@ Pass::Pass()
 Pass::~Pass()
 {
     free(m_cols);
     free(m_startStates);
     free(m_transitions);
     free(m_states);
     free(m_ruleMap);
 
-    delete [] m_rules;
+    if (m_rules) delete [] m_rules;
+    if (m_codes) delete [] m_codes;
+    free(m_progs);
 }
 
-bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED Face & face, Error &e)
+bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base,
+        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;
+    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))
+        return face.error(e);
     m_iMaxLoop = be::read<byte>(p);
     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);
     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);
     m_numColumns = be::read<uint16>(p);
     numRanges = be::read<uint16>(p);
     be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift.
     assert(p - pass_start == 40);
     // Perform some sanity checks.
     if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS)
             || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS)
             || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES)
-            || e.test(numRanges == 0, E_NORANGES))
+            || e.test(m_numRules && numRanges == 0, E_NORANGES))
         return face.error(e);
 
     m_successStart = m_numStates - m_numSuccess;
     if (e.test(p + numRanges * 6 - 4 > pass_end, E_BADPASSLENGTH)) return face.error(e);
     m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1;
     // Calculate the start of various arrays.
     const byte * const ranges = p;
     be::skip<uint16>(p, numRanges*3);
@@ -126,78 +136,92 @@ bool Pass::readPass(const byte * const p
     m_maxPreCtxt = be::read<uint8>(p);
     if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e);
     const byte * const start_states = p;
     be::skip<int16>(p, m_maxPreCtxt - m_minPreCtxt + 1);
     const uint16 * const sort_keys = reinterpret_cast<const uint16 *>(p);
     be::skip<uint16>(p, m_numRules);
     const byte * const precontext = p;
     be::skip<byte>(p, m_numRules);
-    be::skip<byte>(p);     // skip reserved byte
 
-    if (e.test(p + sizeof(uint16) > pass_end, E_BADCTXTLENS)) return face.error(e);
+    if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e);
+    m_colThreshold = be::read<uint8>(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);
     be::skip<int16>(p, m_numTransition*m_numColumns);
-    be::skip<byte>(p);          // skip reserved byte
-    if (e.test(p != pcCode, E_BADPASSCCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e);
+    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(p >= pass_end, E_BADPASSLENGTH)
+    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) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e);
+    if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e);
     be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
 
     // We should be at the end or within the pass
     if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e);
 
     // Load the pass constraint if there is one.
     if (pass_constraint_len)
     {
         face.error_context(face.error_context() + 1);
         m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, 
-                                  precontext[0], be::peek<uint16>(sort_keys), *m_silf, face);
+                                  precontext[0], be::peek<uint16>(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN);
         if (e.test(!m_cPConstraint, E_OUTOFMEM)
-                || e.test(m_cPConstraint.status(), m_cPConstraint.status() + E_CODEFAILURE))
+                || e.test(!m_cPConstraint, m_cPConstraint.status() + E_CODEFAILURE))
             return face.error(e);
         face.error_context(face.error_context() - 1);
     }
-    if (!readRanges(ranges, numRanges, e)) return face.error(e);
-    if (!readRules(rule_map, numEntries,  precontext, sort_keys,
-                   o_constraint, rcCode, o_actions, aCode, face, e)) return false;
+    if (m_numRules)
+    {
+        if (!readRanges(ranges, numRanges, e)) return face.error(e);
+        if (!readRules(rule_map, numEntries,  precontext, sort_keys,
+                   o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false;
+    }
 #ifdef GRAPHITE2_TELEMETRY
     telemetry::category _states_cat(face.tele.states);
 #endif
-    return readStates(start_states, states, o_rule_map, face, e);
+    return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true;
 }
 
 
 bool Pass::readRules(const byte * rule_map, const size_t num_entries,
                      const byte *precontext, const uint16 * sort_key,
                      const uint16 * o_constraint, const byte *rc_data,
                      const uint16 * o_action,     const byte * ac_data,
-                     Face & face, Error &e)
+                     Face & face, passtype pt, Error &e)
 {
     const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules);
     const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules);
 
-    if (e.test(!(m_rules = new Rule [m_numRules]), E_OUTOFMEM)) return face.error(e);
     precontext += m_numRules;
     sort_key   += m_numRules;
     o_constraint += m_numRules;
     o_action += m_numRules;
 
     // 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;
+    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);
 #ifndef NDEBUG
         r->rule_idx   = n - 1;
@@ -206,26 +230,37 @@ bool Pass::readRules(const byte * rule_m
             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)
             return false;
-        r->action     = new vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face);
-        r->constraint = new vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face);
+        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)
+    {
+        m_progs += delta;
+        for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
+        {
+            c->externalProgramMoved(delta);
+        }
+    }
+
     // Load the rule entries map
     face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
     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);
@@ -320,43 +355,62 @@ bool Pass::readRanges(const byte * range
 
         if (e.test(ci != ci_end, E_BADRANGE))
             return false;
     }
     return true;
 }
 
 
-void Pass::runGraphite(Machine & m, FiniteStateMachine & fsm) const
+bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const
 {
     Slot *s = m.slotMap().segment.first();
-    if (!s || !testPassConstraint(m)) return;
-    Slot *currHigh = s->next();
+    if (!s || !testPassConstraint(m)) return true;
+    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);
+        if (fsm.dbgout)  *fsm.dbgout << "rules" << json::array;
+        json::closer rules_array_closer(fsm.dbgout);
 #endif
 
-    m.slotMap().highwater(currHigh);
-    int lc = m_iMaxLoop;
-    do
+        m.slotMap().highwater(currHigh);
+        int lc = m_iMaxLoop;
+        do
+        {
+            findNDoRule(s, m, fsm);
+            if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) {
+                if (!lc)
+                    s = m.slotMap().highwater();
+                lc = m_iMaxLoop;
+                if (s)
+                    m.slotMap().highwater(s->next());
+            }
+        } while (s);
+    }
+
+    if (!(m_flags & 15) || !m.slotMap().segment.hasCollisionInfo())
+        return true;
+
+    if (m_flags & 7)
     {
-        findNDoRule(s, m, fsm);
-        if (s && (m.slotMap().highpassed() || s == m.slotMap().highwater() || --lc == 0)) {
-            if (!lc)
-            {
-//              if (dbgout) *dbgout << json::item << json::flat << rule_event(-1, s, 1);
-                s = m.slotMap().highwater();
-            }
-            lc = m_iMaxLoop;
-            if (s)
-                m.slotMap().highwater(s->next());
+        if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
+        {
+            m.slotMap().segment.positionSlots(0, 0, 0, true);
+//            m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
         }
-    } while (s);
+        if (!collisionShift(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
+            return false;
+    }
+    if ((m_flags & 24) && !collisionKern(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
+        return false;
+    if ((m_flags & 15) && !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)
         return false;
 
@@ -450,28 +504,30 @@ void Pass::findNDoRule(Slot * & slot, Ma
                 if (r->rule->action->deletes()) fsm.slots.collectGarbage();
                 adjustSlot(adv, slot, fsm.slots);
                 return;
             }
         }
     }
 
     slot = slot->next();
+    return;
 }
 
 #if !defined GRAPHITE2_NTRACING
 
 void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const
 {
     *fsm.dbgout << "considered" << json::array;
     for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r)
     {
-        if (r->rule->preContext > fsm.slots.context())  continue;
-    *fsm.dbgout << json::flat << json::object
-                    << "id"     << r->rule - m_rules
+        if (r->rule->preContext > fsm.slots.context())
+            continue;
+        *fsm.dbgout << json::flat << json::object
+                    << "id" << r->rule - m_rules
                     << "failed" << true
                     << "input" << json::flat << json::object
                         << "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
     }
 }
@@ -581,40 +637,413 @@ int Pass::doAction(const Code *codeptr, 
 
     slot_out = *map;
     return ret;
 }
 
 
 void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
 {
-    if (delta < 0)
+    if (!slot_out)
     {
-        if (!slot_out)
+        if (smap.highpassed() || slot_out == smap.highwater())
         {
             slot_out = smap.segment.last();
             ++delta;
-            if (smap.highpassed() && !smap.highwater())
+            if (!smap.highwater())
                 smap.highpassed(false);
         }
+        else
+        {
+            slot_out = smap.segment.first();
+            --delta;
+        }
+    }
+    if (delta < 0)
+    {
         while (++delta <= 0 && slot_out)
         {
             if (smap.highpassed() && smap.highwater() == slot_out)
                 smap.highpassed(false);
             slot_out = slot_out->prev();
         }
     }
     else if (delta > 0)
     {
-        if (!slot_out)
-        {
-            slot_out = smap.segment.first();
-            --delta;
-        }
         while (--delta >= 0 && slot_out)
         {
             slot_out = slot_out->next();
             if (slot_out == smap.highwater() && slot_out)
                 smap.highpassed(true);
         }
     }
 }
 
+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;
+#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)
+            {
+                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)
+        {
+            if (hasCollisions || moved)
+            {
+
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array;
+#endif
+                // phase 2a : if any shiftable glyphs are in collision, iterate backwards,
+                // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY
+                // glyphs that are actually in collision from phases 1 or 2b, and working backwards
+                // has the intended effect of breaking logjams.
+                if (hasCollisions)
+                {
+                    hasCollisions = false;
+                    #if 0
+                    moved = true;
+                    for (Slot *s = start; s != end; s = s->next())
+                    {
+                        SlotCollision * c = seg->collisionInfo(s);
+                        c->setShift(Position(0, 0));
+                    }
+                    #endif
+                    Slot *lend = end ? end->prev() : seg->last();
+                    Slot *lstart = start->prev();
+                    for (Slot *s = lend; s != lstart; s = s->prev())
+                    {
+                        SlotCollision * c = seg->collisionInfo(s);
+                        if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL))
+                                        == (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding
+                        {
+                            if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout))
+                                return false;
+                            c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK);
+                        }
+                    }
+                }
+
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    *dbgout << json::close << json::close // phase 2a
+                        << json::object << "phase" << "2b" << "loop" << i << "moves" << json::array;
+#endif
+
+                // phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts 
+                // glyphs from their current adjusted position, which has the effect of gradually minimizing the  
+                // resulting adjustment; ie, the final result will be gradually closer to the original location.  
+                // Also it allows more flexibility in the final adjustment, since it is moving along the  
+                // possible 8 vectors from successively different starting locations.
+                if (moved)
+                {
+                    moved = false;
+                    for (Slot *s = start; s != end; s = s->next())
+                    {
+                        SlotCollision * c = seg->collisionInfo(s);
+                        if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK
+                                                        | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
+                                  && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
+                            return false;
+                        else if (c->flags() & SlotCollision::COLL_TEMPLOCK)
+                            c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK);
+                    }
+                }
+        //      if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things
+        //          break;
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    *dbgout << json::close << json::close; // phase 2
+#endif
+            }
+        }
+        if (!end)
+            break;
+        start = NULL;
+        for (Slot *s = end->prev(); s; s = s->next())
+        {
+            if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START)
+            {
+                start = s;
+                break;
+            }
+        }
+    }
+    return true;
+}
+
+bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const
+{
+    KernCollider kerncoll(dbgout);
+    Slot *start = seg->first();
+    float ymin = 1e38f;
+    float ymax = -1e38f;
+    const GlyphCache &gc = seg->getFace()->glyphs();
+
+    // phase 3 : handle kerning of clusters
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        *dbgout << json::object << "phase" << "3" << "moves" << json::array;
+#endif
+
+    for (Slot *s = seg->first(); s; s = s->next())
+    {
+        if (!gc.check(s->gid()))
+            return false;
+        const SlotCollision * c = seg->collisionInfo(s);
+        const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid());
+        float y = s->origin().y + c->shift().y;
+        ymax = max(y + bbox.tr.y, ymax);
+        ymin = min(y + bbox.bl.y, ymin);
+        if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
+                        == (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
+            resolveKern(seg, s, start, kerncoll, dir, ymin, ymax, dbgout);
+        if (c->flags() & SlotCollision::COLL_END)
+            start = NULL;
+        if (c->flags() & SlotCollision::COLL_START)
+            start = s;
+    }
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        *dbgout << json::close << json::close; // phase 3
+#endif
+    return true;
+}
+
+bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const
+{
+    for (Slot *s = seg->first(); s; s = s->next())
+    {
+        SlotCollision *c = seg->collisionInfo(s);
+        if (c->shift().x != 0 || c->shift().y != 0)
+        {
+            const Position newOffset = c->shift();
+            const Position nullPosition(0, 0);
+            c->setOffset(newOffset + c->offset());
+            c->setShift(nullPosition);
+        }
+    }
+//    seg->positionSlots();
+
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            *dbgout << json::close;
+#endif
+    return true;
+}
+
+// Can slot s be kerned, or is it attached to something that can be kerned?
+static bool inKernCluster(Segment *seg, Slot *s)
+{
+    SlotCollision *c = seg->collisionInfo(s);
+    if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
+        return true;
+    while (s->attachedTo())
+    {
+        s = s->attachedTo();
+        c = seg->collisionInfo(s);
+        if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
+            return true;
+    }
+    return false;
+}
+
+// Fix collisions for the given slot.
+// Return true if everything was fixed, false if there are still collisions remaining.
+// isRev means be we are processing backwards.
+bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
+        ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol,
+        json * const dbgout) const
+{
+    Slot * nbor;  // neighboring slot
+    SlotCollision *cFix = seg->collisionInfo(slotFix);
+    if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(),
+            cFix->shift(), cFix->offset(), dir, dbgout))
+        return false;
+    bool collides = false;
+    // When we're processing forward, ignore kernable glyphs that preceed the target glyph.
+    // When processing backward, don't ignore these until we pass slotFix.
+    bool ignoreForKern = !isRev;
+    bool rtl = dir & 1;
+    Slot *base = slotFix;
+    while (base->attachedTo())
+        base = base->attachedTo();
+    Position zero(0., 0.);
+    
+    // Look for collisions with the neighboring glyphs.
+    for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next())
+    {
+        SlotCollision *cNbor = seg->collisionInfo(nbor);
+        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_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)))
+            break;
+    }
+    bool isCol = false;
+    if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f)
+    {
+        Position shift = coll.resolve(seg, isCol, dbgout);
+        // isCol has been set to true if a collision remains.
+        if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f)
+        {
+            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);
+            }
+        }
+    }
+    else
+    {
+        // This glyph is not colliding with anything.
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+        {
+            *dbgout << json::object 
+                            << "missed" << objectid(dslot(seg, slotFix));
+            coll.outputJsonDbg(dbgout, seg, -1);
+            *dbgout << json::close;
+        }
+#endif
+    }
+
+    // Set the is-collision flag bit.
+    if (isCol)
+    { cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); }
+    else
+    { cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); }
+    hasCol |= isCol;
+    return true;
+}
+
+float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, KernCollider &coll, int dir,
+    float &ymin, float &ymax, json *const dbgout) const
+{
+    Slot *nbor; // neighboring slot
+    float currSpace = 0.;
+    bool collides = false;
+    unsigned int space_count = 0;
+    Slot *base = slotFix;
+    while (base->attachedTo())
+        base = base->attachedTo();
+    SlotCollision *cFix = seg->collisionInfo(base);
+    const GlyphCache &gc = seg->getFace()->glyphs();
+
+    if (base != slotFix)
+    {
+        cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX);
+        return 0;
+    }
+    bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0;
+    bool isInit = false;
+
+    for (nbor = slotFix->next(); nbor; nbor = nbor->next())
+    {
+        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)
+                break;
+            // Add space for a space glyph.
+            currSpace += nbor->advance();
+            ++space_count;
+        }
+        else
+        {
+            space_count = 0; 
+            float y = nbor->origin().y + cNbor->shift().y;
+            ymax = max(y + bb.tr.y, ymax);
+            ymin = min(y + bb.bl.y, ymin);
+            if (nbor != slotFix && !(cNbor->flags() & SlotCollision::COLL_IGNORE))
+            {
+                seenEnd = true;
+                if (!isInit)
+                {
+                    if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(),
+                                    cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout))
+                        return 0.;
+                    isInit = true;
+                }
+                collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout);
+            }
+        }
+        if (cNbor->flags() & SlotCollision::COLL_END)
+        {
+            if (seenEnd && space_count < 2)
+                break;
+            else
+                seenEnd = true;
+        }
+    }
+    if (collides)
+    {
+        Position mv = coll.resolve(seg, slotFix, dir, cFix->margin(), dbgout);
+        coll.shift(mv, dir);
+        Position delta = slotFix->advancePos() + mv - cFix->shift();
+        slotFix->advance(delta);
+        cFix->setShift(mv);
+        return mv.x;
+    }
+    return 0.;
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/Position.cpp
@@ -0,0 +1,98 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, 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.
+*/
+#include "inc/Position.h"
+#include <cmath>
+
+using namespace graphite2;
+
+bool Rect::hitTest(Rect &other)
+{
+    if (bl.x > other.tr.x) return false;
+    if (tr.x < other.bl.x) return false;
+    if (bl.y > other.tr.y) return false;
+    if (tr.y < other.bl.y) return false;
+    return true;
+}
+
+Position Rect::overlap(Position &offset, Rect &other, Position &othero)
+{
+    float ax = (bl.x + offset.x) - (other.tr.x + othero.x);
+    float ay = (bl.y + offset.y) - (other.tr.y + othero.y);
+    float bx = (other.bl.x + othero.x) - (tr.x + offset.x);
+    float by = (other.bl.y + othero.y) - (tr.y + offset.y);
+    return Position((ax > bx ? ax : bx), (ay > by ? ay : by));
+}
+
+float boundmin(float move, float lim1, float lim2, float &error)
+{
+    // error is always positive for easy comparison
+    if (move < lim1 && move < lim2)
+    { error = 0.; return move; }
+    else if (lim1 < lim2)
+    { error = std::fabs(move - lim1); return lim1; }
+    else
+    { error = std::fabs(move - lim2); return lim2; }
+}
+
+#if 0
+Position Rect::constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox)
+{
+    // a = max, i = min, s = sum, d = diff
+    float eax, eay, eix, eiy, eas, eis, ead, eid;
+    float beste = INF;
+    Position res;
+    // calculate the movements in each direction and the error (amount of remaining overlap)
+    // first param is movement, second and third are movement over the constraining box
+    float ax = boundmin(obox.tr.x + other.x - box.bl.x - offset.x + 1, tr.x - offset.x, INF, &eax);
+    float ay = boundmin(obox.tr.y + other.y - box.bl.y - offset.y + 1, tr.y - offset.y, INF, &eay);
+    float ix = boundmin(obox.bl.x + other.x - box.tr.x - offset.x + 1, bl.x - offset.x, INF, &eix);
+    float iy = boundmin(obox.bl.y + other.y - box.tr.y - offset.y + 1, bl.y - offset.y, INF, &eiy);
+    float as = boundmin(ISQRT2 * (osdbox.tr.x + other.x + other.y - sdbox.bl.x - offset.x - offset.y) + 1, tr.x - offset.x, tr.y - offset.y, &eas);
+    float is = boundmin(ISQRT2 * (osdbox.bl.x + other.x + other.y - sdbox.tr.x - offset.x - offset.y) + 1, bl.x - offset.x, bl.y - offset.y, &eis);
+    float ad = boundmin(ISQRT2 * (osdbox.tr.y + other.x - other.y - sdbox.bl.y - offset.x + offset.y) + 1, tr.y - offset.y, tr.x - offset.x, &ead);
+    float id = boundmin(ISQRT2 * (osdbox.bl.y + other.x - other.y - sdbox.tr.y - offset.x + offset.y) + 1, bl.y - offset.y, bl.x - offset.x, &eid);
+
+    if (eax < beste)
+    { res = Position(ax, 0); beste = eax; }
+    if (eay < beste)
+    { res = Position(0, ay); beste = eay; }
+    if (eix < beste)
+    { res = Position(ix, 0); beste = eix; }
+    if (eiy < beste)
+    { res = Position(0, iy); beste = eiy; }
+    if (SQRT2 * (eas) < beste)
+    { res = Position(as, ad); beste = SQRT2 * (eas); }
+    if (SQRT2 * (eis) < beste)
+    { res = Position(is, is); beste = SQRT2 * (eis); }
+    if (SQRT2 * (ead) < beste)
+    { res = Position(ad, ad); beste = SQRT2 * (ead); }
+    if (SQRT2 * (eid) < beste)
+    { res = Position(id, id); beste = SQRT2 * (eid); }
+    return res;
+}
+#endif
+
--- a/gfx/graphite2/src/Segment.cpp
+++ b/gfx/graphite2/src/Segment.cpp
@@ -32,35 +32,38 @@ of the License or (at your option) any l
 #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/Collider.h"
 #include "graphite2/Segment.h"
 
 
 using namespace graphite2;
 
 Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int textDir)
 : m_freeSlots(NULL),
   m_freeJustifies(NULL),
   m_charinfo(new CharInfo[numchars]),
+  m_collisions(NULL),
   m_face(face),
   m_silf(face->chooseSilf(script)),
   m_first(NULL),
   m_last(NULL),
   m_bufSize(numchars + 10),
   m_numGlyphs(numchars),
   m_numCharinfo(numchars),
   m_passBits(m_silf->aPassBits() ? -1 : 0),
   m_defaultOriginal(0),
-  m_dir(textDir)
+  m_dir(textDir),
+  m_flags(0)
 {
     freeSlot(newSlot());
     m_bufSize = log_binary(numchars)+1;
 }
 
 Segment::~Segment()
 {
     for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
@@ -330,39 +333,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)
+Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, 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)
     {
         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);
+                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, 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);
+                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
         }
     }
     return currpos;
 }
 
 
 void Segment::associateChars(int offset, int numChars)
 {
@@ -429,21 +432,16 @@ 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;
 }
 
-void Segment::prepare_pos(const Font * /*font*/)
-{
-    // copy key changeable metrics into slot (if any);
-}
-
 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)
 {
     if (slotCount() == 0)
@@ -457,17 +455,17 @@ void Segment::bidiPass(uint8 aBidi, int 
     {
         if (s->getBidiClass() == -1)
         {
             unsigned int bAttr = glyphAttr(s->gid(), aBidi);
             s->setBidiClass((bAttr <= 22) * bAttr);
         }
         bmask |= (1 << s->getBidiClass());
         s->setBidiLevel(baseLevel);
-        if (glyphAttr(s->gid(), aMirror) && s->getBidiClass() == 21)
+        if (s->getBidiClass() == 21)
             ++ssize;
     }
 
     BracketPairStack bstack(ssize);
     if (bmask & (paradir ? 0x2E7892 : 0x2E789C))
     {
         // O(8N) algorithm, with no working data beyond what is needed for processParens
         int nextLevel = paradir;
@@ -487,8 +485,21 @@ 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);
         }
     }
 }
 
+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)
+    {
+        ::new (m_collisions + p->index()) SlotCollision(this, p);
+        p = p->next();
+    }
+    return true;
+}
--- a/gfx/graphite2/src/Silf.cpp
+++ b/gfx/graphite2/src/Silf.cpp
@@ -88,16 +88,20 @@ void Silf::releaseBuffers() throw()
 
 
 bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version)
 {
     const byte * p = silf_start,
                * const silf_end = p + lSilf;
     Error e;
 
+    if (e.test(version >= 0x00060000, E_BADSILFVERSION))
+    {
+        releaseBuffers(); return face.error(e);
+    }
     if (version >= 0x00030000)
     {
         if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
         be::skip<int32>(p);    // ruleVersion
         be::skip<uint16>(p,2); // passOffset & pseudosOffset
     }
     else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
     const uint16 maxGlyph = be::read<uint16>(p);
@@ -135,70 +139,83 @@ bool Silf::readGraphite(const byte * con
             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,5);                        // direction and 4 reserved bytes
+    be::skip<byte>(p);                          // direction
+    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
     const byte * o_passes = p,
                * const passes_start = silf_start + be::read<uint32>(p);
 
     const size_t num_attrs = face.glyphs().numAttrs();
     if (e.test(m_aPseudo   >= num_attrs, E_BADAPSEUDO)
         || e.test(m_aBreak >= num_attrs, E_BADABREAK)
         || e.test(m_aBidi  >= num_attrs, E_BADABIDI)
         || e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
+        || e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION)
         || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= silf_end, E_BADPASSESSTART)
         || e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
         || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
         || e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
         || e.test(m_aLig > 127, E_BADALIG))
     {
         releaseBuffers();
         return face.error(e);
     }
     be::skip<uint32>(p, m_numPasses);
     if (e.test(p + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
     m_numPseudo = be::read<uint16>(p);
     be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
-    if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO))
+    m_pseudos = new Pseudo[m_numPseudo];
+    if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO)
+        || e.test(!m_pseudos, E_OUTOFMEM))
     {
         releaseBuffers(); return face.error(e);
     }
-    m_pseudos = new Pseudo[m_numPseudo];
     for (int i = 0; i < m_numPseudo; i++)
     {
         m_pseudos[i].uid = be::read<uint32>(p);
         m_pseudos[i].gid = be::read<uint16>(p);
     }
 
     const size_t clen = readClassMap(p, passes_start - p, version, e);
-    if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
+    m_passes = new Pass[m_numPasses];
+    if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)
+          || e.test(!m_passes, E_OUTOFMEM))
+    { releaseBuffers(); return face.error(e); }
 
-    m_passes = new Pass[m_numPasses];
     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)) {
             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;
+
         m_passes[i].init(this);
-        if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, e))
+        if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, pt,
+            version, e))
         {
             releaseBuffers();
             return false;
         }
     }
 
     // fill in gr_faceinfo
     m_silfinfo.upem = face.glyphs().unitsPerEm();
@@ -368,17 +385,17 @@ bool Silf::runGraphite(Segment *seg, uin
                 *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)
+            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);
                 }
@@ -398,18 +415,19 @@ bool Silf::runGraphite(Segment *seg, uin
             seg->positionSlots(0);
             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].runGraphite(m, fsm);
+        if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || (m_passes[i].flags() & 7))
+                && !m_passes[i].runGraphite(m, fsm))
+            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))))
             return false;
     }
     return true;
 }
--- a/gfx/graphite2/src/Slot.cpp
+++ b/gfx/graphite2/src/Slot.cpp
@@ -24,16 +24,17 @@ Mozilla Public License (http://mozilla.o
 License, as published by the Free Software Foundation, either version 2
 of the License or (at your option) any later version.
 */
 #include "inc/Segment.h"
 #include "inc/Slot.h"
 #include "inc/Silf.h"
 #include "inc/CharInfo.h"
 #include "inc/Rule.h"
+#include "inc/Collider.h"
 
 
 using namespace graphite2;
 
 Slot::Slot() :
     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),
@@ -80,83 +81,90 @@ 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)
+Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal)
 {
+    SlotCollision *coll = NULL;
     if (attrLevel && m_attLevel > attrLevel) return Position(0, 0);
-    float scale = 1.0;
+    float scale = font ? font->scale() : 1.0f;
     Position shift(m_shift.x * ((seg->dir() & 1) * -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))
+            shift = shift + collshift;
+    }
     const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph());
     if (font)
     {
         scale = font->scale();
         shift *= scale;
         if (font->isHinted() && glyphFace)
-            tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(m_glyphid);
+            tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph());
         else
             tAdvance *= scale;
     }    
     Position res;
 
     m_position = base + shift;
     if (!m_parent)
     {
         res = base + Position(tAdvance, m_advance.y * scale);
-        clusterMin = base.x;
+        clusterMin = m_position.x;
     }
     else
     {
         float tAdv;
         m_position += (m_attach - m_with) * scale;
-        tAdv = m_advance.x >= 0.5 ? m_position.x + tAdvance - shift.x : 0.f;
+        tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f;
         res = Position(tAdv, 0);
-        if ((m_advance.x >= 0.5 || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
+        if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
     }
 
     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);
-        if ((!m_parent || m_advance.x >= 0.5) && tRes.x > res.x) res = tRes;
+        Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, 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);
+        Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, isFinal);
         if (tRes.x > res.x) res = tRes;
     }
     
     if (!m_parent && clusterMin < base.x)
     {
-        Position adj = Position(base.x - clusterMin, 0.);
+        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)
 {
     Position base;
     Rect bbox = seg->theGlyphBBoxTemporary(glyph());
     float clusterMin = 0.;
-    Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin);
+    Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, 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 :
@@ -175,16 +183,18 @@ int32 Slot::clusterMetric(const Segment 
         return static_cast<uint32>(res.x);
     case kgmetAdvHeight :
         return static_cast<uint32>(res.y);
     default :
         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;
     }
@@ -206,33 +216,65 @@ int Slot::getAttr(const Segment *seg, at
     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(seg->glyphAttr(gid(), seg->silf()->aBidi()));
+                                const_cast<Slot *>(this)->setBidiClass(int8(seg->glyphAttr(gid(), seg->silf()->aBidi())));
                             return m_bidiCls;
     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);
     case gr_slatUserDefn :  return m_userAttr[subindex];
     case gr_slatSegSplit :  return seg->charinfo(m_original)->flags() & 3;
     case gr_slatBidiLevel:  return m_bidiLevel;
-    default :               return 0;
+    case gr_slatColFlags :		{ SlotCollision *c = seg->collisionInfo(this); return c ? c->flags() : 0; }
+    case gr_slatColLimitblx :	SLOTGETCOLATTR(limit().bl.x)
+    case gr_slatColLimitbly :	SLOTGETCOLATTR(limit().bl.y)
+    case gr_slatColLimittrx :	SLOTGETCOLATTR(limit().tr.x)
+    case gr_slatColLimittry :	SLOTGETCOLATTR(limit().tr.y)
+    case gr_slatColShiftx :		SLOTGETCOLATTR(offset().x)
+    case gr_slatColShifty :		SLOTGETCOLATTR(offset().y)
+    case gr_slatColMargin :		SLOTGETCOLATTR(margin())
+    case gr_slatColMarginWt :	SLOTGETCOLATTR(marginWt())
+    case gr_slatColExclGlyph :	SLOTGETCOLATTR(exclGlyph())
+    case gr_slatColExclOffx :	SLOTGETCOLATTR(exclOffset().x)
+    case gr_slatColExclOffy :	SLOTGETCOLATTR(exclOffset().y)
+    case gr_slatSeqClass :		SLOTGETCOLATTR(seqClass())
+	case gr_slatSeqProxClass :	SLOTGETCOLATTR(seqProxClass())
+    case gr_slatSeqOrder :		SLOTGETCOLATTR(seqOrder())
+    case gr_slatSeqAboveXoff :	SLOTGETCOLATTR(seqAboveXoff())
+    case gr_slatSeqAboveWt :	SLOTGETCOLATTR(seqAboveWt())
+    case gr_slatSeqBelowXlim :	SLOTGETCOLATTR(seqBelowXlim())
+    case gr_slatSeqBelowWt :	SLOTGETCOLATTR(seqBelowWt())
+    case gr_slatSeqValignHt :	SLOTGETCOLATTR(seqValignHt())
+    case gr_slatSeqValignWt :	SLOTGETCOLATTR(seqValignWt())
+    default : return 0;
     }
 }
 
+#define SLOTCOLSETATTR(x) { \
+        SlotCollision *c = seg->collisionInfo(this); \
+        if (c) { c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
+        break; }
+#define SLOTCOLSETCOMPLEXATTR(t, y, x) { \
+        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;
     }
@@ -247,19 +289,19 @@ void Slot::setAttr(Segment *seg, attrCod
     case gr_slatAdvX :  m_advance.x = value; break;
     case gr_slatAdvY :  m_advance.y = value; break;
     case gr_slatAttTo :
     {
         const uint16 idx = uint16(value);
         if (idx < map.size() && map[idx])
         {
             Slot *other = map[idx];
-            if (other == this) break;
+            if (other == this || other == m_parent) break;
             if (m_parent) m_parent->removeChild(this);
-            if (other->child(this))
+            if (!other->isChildOf(this) && other->child(this))
             {
                 attachTo(other);
                 if (((seg->dir() & 1) != 0) ^ (idx > subindex))
                     m_with = Position(advance(), 0);
                 else        // normal match to previous root
                     m_attach = Position(other->advance(), 0);
             }
         }
@@ -275,29 +317,52 @@ 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 = value; break;
+    case gr_slatDir :       m_bidiCls = int8(value); 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;
     case gr_slatMeasureEol :    break;
     case gr_slatJWidth :    just(value); break;
     case gr_slatSegSplit :  seg->charinfo(m_original)->addflags(value & 3); break;
     case gr_slatUserDefn :  m_userAttr[subindex] = value; break;
+    case gr_slatColFlags :  {
+        SlotCollision *c = seg->collisionInfo(this);
+        if (c)
+            c->setFlags(value);
+        break; }
+    case gr_slatColLimitblx :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(value, s.bl.y), s.tr)))
+    case gr_slatColLimitbly :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(s.bl.x, value), s.tr)))
+    case gr_slatColLimittrx :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(value, s.tr.y))))
+    case gr_slatColLimittry :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(s.tr.x, value))))
+    case gr_slatColMargin :		SLOTCOLSETATTR(setMargin(value))
+    case gr_slatColMarginWt :	SLOTCOLSETATTR(setMarginWt(value))
+    case gr_slatColExclGlyph :	SLOTCOLSETATTR(setExclGlyph(value))
+    case gr_slatColExclOffx :	SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(value, s.y)))
+    case gr_slatColExclOffy :	SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(s.x, value)))
+    case gr_slatSeqClass :		SLOTCOLSETATTR(setSeqClass(value))
+	case gr_slatSeqProxClass :	SLOTCOLSETATTR(setSeqProxClass(value))
+    case gr_slatSeqOrder :		SLOTCOLSETATTR(setSeqOrder(value))
+    case gr_slatSeqAboveXoff :	SLOTCOLSETATTR(setSeqAboveXoff(value))
+    case gr_slatSeqAboveWt :	SLOTCOLSETATTR(setSeqAboveWt(value))
+    case gr_slatSeqBelowXlim :	SLOTCOLSETATTR(setSeqBelowXlim(value))
+    case gr_slatSeqBelowWt :	SLOTCOLSETATTR(setSeqBelowWt(value))
+    case gr_slatSeqValignHt :	SLOTCOLSETATTR(setSeqValignHt(value))
+    case gr_slatSeqValignWt :	SLOTCOLSETATTR(setSeqValignWt(value))
     default :
         break;
     }
 }
 
 int Slot::getJustify(const Segment *seg, uint8 level, uint8 subindex) const
 {
     if (level && level >= seg->silf()->numJustLevels()) return 0;
@@ -369,16 +434,17 @@ bool Slot::removeChild(Slot *ap)
 }
 
 bool Slot::removeSibling(Slot *ap)
 {
     if (this == ap || !m_sibling) return false;
     else if (ap == m_sibling)
     {
         m_sibling = m_sibling->nextSibling();
+        ap->sibling(NULL);
         return true;
     }
     else
         return m_sibling->removeSibling(ap);
     return true;
 }
 
 void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
@@ -420,8 +486,34 @@ void SlotJustify::LoadSlot(const Slot *s
         Justinfo *justs = seg->silf()->justAttrs() + i;
         int16 *v = values + i * NUMJUSTPARAMS;
         v[0] = seg->glyphAttr(s->gid(), justs->attrStretch());
         v[1] = seg->glyphAttr(s->gid(), justs->attrShrink());
         v[2] = seg->glyphAttr(s->gid(), justs->attrStep());
         v[3] = seg->glyphAttr(s->gid(), justs->attrWeight());
     }
 }
+
+Slot * Slot::nextInCluster(const Slot *s) const
+{
+    Slot *base;
+    if (s->firstChild())
+        return s->firstChild();
+    else if (s->nextSibling())
+        return s->nextSibling();
+    while ((base = s->attachedTo()))
+    {
+        if (base->firstChild() == s && base->nextSibling())
+            return base->nextSibling();
+        s = base;
+    }
+    return NULL;
+}
+
+bool Slot::isChildOf(const Slot *base) const
+{
+    if (m_parent == base)
+        return true;
+    else if (!m_parent)
+        return false;
+    else
+        return m_parent->isChildOf(base);
+}
--- a/gfx/graphite2/src/TtfUtil.cpp
+++ b/gfx/graphite2/src/TtfUtil.cpp
@@ -868,33 +868,63 @@ const void * FindCmapSubtable(const void
     }
 
     return 0;
 }
 
 /*----------------------------------------------------------------------------------------------
     Check the Microsoft Unicode subtable for expected values
 ----------------------------------------------------------------------------------------------*/
-bool CheckCmapSubtable4(const void * pCmapSubtable4)
+bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/)
 {
     if (!pCmapSubtable4) return false;
     const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);
-    // Bob H says ome freeware TT fonts have version 1 (eg, CALIGULA.TTF) 
+    // 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 < 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);
-    return (chEnd == 0xFFFF);
+    if (chEnd != 0xFFFF)
+        return false;
+#if 0
+    int lastend = -1;
+    for (int i = 0; i < nRanges; ++i)
+    {
+        uint16 end = be::peek<uint16>(pTable4->end_code + i);
+        uint16 start = be::peek<uint16>(pTable4->end_code + nRanges + 1 + i);
+        int16 delta = be::peek<int16>(pTable4->end_code + 2*nRanges + 1 + i);
+        uint16 offset = be::peek<uint16>(pTable4->end_code + 3*nRanges + 1 + i);
+        if (lastend >= end || lastend >= start)
+            return false;
+        if (offset)
+        {
+            const uint16 *gstart = pTable4->end_code + 3*nRanges + 1 + i + (offset >> 1);
+            const uint16 *gend = gstart + end - start;
+            if ((char *)gend >= (char *)pCmapSubtable4 + length)
+                return false;
+            while (gstart <= gend)
+            {
+                uint16 g = be::peek<uint16>(gstart++);
+                if (g && ((g + delta) & 0xFFFF) > maxgid)
+                    return false;
+            }
+        }
+        else if (((delta + end) & 0xFFFF) > maxgid)
+            return false;
+        lastend = end;
+    }
+#endif
+    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)
@@ -1027,29 +1057,39 @@ 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)
+bool CheckCmapSubtable12(const void *pCmapSubtable12 /*, 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 < sizeof(Sfnt::CmapSubTableFormat12))
         return false;
-    
-    return (length == (sizeof(Sfnt::CmapSubTableFormat12) + (be::swap(pTable12->num_groups) - 1)
-        * sizeof(uint32) * 3));
+    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)
+    {
+        if (be::swap(pTable12->group[i].end_char_code)  - be::swap(pTable12->group[i].start_char_code) + be::swap(pTable12->group[i].start_glyph_id) > maxgid)
+            return false;
+        if (i > 0 && be::swap(pTable12->group[i].start_char_code) <= be::swap(pTable12->group[i-1].end_char_code))
+            return false;
+    }
+#endif
+    return true;
 }
 
 /*----------------------------------------------------------------------------------------------
     Return the Glyph ID for the given Unicode ID in the Microsoft UCS-4 subtable.
     (Actually this code only depends on subtable being format 12.)
     Return 0 if the Unicode ID is not in the subtable.
 ----------------------------------------------------------------------------------------------*/
 gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey)
@@ -1140,49 +1180,53 @@ unsigned int CmapSubtable12NextCodepoint
     Technically this method should return an unsigned long but it is unlikely the offset will
         exceed 2^31.
 ----------------------------------------------------------------------------------------------*/
 size_t LocaLookup(gid16 nGlyphId, 
         const void * pLoca, size_t lLocaSize, 
         const void * pHead) // throw (std::out_of_range)
 {
     const Sfnt::FontHeader * pTable = reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+    size_t res = -2;
 
     // CheckTable verifies the index_to_loc_format is valid
     if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat)
     { // loca entries are two bytes and have been divided by two
         if (nGlyphId < (lLocaSize >> 1) - 1) // allow sentinel value to be accessed
         {
             const uint16 * pShortTable = reinterpret_cast<const uint16 *>(pLoca);
-            return (be::peek<uint16>(pShortTable + nGlyphId) << 1);
+            res = be::peek<uint16>(pShortTable + nGlyphId) << 1;
+            if (res == static_cast<size_t>(be::peek<uint16>(pShortTable + nGlyphId + 1) << 1))
+                return -1;
         }
     }
-    
-    if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)
+    else if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)
     { // loca entries are four bytes
         if (nGlyphId < (lLocaSize >> 2) - 1)
         {
             const uint32 * pLongTable = reinterpret_cast<const uint32 *>(pLoca);
-            return be::peek<uint32>(pLongTable + nGlyphId);
+            res = be::peek<uint32>(pLongTable + nGlyphId);
+            if (res == static_cast<size_t>(be::peek<uint32>(pLongTable + nGlyphId + 1)))
+                return -1;
         }
     }
 
     // only get here if glyph id was bad
-    return -1;
+    return res;
     //throw std::out_of_range("glyph id out of range for font");
 }
 
 /*----------------------------------------------------------------------------------------------
     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 >= nTableLen)
+        if (nGlyfOffset == size_t(-1) || nGlyfOffset == size_t(-2) || nGlyfOffset >= 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.
 ----------------------------------------------------------------------------------------------*/
--- a/gfx/graphite2/src/files.mk
+++ b/gfx/graphite2/src/files.mk
@@ -46,66 +46,76 @@
     $($(_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 \
     $($(_NS)_BASE)/src/Font.cpp \
     $($(_NS)_BASE)/src/GlyphCache.cpp \
     $($(_NS)_BASE)/src/GlyphFace.cpp \
+    $($(_NS)_BASE)/src/Intervals.cpp \
     $($(_NS)_BASE)/src/Justifier.cpp \
     $($(_NS)_BASE)/src/NameTable.cpp \
     $($(_NS)_BASE)/src/Pass.cpp \
+    $($(_NS)_BASE)/src/Position.cpp \
     $($(_NS)_BASE)/src/SegCache.cpp \
     $($(_NS)_BASE)/src/SegCacheEntry.cpp \
     $($(_NS)_BASE)/src/SegCacheStore.cpp \
     $($(_NS)_BASE)/src/Segment.cpp \
     $($(_NS)_BASE)/src/Silf.cpp \
     $($(_NS)_BASE)/src/Slot.cpp \
     $($(_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/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 \
     $($(_NS)_BASE)/src/inc/GlyphCache.h \
     $($(_NS)_BASE)/src/inc/GlyphFace.h \
+    $($(_NS)_BASE)/src/inc/Intervals.h \
     $($(_NS)_BASE)/src/inc/List.h \
     $($(_NS)_BASE)/src/inc/locale2lcid.h \
     $($(_NS)_BASE)/src/inc/Machine.h \
     $($(_NS)_BASE)/src/inc/Main.h \
     $($(_NS)_BASE)/src/inc/NameTable.h \
     $($(_NS)_BASE)/src/inc/opcode_table.h \
     $($(_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_face.cpp
+++ b/gfx/graphite2/src/gr_face.cpp
@@ -41,17 +41,17 @@ extern json *global_log;
 
 namespace
 {
     bool load_face(Face & face, unsigned int options)
     {
 #ifdef GRAPHITE2_TELEMETRY
         telemetry::category _misc_cat(face.tele.misc);
 #endif
-        Face::Table silf(face, Tag::Silf);
+        Face::Table silf(face, Tag::Silf, 0x00050000);
         if (silf)   options &= ~gr_face_dumbRendering;
         else if (!(options &  gr_face_dumbRendering))
             return false;
 
         if (!face.readGlyphs(options))
             return false;
 
         if (silf)
--- a/gfx/graphite2/src/gr_logging.cpp
+++ b/gfx/graphite2/src/gr_logging.cpp
@@ -27,16 +27,17 @@ of the License or (at your option) any l
 #include <stdio.h>
 
 #include "graphite2/Log.h"
 #include "inc/debug.h"
 #include "inc/CharInfo.h"
 #include "inc/Slot.h"
 #include "inc/Segment.h"
 #include "inc/json.h"
+#include "inc/Collider.h"
 
 #if defined _WIN32
 #include "windows.h"
 #endif
 
 using namespace graphite2;
 
 #if !defined GRAPHITE2_NTRACING
@@ -179,16 +180,17 @@ json & graphite2::operator << (json & j,
 
 
 json & graphite2::operator << (json & j, const dslot & ds) throw()
 {
     assert(ds.first);
     assert(ds.second);
     const Segment & seg = *ds.first;
     const Slot & s = *ds.second;
+    const SlotCollision *cslot = seg.collisionInfo(ds.second);
 
     j << json::object
         << "id"             << objectid(ds)
         << "gid"            << s.gid()
         << "charinfo" << json::flat << json::object
             << "original"       << s.original()
             << "before"         << s.before()
             << "after"          << s.after()
@@ -215,16 +217,38 @@ json & graphite2::operator << (json & j,
         j   << json::close;
     if (s.firstChild())
     {
         j   << "children" << json::flat << json::array;
         for (const Slot *c = s.firstChild(); c; c = c->nextSibling())
             j   << objectid(dslot(&seg, c));
         j       << json::close;
     }
+    if (cslot)
+    {
+		// Note: the reason for using Positions to lump together related attributes is to make the 
+		// JSON output slightly more compact.
+        j << "collision" << json::flat << json::object
+//              << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself
+              << "offset" << cslot->offset()
+              << "limit" << cslot->limit()
+              << "flags" << cslot->flags()
+              << "margin" << Position(cslot->margin(), cslot->marginWt())
+              << "exclude" << cslot->exclGlyph()
+              << "excludeoffset" << cslot->exclOffset();
+		if (cslot->seqOrder() != 0)
+		{
+			j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass())
+				<< "seqorder" << cslot->seqOrder()
+				<< "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt())
+				<< "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt())
+				<< "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt());
+		}
+        j << json::close;
+    }
     return j << json::close;
 }
 
 
 graphite2::objectid::objectid(const dslot & ds) throw()
 {
     const Slot * const p = ds.second;
     uint32 s = reinterpret_cast<size_t>(p);
--- a/gfx/graphite2/src/gr_segment.cpp
+++ b/gfx/graphite2/src/gr_segment.cpp
@@ -43,20 +43,16 @@ namespace
       Segment* pRes=new Segment(nChars, face, script, dir);
 
       
       if (!pRes->read_text(face, pFeats, enc, pStart, nChars) || !pRes->runGraphite())
       {
         delete pRes;
         return NULL;
       }
-      // run the line break passes
-      // run the substitution passes
-      pRes->prepare_pos(font);
-      // run the positioning passes
       pRes->finalise(font);
 
       return static_cast<gr_segment*>(pRes);
   }
 
 
 }
 
--- a/gfx/graphite2/src/gr_slot.cpp
+++ b/gfx/graphite2/src/gr_slot.cpp
@@ -98,21 +98,21 @@ float gr_slot_advance_X(const gr_slot* p
         if (face && font->isHinted())
             res = (res - face->glyphs().glyph(p->gid())->theAdvance().x) * scale + font->advance(p->gid());
         else
             res = res * scale;
     }
     return res;
 }
 
-float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, const gr_face *face, const gr_font *font)
+float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, GR_MAYBE_UNUSED const gr_face *face, const gr_font *font)
 {
     assert(p);
     float res = p->advancePos().y;
-    if (font && (face || !face))
+    if (font)
         return res * font->scale();
     else
         return res;
 }
         
 int gr_slot_before(const gr_slot* p/*not NULL*/)
 {
     assert(p);
--- a/gfx/graphite2/src/inc/Code.h
+++ b/gfx/graphite2/src/inc/Code.h
@@ -36,35 +36,45 @@ of the License or (at your option) any l
 #include "inc/Main.h"
 #include "inc/Machine.h"
 
 namespace graphite2 {
 
 class Silf;
 class Face;
 
+enum passtype {
+    PASS_TYPE_UNKNOWN = 0,
+    PASS_TYPE_LINEBREAK,
+    PASS_TYPE_SUBSTITUTE,
+    PASS_TYPE_POSITIONING,
+    PASS_TYPE_JUSTIFICATION
+};
+
 namespace vm {
 
 class Machine::Code
 {
 public:
     enum status_t 
     {
         loaded,
         alloc_failed, 
         invalid_opcode, 
         unimplemented_opcode_used,
         out_of_range_data,
         jump_past_end,
         arguments_exhausted,
         missing_return,
-        nested_context_item
+        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;
@@ -74,35 +84,38 @@ private:
     mutable bool _own;
 
     void release_buffers() throw ();
     void failure(const status_t) throw();
 
 public:
     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 &);
+         uint8 pre_context, uint16 rule_length, const Silf &, const Face &,
+         enum passtype pt, byte * & _out = local_memory);
     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();
+    void          externalProgramMoved(ptrdiff_t) throw();
 
     int32 run(Machine &m, slotref * & map) const;
     
     CLASS_NEW_DELETE;
 };
 
+
 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),
   _own(false)
 {
 }
 
 inline Machine::Code::Code(const Machine::Code &obj) throw ()
@@ -166,10 +179,19 @@ inline bool Machine::Code::deletes() con
   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;
+    }
+}
+
 } // namespace vm
 } // namespace graphite2
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/inc/Collider.h
@@ -0,0 +1,220 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, 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 <utility>
+#include "inc/List.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;
+class Slot;
+class Segment;
+
+#define SLOTCOLSETUINTPROP(x, y) uint16 x() const { return _ ##x; } void y (uint16 v) { _ ##x = v; }
+#define SLOTCOLSETINTPROP(x, y) int16 x() const { return _ ##x; } void y (int16 v) { _ ##x = v; }
+#define SLOTCOLSETPOSITIONPROP(x, y) const Position &x() const { return _ ##x; } void y (const Position &v) { _ ##x = v; }
+
+// Slot attributes related to collision-fixing
+class SlotCollision
+{
+public:
+    enum {
+    //  COLL_TESTONLY = 0,  // default - test other glyphs for collision with this one, but don't move this one
+        COLL_FIX = 1,       // fix collisions involving this glyph
+        COLL_IGNORE = 2,    // ignore this glyph altogether
+        COLL_START = 4,     // start of range of possible collisions
+        COLL_END = 8,       // end of range of possible collisions
+        COLL_KERN = 16,     // collisions with this glyph are fixed by adding kerning space after it
+        COLL_ISCOL = 32,    // this glyph has a collision
+        COLL_KNOWN = 64,    // we've figured out what's happening with this glyph
+        COLL_TEMPLOCK = 128,    // Lock glyphs that have been given priority positioning
+        ////COLL_JUMPABLE = 128,    // moving glyphs may jump this stationary glyph in any direction - DELETE
+        ////COLL_OVERLAP = 256,    // use maxoverlap to restrict - DELETE
+    };
+    
+    // Behavior for the collision.order attribute. To GDL this is an enum, to us it's a bitfield, with only 1 bit set
+    // Allows for easier inversion.
+    enum {
+        SEQ_ORDER_LEFTDOWN = 1,
+        SEQ_ORDER_RIGHTUP = 2,
+        SEQ_ORDER_NOABOVE = 4,
+        SEQ_ORDER_NOBELOW = 8,
+        SEQ_ORDER_NOLEFT = 16,
+        SEQ_ORDER_NORIGHT = 32
+    };
+    
+    SlotCollision(Segment *seg, Slot *slot);
+    void initFromSlot(Segment *seg, Slot *slot);
+    
+    const Rect &limit() const { return _limit; }
+    void setLimit(const Rect &r) { _limit = r; }
+    SLOTCOLSETPOSITIONPROP(shift, setShift)
+    SLOTCOLSETPOSITIONPROP(offset, setOffset)
+    SLOTCOLSETPOSITIONPROP(exclOffset, setExclOffset)
+    SLOTCOLSETUINTPROP(margin, setMargin)
+    SLOTCOLSETUINTPROP(marginWt, setMarginWt)
+    SLOTCOLSETUINTPROP(flags, setFlags)
+    SLOTCOLSETUINTPROP(exclGlyph, setExclGlyph)
+    SLOTCOLSETUINTPROP(seqClass, setSeqClass)
+    SLOTCOLSETUINTPROP(seqProxClass, setSeqProxClass)
+    SLOTCOLSETUINTPROP(seqOrder, setSeqOrder)
+    SLOTCOLSETINTPROP(seqAboveXoff, setSeqAboveXoff)
+    SLOTCOLSETUINTPROP(seqAboveWt, setSeqAboveWt)
+    SLOTCOLSETINTPROP(seqBelowXlim, setSeqBelowXlim)
+    SLOTCOLSETUINTPROP(seqBelowWt, setSeqBelowWt)
+    SLOTCOLSETUINTPROP(seqValignHt, setSeqValignHt)
+    SLOTCOLSETUINTPROP(seqValignWt, setSeqValignWt)
+
+    float getKern(int dir) const;
+    
+private:
+    Rect        _limit;
+    Position    _shift;     // adjustment within the given pass
+    Position    _offset;    // total adjustment for collisions
+    Position    _exclOffset;
+    uint16		_margin;
+    uint16		_marginWt;
+    uint16		_flags;
+    uint16		_exclGlyph;
+    uint16		_seqClass;
+	uint16		_seqProxClass;
+    uint16		_seqOrder;
+    int16		_seqAboveXoff;
+    uint16		_seqAboveWt;
+    int16		_seqBelowXlim;
+    uint16		_seqBelowWt;
+    uint16		_seqValignHt;
+    uint16		_seqValignWt;
+	
+};  // end of class SlotColllision
+
+class BBox;
+class 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() 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);
+    void addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int mode);
+    void removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int mode);
+    const Position &origin() const { return _origin; }
+
+#if !defined GRAPHITE2_NTRACING
+	void outputJsonDbg(json * const dbgout, Segment *seg, int axis);
+	void outputJsonDbgStartSlot(json * const dbgout, Segment *seg);
+	void outputJsonDbgEndSlot(json * const dbgout, Position resultPos, int bestAxis, bool isCol);
+	void outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, float tleft, float bestCost, float bestVal);
+	void outputJsonDbgRawRanges(json * const dbgout, int axis);
+	void outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg);
+#endif
+
+    CLASS_NEW_DELETE;
+
+protected:
+    Zones _ranges[4];   // possible movements in 4 directions (horizontally, vertically, diagonally);
+    Slot *  _target;    // the glyph to fix
+    Rect    _limit;
+    Position _currShift;
+    Position _currOffset;
+    Position _origin;   // Base for all relative calculations
+    float   _margin;
+	float	_marginWt;
+    float   _len[4];
+    uint16  _seqClass;
+	uint16	_seqProxClass;
+    uint16  _seqOrder;
+    
+	//bool _scraping[4];
+
+};	// end of class ShiftCollider
+
+class KernCollider
+{
+public:
+    KernCollider(GR_MAYBE_UNUSED json *dbg) : _miny(-1e38f), _maxy(1e38f) { };
+    ~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);
+
+    CLASS_NEW_DELETE;
+
+private:
+    Slot *  _target;        // the glyph to fix
+    Rect    _limit;
+    float   _margin;
+    Position _offsetPrev;   // kern from a previous pass
+    Position _currShift;    // NOT USED??
+    float _miny;	        // y-coordinates offset by global slot position
+    float _maxy;
+    Vector<float> _edges;   // edges of horizontal slices
+    float _sliceWidth;      // width of each slice
+    float _mingap;
+    float _xbound;        // max or min edge
+
+#if !defined GRAPHITE2_NTRACING    
+    // Debugging
+    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; }
+
+
+};  // end of namespace graphite2
+
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/inc/Decompressor.h
@@ -0,0 +1,52 @@
+/*  Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
+    Copyright (c) 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.
+*/
+#pragma once
+
+#include <cstddef>
+
+namespace shrinker
+{
+
+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/Error.h
+++ b/gfx/graphite2/src/inc/Error.h
@@ -106,22 +106,29 @@ enum errors {
     E_BADRULECCODEPTR = 45, // The rule constraint code position does not align with where the forward reference says it should be
     E_BADCCODELEN = 46,     // Bad rule/pass constraint code length
     E_BADACTIONCODEPTR = 47,    // The action code position does not align with where the forward reference says it should be
     E_MUTABLECCODE = 48,    // Constraint code edits slots. It shouldn't.
     E_BADSTATE = 49,        // Bad state transition referencing an illegal state
     E_BADRULEMAPPING = 50,  // The structure of the rule mapping is bad
     E_BADRANGE = 51,        // Bad column range structure including a glyph in more than one column
     E_BADRULENUM = 52,      // A reference to a rule is out of range (too high)
+    E_BADACOLLISION = 53,   // Bad Silf table collision attribute number (too high)
+    E_BADEMPTYPASS = 54,    // Can't have empty passes (no rules) except for collision passes
+    E_BADSILFVERSION = 55,  // The Silf table has a bad version (probably too high)
+    E_BADCOLLISIONPASS = 56,    // Collision flags set on a non positioning pass
 // Code errors
     E_CODEFAILURE = 60,     // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h
-        E_CODEALLOC = 61,       // Out of memory
-        E_INVALIDOPCODE = 62,   // Invalid op code
-        E_UNIMPOPCODE = 63,     // Unimplemented op code encountered
-        E_OUTOFRANGECODE = 64,  // Code argument out of range
-        E_BADJUMPCODE = 65,     // Code jumps past end of op codes
-        E_CODEBADARGS = 66,     // Code arguments exhausted
-        E_CODENORETURN = 67,    // Missing return type op code at end of code
-        E_CODENESTEDCTXT = 68   // Nested context encountered in code
+    E_CODEALLOC = 61,       // Out of memory
+    E_INVALIDOPCODE = 62,   // Invalid op code
+    E_UNIMPOPCODE = 63,     // Unimplemented op code encountered
+    E_OUTOFRANGECODE = 64,  // Code argument out of range
+    E_BADJUMPCODE = 65,     // Code jumps past end of op codes
+    E_CODEBADARGS = 66,     // Code arguments exhausted
+    E_CODENORETURN = 67,    // Missing return type op code at end of code
+    E_CODENESTEDCTXT = 68,   // Nested context encountered in code
+// Compression errors
+    E_BADSCHEME = 69,
+    E_SHRINKERFAILED = 70,
 };
 
 }
 
--- a/gfx/graphite2/src/inc/Face.h
+++ b/gfx/graphite2/src/inc/Face.h
@@ -165,46 +165,51 @@ json * Face::logger() const throw()
 
 
 
 class Face::Table
 {
     const Face *            _f;
     mutable const byte *    _p;
     uint32                  _sz;
+    bool                    _compressed;
+
+    Error decompress();
 
 public:
     Table() throw();
-    Table(const Face & face, const Tag n) throw();
+    Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw();
     Table(const Table & rhs) throw();
     ~Table() throw();
 
     operator const byte * () const throw();
 
     Table & operator = (const Table & rhs) throw();
     size_t  size() const throw();
 };
 
 inline
 Face::Table::Table() throw()
-: _f(0), _p(0), _sz(0)
+: _f(0), _p(0), _sz(0), _compressed(false)
 {
 }
 
 inline
 Face::Table::Table(const Table & rhs) throw()
-: _f(rhs._f), _p(rhs._p), _sz(rhs._sz)
+: _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed)
 {
     rhs._p = 0;
 }
 
 inline
 Face::Table::~Table() throw()
 {
-    if (_p && _f->m_ops.release_table)
+    if (_compressed)
+        free(const_cast<byte *>(_p));
+    else if (_p && _f->m_ops.release_table)
         (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
 }
 
 inline
 Face::Table::operator const byte * () const throw()
 {
     return _p;
 }
--- a/gfx/graphite2/src/inc/FeatureMap.h
+++ b/gfx/graphite2/src/inc/FeatureMap.h
@@ -112,35 +112,34 @@ class NameAndFeatureRef
  
     uint32 m_name;
     const FeatureRef* m_pFRef;
 };
 
 class FeatureMap
 {
 public:
-    FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL),
-        m_defaultFeatures(NULL) {}
-    ~FeatureMap() { delete [] m_feats; delete[] m_pNamedFeats; delete m_defaultFeatures; }
+    FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL) {}
+    ~FeatureMap() { delete [] m_feats; delete[] m_pNamedFeats; }
 
     bool readFeats(const Face & face);
     const FeatureRef *findFeatureRef(uint32 name) const;
     FeatureRef *feature(uint16 index) const { return m_feats + index; }
     //GrFeatureRef *featureRef(byte index) { return index < m_numFeats ? m_feats + index : NULL; }
     const FeatureRef *featureRef(byte index) const { return index < m_numFeats ? m_feats + index : NULL; }
     FeatureVal* cloneFeatures(uint32 langname/*0 means default*/) const;      //call destroy_Features when done.
     uint16 numFeats() const { return m_numFeats; };
     CLASS_NEW_DELETE
 private:
 friend class SillMap;
     uint16 m_numFeats;
 
     FeatureRef *m_feats;
     NameAndFeatureRef* m_pNamedFeats;   //owned
-    FeatureVal* m_defaultFeatures;        //owned
+    FeatureVal m_defaultFeatures;        //owned
     
 private:        //defensive on m_feats, m_pNamedFeats, and m_defaultFeatures
     FeatureMap(const FeatureMap&);
     FeatureMap& operator=(const FeatureMap&);
 };
 
 
 class SillMap
--- a/gfx/graphite2/src/inc/GlyphCache.h
+++ b/gfx/graphite2/src/inc/GlyphCache.h
@@ -24,47 +24,110 @@ Mozilla Public License (http://mozilla.o
 License, as published by the Free Software Foundation, either version 2
 of the License or (at your option) any later version.
 */
 #pragma once
 
 
 #include "graphite2/Font.h"
 #include "inc/Main.h"
+#include "inc/Position.h"
+#include "inc/GlyphFace.h"
 
 namespace graphite2 {
 
 class Face;
 class FeatureVal;
-class GlyphFace;
 class Segment;
 
+
+class SlantBox
+{
+public:
+    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
+{
+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) {}; 
+
+    void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; }
+    Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; }
+    const Rect &slant() const { return _slant; }
+    uint8 num() const { return _num; }
+    const Rect *subs() const { return _subs; }
+
+private:
+    uint8   _num;
+    unsigned short  _bitmap;
+    Rect    _slant;
+    Rect    _subs[1];
+};
+
 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 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 Loader        * _glyph_loader;
     const GlyphFace *   * _glyphs;
+    GlyphBox        *   * _boxes;
     unsigned short        _num_glyphs,
                           _num_attrs,
                           _upem;
 };
 
 inline
 unsigned short GlyphCache::numGlyphs() const throw()
 {
@@ -79,14 +142,86 @@ unsigned short GlyphCache::numAttrs() co
 
 inline
 unsigned short  GlyphCache::unitsPerEm() const throw()
 {
     return _upem;
 }
 
 inline
+bool GlyphCache::check(unsigned short glyphid) const
+{
+    return glyphid < _num_glyphs;
+}
+
+inline
 const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const
 {
     return glyphid < _num_glyphs ? glyph(glyphid) : NULL;
 }
 
+inline
+float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const
+{
+    if (glyphid >= _num_glyphs) return 0.;
+    switch (metric) {
+        case 0: return (float)(glyph(glyphid)->theBBox().bl.x);                          // x_min
+        case 1: return (float)(glyph(glyphid)->theBBox().bl.y);                          // y_min
+        case 2: return (float)(glyph(glyphid)->theBBox().tr.x);                          // x_max
+        case 3: return (float)(glyph(glyphid)->theBBox().tr.y);                          // y_max
+        case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f);    // sum_min
+        case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f);    // diff_min
+        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;
+}
+
+inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
+{
+    return *(BBox *)(&(glyph(glyphid)->theBBox()));
+}
+
+inline
+float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const
+{
+    GlyphBox *b = _boxes[glyphid];
+    if (b == NULL || subindex >= b->num()) return 0;
+
+    switch (metric) {
+        case 0: return b->subVal(subindex, 0).bl.x;
+        case 1: return b->subVal(subindex, 0).bl.y;
+        case 2: return b->subVal(subindex, 0).tr.x;
+        case 3: return b->subVal(subindex, 0).tr.y;
+        case 4: return b->subVal(subindex, 1).bl.x;
+        case 5: return b->subVal(subindex, 1).bl.y;
+        case 6: return b->subVal(subindex, 1).tr.x;
+        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;
+}
+
 } // namespace graphite2
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/inc/Intervals.h
@@ -0,0 +1,231 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, 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 <utility>
+
+#include "inc/Main.h"
+#include "inc/List.h"
+#include "inc/json.h"
+#include "inc/Position.h"
+
+// An IntervalSet represents the possible movement of a given glyph in a given direction
+// (horizontally, vertically, or diagonally).
+// A vector is needed to represent disjoint ranges, eg, -300..-150, 20..200, 500..750.
+// Each pair represents the min/max of a sub-range.
+
+namespace graphite2 {
+
+class Segment;
+
+enum zones_t {SD, XY};
+
+class Zones
+{
+    struct Exclusion
+    {
+        template<zones_t O>
+        static Exclusion weighted(float xmin, float xmax, float f, float a0,
+                float m, float xi, float ai, float c, bool nega);
+
+        float   x,  // x position
+                xm, // xmax position
+                c,  // constant + sum(MiXi^2)
+                sm, // sum(Mi)
+                smx; // sum(MiXi)
+        bool    open;
+
+        Exclusion(float x, float w, float smi, float smxi, float c);
+        Exclusion & operator += (Exclusion const & rhs);
+        uint8 outcode(float p) const;
+
+        Exclusion   split_at(float p);
+        void        left_trim(float p);
+
+        bool        track_cost(float & cost, float & x, float origin) const;
+
+    private:
+        float test_position(float origin) const;
+        float cost(float x) const;
+     };
+
+    typedef Vector<Exclusion>                   exclusions;
+
+    typedef exclusions::iterator                iterator;
+    typedef Exclusion *                         pointer;
+    typedef Exclusion &                         reference;
+    typedef std::reverse_iterator<iterator>     reverse_iterator;
+
+public:
+    typedef exclusions::const_iterator              const_iterator;
+    typedef Exclusion const *                       const_pointer;
+    typedef Exclusion const &                       const_reference;
+    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;
+
+#if !defined GRAPHITE2_NTRACING
+    struct Debug
+    {
+        Exclusion       _excl;
+        bool            _isdel;
+        Vector<void *>  _env;
+
+        Debug(Exclusion *e, bool isdel, json *dbg) : _excl(*e), _isdel(isdel), _env(dbg->getenvs()) { };
+    };
+
+    typedef Vector<Debug>                       debugs;
+    typedef debugs::const_iterator                    idebugs;
+    void addDebug(Exclusion *e);
+    void removeDebug(float pos, float posm);
+    void setdebug(json *dbgout) { _dbg = dbgout; }
+    idebugs dbgs_begin() const { return _dbgs.begin(); }
+    idebugs dbgs_end() const { return _dbgs.end(); }
+    void jsonDbgOut(Segment *seg) const;
+    Position position() const { return Position(_pos, _posm); }
+#endif
+
+    Zones();
+    template<zones_t O>
+    void initialise(float xmin, float xmax, float margin_len, float margin_weight, float ao);
+
+    void exclude(float xmin, float xmax);
+    void exclude_with_margins(float xmin, float xmax, int axis);
+
+    template<zones_t O>
+    void weighted(float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
+    void weightedAxis(int axis, float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
+
+    float closest( float origin, float &cost) const;
+
+    const_iterator begin() const { return _exclusions.begin(); }
+    const_iterator end() const { return _exclusions.end(); }
+
+private:
+    exclusions  _exclusions;
+#if !defined GRAPHITE2_NTRACING
+    json      * _dbg;
+    debugs      _dbgs;
+#endif
+    float       _margin_len,
+                _margin_weight,
+                _pos,
+                _posm;
+
+    void            insert(Exclusion e);
+    void            remove(float x, float xm);
+    const_iterator  find_exclusion_under(float x) const;
+};
+
+
+inline
+Zones::Zones()
+: _margin_len(0), _margin_weight(0), _pos(0), _posm(0)
+{
+    _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)
+{ }
+
+template<zones_t O>
+inline
+void Zones::initialise(float xmin, float xmax, float margin_len,
+        float margin_weight, float a0)
+{
+    _margin_len = margin_len;
+    _margin_weight = margin_weight;
+    _pos = xmin;
+    _posm = xmax;
+    _exclusions.clear();
+    _exclusions.push_back(Exclusion::weighted<O>(xmin, xmax, 1, a0, 0, 0, 0, 0, false));
+    _exclusions.front().open = true;
+#if !defined GRAPHITE2_NTRACING
+    _dbgs.clear();
+#endif
+}
+
+inline
+void Zones::exclude(float xmin, float xmax) {
+    remove(xmin, xmax);
+}
+
+template<zones_t O>
+inline
+void Zones::weighted(float xmin, float xmax, float f, float a0,
+        float m, float xi, float ai, float c, bool nega) {
+    insert(Exclusion::weighted<O>(xmin, xmax, f, a0, m, xi, ai, c, nega));
+}
+
+inline
+void Zones::weightedAxis(int axis, float xmin, float xmax, float f, float a0,
+        float m, float xi, float ai, float c, bool nega) {
+    if (axis < 2)
+        weighted<XY>(xmin, xmax, f, a0, m, xi, ai, c, nega);
+    else
+        weighted<SD>(xmin, xmax, f, a0, m, xi, ai, c, nega);
+}
+
+#if !defined GRAPHITE2_NTRACING
+inline
+void Zones::addDebug(Exclusion *e) {
+    if (_dbg)
+        _dbgs.push_back(Debug(e, false, _dbg));
+}
+
+inline
+void Zones::removeDebug(float pos, float posm) {
+    if (_dbg)
+    {
+        Exclusion e(pos, posm, 0, 0, 0);
+        _dbgs.push_back(Debug(&e, true, _dbg));
+    }
+}
+#endif
+
+template<>
+inline
+Zones::Exclusion Zones::Exclusion::weighted<XY>(float xmin, float xmax, float f, float a0,
+        float m, float xi, GR_MAYBE_UNUSED float ai, float c, GR_MAYBE_UNUSED bool nega) {
+    return Exclusion(xmin, xmax,
+            m + f,
+            m * xi, 
+            m * xi * xi + f * a0 * a0 + c);
+}
+
+template<>
+inline
+Zones::Exclusion Zones::Exclusion::weighted<SD>(float xmin, float xmax, float f, float a0,
+        float m, float xi, float ai,float c, bool nega) {
+    float xia = nega ? xi - ai : xi + ai;
+    return Exclusion(xmin, xmax, 
+            0.25f * (m + 2.f * f), 
+            0.25f * m * xia, 
+            0.25f * (m * xia * xia + 2.f * f * a0 * a0) + c);
+}
+
+} // end of namespace graphite2
--- a/gfx/graphite2/src/inc/List.h
+++ b/gfx/graphite2/src/inc/List.h
@@ -65,29 +65,30 @@ public:
     iterator            end()           { return m_last; }
     const_iterator      end() const     { return m_last; }
     
     bool                empty() const   { return m_first == m_last; }
     size_t              size() const    { return m_last - m_first; }
     size_t              capacity() const{ return m_end - m_first; }
     
     void                reserve(size_t n);
+    void                resize(size_t n, const T & v = T());
     
     reference           front()         { assert(size() > 0); return *begin(); }
     const_reference     front() const   { assert(size() > 0); return *begin(); }
     reference           back()          { assert(size() > 0); return *(end()-1); }
     const_reference     back() const    { assert(size() > 0); return *(end()-1); }
     
     Vector<T>         & operator = (const Vector<T> & rhs) { assign(rhs.begin(), rhs.end()); return *this; }
     reference           operator [] (size_t n)          { assert(size() > n); return m_first[n]; }
     const_reference     operator [] (size_t n) const    { assert(size() > n); return m_first[n]; }
     
     void                assign(size_t n, const T& u)    { clear(); insert(begin(), n, u); }
     void                assign(const_iterator first, const_iterator last)      { clear(); insert(begin(), first, last); }
-    iterator            insert(iterator p, const T & x) { p = _insert_default(p, 1); *p = x; return p; }
+    iterator            insert(iterator p, const T & x) { p = _insert_default(p, 1); new (p) T(x); return p; }
     void                insert(iterator p, size_t n, const T & x);
     void                insert(iterator p, const_iterator first, const_iterator last);
     void                pop_back()              { assert(size() > 0); --m_last; }
     void                push_back(const T &v)   { if (m_last == m_end) reserve(size()+1); new (m_last++) T(v); }
 
     void                clear()                 { erase(begin(), end()); }
     iterator            erase(iterator p)       { return erase(p, p+1); }
     iterator            erase(iterator first, iterator last);
@@ -104,23 +105,31 @@ void Vector<T>::reserve(size_t n)
     {
         const ptrdiff_t sz = size();
         m_first = static_cast<T*>(realloc(m_first, n*sizeof(T)));
         m_last  = m_first + sz;
         m_end   = m_first + n;
     }
 }
 
+template <typename T>
+inline
+void Vector<T>::resize(size_t n, const T & v) {
+    const ptrdiff_t d = n-size();
+    if (d < 0)      erase(end()+d, end());
+    else if (d > 0) insert(end(), d, v);
+}
+
 template<typename T> 
 inline 
 typename Vector<T>::iterator Vector<T>::_insert_default(iterator p, size_t n)
 {
     assert(begin() <= p && p <= end());
     const ptrdiff_t i = p - begin();
-    reserve((size() + n + 7) >> 3 << 3);
+    reserve(((size() + n + 7) >> 3) << 3);
     p = begin() + i;
     // Move tail if there is one
     if (p != end()) memmove(p + n, p, distance(p,end())*sizeof(T));
     m_last += n;
     return p;
 }
 
 template<typename T> 
--- a/gfx/graphite2/src/inc/Machine.h
+++ b/gfx/graphite2/src/inc/Machine.h
@@ -105,17 +105,19 @@ enum opcode {
 
     PUSH_IGLYPH_ATTR,    // not implemented
 
     POP_RET,                        RET_ZERO,           RET_TRUE,
     IATTR_SET,                      IATTR_ADD,          IATTR_SUB,
     PUSH_PROC_STATE,                PUSH_VERSION,
     PUT_SUBS,                       PUT_SUBS2,          PUT_SUBS3,
     PUT_GLYPH,                      PUSH_GLYPH_ATTR,    PUSH_ATT_TO_GLYPH_ATTR,
-    MAX_OPCODE,
+    BITOR,                          BITAND,             BITNOT,
+    BITSET,                         SET_FEAT,
+    MAX_OPCODE,                     
     // private opcodes for internal use only, comes after all other on disk opcodes
     TEMP_COPY = MAX_OPCODE
 };
 
 struct opcode_t 
 {
     instr           impl[2];
     uint8           param_sz;
@@ -143,17 +145,17 @@ public:
 
     Machine(SlotMap &) throw();
     static const opcode_t *   getOpcodeTable() throw();
 
     CLASS_NEW_DELETE;
 
     SlotMap   & slotMap() const throw();
     status_t    status() const throw();
-    operator bool () const throw();
+//    operator bool () const throw();
 
 private:
     void    check_final_stack(const stack_t * const sp);
     stack_t run(const instr * program, const byte * data,
                 slotref * & map) HOT;
 
     SlotMap       & _map;
     stack_t         _stack[STACK_MAX + 2*STACK_GUARD];
--- a/gfx/graphite2/src/inc/Main.h
+++ b/gfx/graphite2/src/inc/Main.h
@@ -120,8 +120,13 @@ inline T max(const T a, const T b)
     void operator delete[] (void * p)throw() { free(p); } \
     void operator delete[] (void *, void *) throw() {}
 
 #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)
+#endif
--- a/gfx/graphite2/src/inc/Pass.h
+++ b/gfx/graphite2/src/inc/Pass.h
@@ -34,65 +34,82 @@ namespace graphite2 {
 class Segment;
 class Face;
 class Silf;
 struct Rule;
 struct RuleEntry;
 struct State;
 class FiniteStateMachine;
 class Error;
+class ShiftCollider;
+class KernCollider;
+class json;
+
+enum passtype;
 
 class Pass
 {   
 public:
     Pass();
     ~Pass();
     
-    bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face, Error &e);
-    void runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const;
+    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;
     void init(Silf *silf) { m_silf = silf; }
-    byte spaceContextuals() const { return (m_flags & 0x0E) >> 1; }
+    byte flags() const { return m_flags; }
 
     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,
                      const byte *precontext, const uint16 * sort_key,
                      const uint16 * o_constraint, const byte *constraint_data, 
                      const uint16 * o_action, const byte * action_data,
-                     Face &, Error &e);
+                     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    adjustSlot(int delta, Slot * & slot_out, SlotMap &) const;
-    const Silf* m_silf;
-    uint16    * m_cols;
-    Rule      * m_rules; // rules
-    RuleEntry * m_ruleMap;
-    uint16    * m_startStates; // prectxt length
-    uint16    * m_transitions;
-    State     * m_states;
-    
+    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;
+
+    const Silf        * m_silf;
+    uint16            * m_cols;
+    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_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;
     vm::Machine::Code m_cPConstraint;
     
 private:        //defensive
     Pass(const Pass&);
     Pass& operator=(const Pass&);
 };
 
 } // namespace graphite2
--- a/gfx/graphite2/src/inc/Position.h
+++ b/gfx/graphite2/src/inc/Position.h
@@ -45,15 +45,24 @@ public:
 
 class Rect
 {
 public :
     Rect() {}
     Rect(const Position& botLeft, const Position& topRight): bl(botLeft), tr(topRight) {}
     Rect widen(const Rect& other) { return Rect(Position(bl.x > other.bl.x ? other.bl.x : bl.x, bl.y > other.bl.y ? other.bl.y : bl.y), Position(tr.x > other.tr.x ? tr.x : other.tr.x, tr.y > other.tr.y ? tr.y : other.tr.y)); }
     Rect operator + (const Position &a) const { return Rect(Position(bl.x + a.x, bl.y + a.y), Position(tr.x + a.x, tr.y + a.y)); }
+    Rect operator - (const Position &a) const { return Rect(Position(bl.x - a.x, bl.y - a.y), Position(tr.x - a.x, tr.y - a.y)); }
     Rect operator * (float m) const { return Rect(Position(bl.x, bl.y) * m, Position(tr.x, tr.y) * m); }
+    float width() const { return tr.x - bl.x; }
+    float height() const { return tr.y - bl.y; }
+
+    bool hitTest(Rect &other);
+
+    // returns Position(overlapx, overlapy) where overlap<0 if overlapping else positive)
+    Position overlap(Position &offset, Rect &other, Position &otherOffset);
+    //Position constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox);
 
     Position bl;
     Position tr;
 };
 
 } // namespace graphite2
--- a/gfx/graphite2/src/inc/Rule.h
+++ b/gfx/graphite2/src/inc/Rule.h
@@ -48,18 +48,16 @@ struct Rule {
 
 private:
   Rule(const Rule &);
   Rule & operator = (const Rule &);
 };
 
 inline Rule::~Rule()
 {
-  delete constraint;
-  delete action;
 }
 
 
 struct RuleEntry
 {
   const Rule   * rule;
 
   inline
--- a/gfx/graphite2/src/inc/Segment.h
+++ b/gfx/graphite2/src/inc/Segment.h
@@ -35,16 +35,17 @@ of the License or (at your option) any l
 #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/Collider.h"
 
 #define MAX_SEG_GROWTH_FACTOR  256
 
 namespace graphite2 {
 
 typedef Vector<Features>        FeatureList;
 typedef Vector<Slot *>          SlotRope;
 typedef Vector<int16 *>        AttributeRope;
@@ -81,16 +82,21 @@ private:
 
 class Segment
 {
     // Prevent copying of any kind.
     Segment(const Segment&);
     Segment& operator=(const Segment&);
 
 public:
+
+    enum {
+        SEG_INITCOLLISIONS = 1
+    };
+
     unsigned int slotCount() const { return m_numGlyphs; }      //one slot per glyph
     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; }
@@ -102,32 +108,41 @@ public:
 #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,
             Slot * endSlot, const Slot * srcSlot,
             const size_t numGlyphs);
 #endif
+    uint8 flags() const { return m_flags; }
+    void flags(uint8 f) { m_flags = f; }
     Slot *first() { return m_first; }
     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, Slot *first=0, Slot *last=0);
+    Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, 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]);
+        } }
     void dir(int8 val) { m_dir = val; }
     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;
     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; }
@@ -136,55 +151,59 @@ public:
     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);
     Slot *addLineEnd(Slot *nSlot);
     void delLineEnd(Slot *s);
     bool hasJustification() const { return m_justifies.size() != 0; }
 
     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 prepare_pos(const Font *font);
     void finalise(const Font *font);
     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
     FeatureList     m_feats;            // feature settings referenced by charinfos in this segment
     Slot          * m_freeSlots;        // linked list of free slots
     SlotJustify   * m_freeJustifies;    // Slot justification blocks free list
     CharInfo      * m_charinfo;         // character info, one per input character
+    SlotCollision * m_collisions;       // Array of SlotCollisions for each slot
     const Face    * m_face;             // GrFace
     const Silf    * m_silf;
     Slot          * m_first;            // first slot in segment
     Slot          * m_last;             // last slot in segment
     unsigned int    m_bufSize,          // how big a buffer to create when need more slots
                     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
 void Segment::finalise(const Font *font)
 {
     if (!m_first) return;
 
     m_advance = positionSlots(font);
-    associateChars(0, m_numCharinfo);
+    //associateChars(0, m_numCharinfo);
     linkClusters(m_first, m_last);
 }
 
 inline
 int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const {
     if (attrLevel > 0)
     {
         Slot *is = findRoot(iSlot);
new file mode 100644
--- /dev/null
+++ b/gfx/graphite2/src/inc/Shrinker.h
@@ -0,0 +1,119 @@
+/*  Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
+    Copyright (c) 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.
+*/
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+#include <iterator>
+
+//the code from LZ4
+#if (GCC_VERSION >= 302) || (__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
+u8 * memcpy_nooverlap(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 * memcpy_nooverlap_surpass(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 * memcpy_(u8 * d, u8 const * s, size_t n) {
+    if (likely(d>s+sizeof(unsigned long)))
+        return memcpy_nooverlap(d,s,n);
+    else while (n--) *d++ = *s++;
+    return d;
+}
+
+} // end of anonymous namespace
+
+
--- a/gfx/graphite2/src/inc/Silf.h
+++ b/gfx/graphite2/src/inc/Silf.h
@@ -80,16 +80,17 @@ public:
     uint16 getClassGlyph(uint16 cid, unsigned int index) const;
     uint16 findPseudo(uint32 uid) const;
     uint8 numUser() const { return m_aUser; }
     uint8 aPseudo() const { return m_aPseudo; }
     uint8 aBreak() const { return m_aBreak; }
     uint8 aMirror() const {return m_aMirror; }
     uint8 aPassBits() const { return m_aPassBits; }
     uint8 aBidi() const { return m_aBidi; }
+    uint8 aCollision() const { return m_aCollision; }
     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; }
@@ -109,21 +110,18 @@ private:
     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;
 
-    uint8   m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
-            m_iMaxComp;
-    uint16  m_aLig,
-            m_numPseudo,
-            m_nClass,
-            m_nLinear,
-            m_gEndLine;
+    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();
 };
 
 } // namespace graphite2
--- a/gfx/graphite2/src/inc/Slot.h
+++ b/gfx/graphite2/src/inc/Slot.h
@@ -70,16 +70,17 @@ class Slot
     };
 
 public:
     struct iterator;
 
     unsigned short gid() const { return m_glyphid; }
     Position origin() const { return m_position; }
     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();
     void set(const Slot & slot, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars);
@@ -93,17 +94,17 @@ public:
     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);
+    Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, 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; }
@@ -117,26 +118,30 @@ public:
     int getAttr(const Segment *seg, attrCode ind, uint8 subindex) const;
     int getJustify(const Segment *seg, uint8 level, uint8 subindex) const;
     void setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value);
     bool isLocalJustify() const { return m_justs != NULL; };
     void attachTo(Slot *ap) { m_parent = ap; }
     Slot *attachedTo() const { return m_parent; }
     Position attachOffset() const { return m_attach - m_with; }
     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);
     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
 
 private:
     Slot *m_next;           // linked list of slots
     Slot *m_prev;
     unsigned short m_glyphid;        // glyph id
     uint16 m_realglyphid;
--- 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);
+    bool CheckCmapSubtable4(const void * pCmap31 /*, 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);
+    bool CheckCmapSubtable12(const void *pCmap310 /*, 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/bits.h
+++ b/gfx/graphite2/src/inc/bits.h
@@ -24,25 +24,73 @@ Mozilla Public License (http://mozilla.o
 License, as published by the Free Software Foundation, either version 2
 of the License or (at your option) any later version.
 */
 #pragma once
 
 namespace graphite2
 {
 
+
+#if defined GRAPHITE2_BUILTINS && (defined __GNUC__ || defined __clang__)
+
 template<typename T>
 inline unsigned int bit_set_count(T v)
 {
-    v = v - ((v >> 1) & T(~T(0)/3));                           // temp
-    v = (v & T(~T(0)/15*3)) + ((v >> 2) & T(~T(0)/15*3));      // temp
-    v = (v + (v >> 4)) & T(~T(0)/255*15);                      // temp
-    return (T)(v * T(~T(0)/255)) >> (sizeof(T)-1)*8;           // count
+    return __builtin_popcount(v);
+}
+
+template<>
+inline unsigned int bit_set_count(int16 v)
+{
+    return __builtin_popcount(static_cast<uint16>(v));
+}
+
+template<>
+inline unsigned int bit_set_count(int8 v)
+{
+    return __builtin_popcount(static_cast<uint8>(v));
+}
+
+template<>
+inline unsigned int bit_set_count(unsigned long v)
+{
+    return __builtin_popcountl(v);
 }
 
+template<>
+inline unsigned int bit_set_count(signed long v)
+{
+    return __builtin_popcountl(v);
+}
+
+template<>
+inline unsigned int bit_set_count(unsigned long long v)
+{
+    return __builtin_popcountll(v);
+}
+
+template<>
+inline unsigned int bit_set_count(signed long long v)
+{
+    return __builtin_popcountll(v);
+}
+#else
+
+template<typename T>
+inline unsigned int bit_set_count(T v)
+{
+    v = v - ((v >> 1) & T(~(0UL)/3));                           // temp
+    v = (v & T(~(0UL)/15*3)) + ((v >> 2) & T(~(0UL)/15*3));     // temp
+    v = (v + (v >> 4)) & T(~(0UL)/255*15);                      // temp
+    return (T)(v * T(~(0UL)/255)) >> (sizeof(T)-1)*8;           // count
+}
+
+#endif
+
 
 template<int S>
 inline unsigned long _mask_over_val(unsigned long v)
 {
     v = _mask_over_val<S/2>(v);
     v |= v >> S*4;
     return v;
 }
@@ -82,9 +130,17 @@ inline T has_zero(const T x)
 
 template<typename T>
 inline T zero_bytes(const T x, unsigned char n)
 {
     const T t = T(~T(0)/255*n);
     return T((has_zero(x^t) >> 7)*n);
 }
 
+#if 0
+inline float float_round(float x, uint32 m)
+{
+    *reinterpret_cast<unsigned int *>(&x) &= m;
+    return *reinterpret_cast<float *>(&x);
 }
+#endif
+
+}
--- a/gfx/graphite2/src/inc/debug.h
+++ b/gfx/graphite2/src/inc/debug.h
@@ -49,31 +49,39 @@ struct objectid
 {
     char name[16];
     objectid(const dslot &) throw();
     objectid(const Segment * const p) throw();
 };
 
 
 json & operator << (json & j, const Position &) throw();
+json & operator << (json & j, const Rect &) throw();
 json & operator << (json & j, const CharInfo &) throw();
 json & operator << (json & j, const dslot &) throw();
 json & operator << (json & j, const objectid &) throw();
 json & operator << (json & j, const telemetry &) throw();
 
 
 
 inline
 json & operator << (json & j, const Position & p) throw()
 {
     return j << json::flat << json::array << p.x << p.y << json::close;
 }
 
 
 inline
+json & operator << (json & j, const Rect & p) throw()
+{
+    return j << json::flat << json::array << p.bl.x << p.bl.y << p.tr.x << p.tr.y << json::close;
+}
+
+
+inline
 json & operator << (json & j, const objectid & sid) throw()
 {
     return j << sid.name;
 }
 
 
 } // namespace graphite2
 
--- a/gfx/graphite2/src/inc/json.h
+++ b/gfx/graphite2/src/inc/json.h
@@ -24,19 +24,21 @@ Mozilla Public License (http://mozilla.o
 License, as published by the Free Software Foundation, either version 2
 of the License or (at your option) any later version.
 */
 // JSON pretty printer for graphite font debug output logging.
 // Created on: 15 Dec 2011
 //     Author: Tim Eves
 
 #pragma once
+
 #include "inc/Main.h"
 #include <cassert>
 #include <stdio.h>
+#include "inc/List.h"
 
 namespace graphite2 {
 
 class json
 {
     // Prevent copying
     json(const json &);
     json & operator = (const json &);
@@ -44,31 +46,36 @@ class json
     typedef void (*_context_t)(json &);
     class _null_t {};
 
     FILE * const    _stream;
     char            _contexts[128], // context stack
                   * _context,       // current context (top of stack)
                   * _flatten;       // if !0 points to context above which
                                     //  pretty printed output should occur.
+    Vector<void *>  _env;
 
     void context(const char current) throw();
     void indent(const int d=0) throw();
     void push_context(const char, const char) throw();
     void pop_context() throw();
 
 public:
     class closer;
 
     typedef const char *    string;
     typedef double          number;
     typedef long signed int integer;
     typedef bool            boolean;
     static const _null_t    null;
 
+    void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; }
+    void *getenv(unsigned int index) const { return _env[index]; }
+    const Vector<void *> &getenvs() const { return _env; }
+
     static void flat(json &) throw();
     static void close(json &) throw();
     static void object(json &) throw();
     static void array(json &) throw();
     static void item(json &) throw();
 
     json(FILE * stream) throw();
     ~json() throw ();
--- a/gfx/graphite2/src/inc/opcode_table.h
+++ b/gfx/graphite2/src/inc/opcode_table.h
@@ -38,17 +38,17 @@ of the License or (at your option) any l
 //      sattrnum - 0 .. 29 (gr_slatJWidth) , 55 (gr_slatUserDefn)
 //      attrid - 0 .. silf.numUser() where sattrnum == 55; 0..silf.m_iMaxComp where sattrnum == 15 otherwise 0
 //      gattrnum - 0 .. face->getGlyphFaceCache->numAttrs()
 //      gmetric - 0 .. 11 (kgmetDescent)
 //      featidx - 0 .. face.numFeatures()
 //      level - any byte
 static const opcode_t opcode_table[] = 
 {
-    {{do2(nop)},                                    0,  "NOP"},
+    {{do2(nop)},                                    0, "NOP"},
 
     {{do2(push_byte)},                              1, "PUSH_BYTE"},                // number
     {{do2(push_byte_u)},                            1, "PUSH_BYTE_U"},              // number
     {{do2(push_short)},                             2, "PUSH_SHORT"},               // number number
     {{do2(push_short_u)},                           2, "PUSH_SHORT_U"},             // number number
     {{do2(push_long)},                              4, "PUSH_LONG"},                // number number number number
 
     {{do2(add)},                                    0, "ADD"},
@@ -109,12 +109,17 @@ static const opcode_t opcode_table[] =
     {{do2(push_proc_state)},                        1, "PUSH_PROC_STATE"},          // dummy
     {{do2(push_version)},                           0, "PUSH_VERSION"},
     {{do_(put_subs), NILOP},                        5, "PUT_SUBS"},                 // slot input_class input_class output_class output_class
     {{NILOP,NILOP},                                 0, "PUT_SUBS2"},
     {{NILOP,NILOP},                                 0, "PUT_SUBS3"},
     {{do_(put_glyph), NILOP},                       2, "PUT_GLYPH"},                // output_class output_class
     {{do2(push_glyph_attr)},                        3, "PUSH_GLYPH_ATTR"},          // gattrnum gattrnum slot
     {{do2(push_att_to_glyph_attr)},                 3, "PUSH_ATT_TO_GLYPH_ATTR"},   // gattrnum gattrnum slot
+    {{do2(bor)},                                    0, "BITOR"},
+    {{do2(band)},                                   0, "BITAND"},
+    {{do2(bnot)},                                   0, "BITNOT"},   // 0x40
+    {{do2(setbits)},                                4, "BITSET"},
+    {{do2(set_feat)},                               2, "SET_FEAT"},
     // private opcodes for internal use only, comes after all other on disk opcodes.
     {{do_(temp_copy), NILOP},                       0, "TEMP_COPY"}
 };
 
--- a/gfx/graphite2/src/inc/opcodes.h
+++ b/gfx/graphite2/src/inc/opcodes.h
@@ -236,30 +236,34 @@ STARTOP(put_subs_8bit_obs)
         index = seg.findClassIndex(input_class, slot->gid());
         is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
     }
 ENDOP
 
 STARTOP(put_copy)
     declare_params(1);
     const int  slot_ref = int8(*param);
-    if (is && (slot_ref ||is != *map))
+    if (is)
     {
-        int16 *tempUserAttrs = is->userAttrs();
         slotref ref = slotat(slot_ref);
-        if (ref)
+        if (ref && ref != is)
         {
-            memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
+            int16 *tempUserAttrs = is->userAttrs();
+            if (is->attachedTo() || is->firstChild()) DIE
             Slot *prev = is->prev();
             Slot *next = is->next();
-            memcpy(is, slotat(slot_ref), sizeof(Slot));
+            memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
+            memcpy(is, ref, sizeof(Slot));
+            is->firstChild(NULL);
+            is->nextSibling(NULL);
             is->userAttrs(tempUserAttrs);
             is->next(next);
             is->prev(prev);
-            is->sibling(NULL);
+            if (is->attachedTo())
+                is->attachedTo()->child(is);
         }
         is->markCopied(false);
         is->markDeleted(false);
     }
 ENDOP
 
 STARTOP(insert)
     Slot *newSlot = seg.newSlot();
@@ -311,17 +315,17 @@ STARTOP(insert)
     }
     is = newSlot;
     seg.extendLength(1);
     if (map != &smap[-1]) 
         --map;
 ENDOP
 
 STARTOP(delete_)
-    if (!is) DIE
+    if (!is || is->isDeleted()) DIE
     is->markDeleted(true);
     if (is->prev())
         is->prev()->next(is->next());
     else
         seg.first(is->next());
     
     if (is->next())
         is->next()->prev(is->prev());
@@ -639,8 +643,42 @@ STARTOP(temp_copy)
     if (!newSlot) DIE;
     int16 *tempUserAttrs = newSlot->userAttrs();
     memcpy(newSlot, is, sizeof(Slot));
     memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16));
     newSlot->userAttrs(tempUserAttrs);
     newSlot->markCopied(true);
     *map = newSlot;
 ENDOP
+
+STARTOP(band)
+    binop(&);
+ENDOP
+
+STARTOP(bor)
+    binop(|);
+ENDOP
+
+STARTOP(bnot)
+    *sp = ~*sp;
+ENDOP
+
+STARTOP(setbits)
+    declare_params(4);
+    const uint16 m  = uint16(param[0]) << 8
+                    | uint8(param[1]);
+    const uint16 v  = uint16(param[2]) << 8
+                    | uint8(param[3]);
+    *sp = ((*sp) & ~m) | v;
+ENDOP
+
+STARTOP(set_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)
+    {
+        uint8 fid = seg.charinfo(slot->original())->fid();
+        seg.setFeature(fid, feat, pop());
+    }
+ENDOP
+
--- a/gfx/graphite2/src/json.cpp
+++ b/gfx/graphite2/src/json.cpp
@@ -25,16 +25,17 @@ License, as published by the Free Softwa
 of the License or (at your option) any later version.
 */
 // JSON debug logging
 // Author: Tim Eves
 
 #if !defined GRAPHITE2_NTRACING
 
 #include <stdio.h>
+#include <limits>
 #include "inc/json.h"
 
 using namespace graphite2;
 
 namespace
 {
     enum
     {
@@ -111,16 +112,29 @@ json & json::operator << (json::string s
     const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq;
     context(ctxt);
     fprintf(_stream, "\"%s\"", s);
     if (ctxt == member) fputc(' ', _stream);
 
     return *this;
 }
 
-json & json::operator << (json::number f) throw()   { context(seq); fprintf(_stream, "%g", f); return *this; }
+json & json::operator << (json::number f) throw()
+{ 
+    context(seq); 
+    if (std::numeric_limits<json::number>::infinity() == f)
+        fputs("Infinity", _stream);
+    else if (-std::numeric_limits<json::number>::infinity() == f)
+        fputs("-Infinity", _stream);
+    else if (std::numeric_limits<json::number>::quiet_NaN() == f ||
+            std::numeric_limits<json::number>::signaling_NaN() == f)
+        fputs("NaN", _stream);
+    else
+        fprintf(_stream, "%g", f); 
+    return *this; 
+}
 json & json::operator << (json::integer d) throw()  { context(seq); fprintf(_stream, "%ld", d); return *this; }
 json & json::operator << (long unsigned d) throw()  { context(seq); fprintf(_stream, "%ld", d); return *this; }
 json & json::operator << (json::boolean b) throw()  { context(seq); fputs(b ? "true" : "false", _stream); return *this; }
 json & json::operator << (json::_null_t) throw()    { context(seq); fputs("null",_stream); return *this; }
 
 #endif
 
--- a/gfx/graphite2/src/moz.build
+++ b/gfx/graphite2/src/moz.build
@@ -22,33 +22,37 @@ else:
     ]
 
 # This should contain all of the _SOURCES from files.mk, except *_machine.cpp
 UNIFIED_SOURCES += [
     'Bidi.cpp',
     'CachedFace.cpp',
     'CmapCache.cpp',
     'Code.cpp',
+    'Collider.cpp',
+    'Decompressor.cpp',
     'Face.cpp',
     'FeatureMap.cpp',
     'FileFace.cpp',
     'Font.cpp',
     'GlyphCache.cpp',
     'GlyphFace.cpp',
     'gr_char_info.cpp',
     'gr_face.cpp',
     'gr_features.cpp',
     'gr_font.cpp',
     'gr_logging.cpp',
     'gr_segment.cpp',
     'gr_slot.cpp',
+    'Intervals.cpp',
     'json.cpp',
     'Justifier.cpp',
     'NameTable.cpp',
     'Pass.cpp',
+    'Position.cpp',
     'SegCache.cpp',
     'SegCacheEntry.cpp',
     'SegCacheStore.cpp',
     'Segment.cpp',
     'Silf.cpp',
     'Slot.cpp',
     'Sparse.cpp',
     'TtfUtil.cpp',
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -116,16 +116,17 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Di
  */
 enum class EffectTypes : uint8_t {
   MASK,
   BLEND_MODE,
   COLOR_MATRIX,
   MAX_SECONDARY, // sentinel for the count of secondary effect types
   RGB,
   YCBCR,
+  NV12,
   COMPONENT_ALPHA,
   SOLID_COLOR,
   RENDER_TARGET,
   VR_DISTORTION,
   MAX  //sentinel for the count of all effect types
 };
 
 /**
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -192,16 +192,25 @@ struct EffectYCbCr : public TexturedEffe
 {
   EffectYCbCr(TextureSource *aSource, gfx::Filter aFilter)
     : TexturedEffect(EffectTypes::YCBCR, aSource, false, aFilter)
   {}
 
   virtual const char* Name() { return "EffectYCbCr"; }
 };
 
+struct EffectNV12 : public TexturedEffect
+{
+  EffectNV12(TextureSource *aSource, gfx::Filter aFilter)
+    : TexturedEffect(EffectTypes::NV12, aSource, false, aFilter)
+  {}
+
+  virtual const char* Name() { return "EffectNV12"; }
+};
+
 struct EffectComponentAlpha : public TexturedEffect
 {
   EffectComponentAlpha(TextureSource *aOnBlack,
                        TextureSource *aOnWhite,
                        gfx::Filter aFilter)
     : TexturedEffect(EffectTypes::COMPONENT_ALPHA, nullptr, false, aFilter)
     , mOnBlack(aOnBlack)
     , mOnWhite(aOnWhite)
@@ -260,16 +269,19 @@ CreateTexturedEffect(gfx::SurfaceFormat 
   case gfx::SurfaceFormat::R8G8B8X8:
   case gfx::SurfaceFormat::R5G6B5:
   case gfx::SurfaceFormat::R8G8B8A8:
     result = new EffectRGB(aSource, isAlphaPremultiplied, aFilter);
     break;
   case gfx::SurfaceFormat::YUV:
     result = new EffectYCbCr(aSource, aFilter);
     break;
+  case gfx::SurfaceFormat::NV12:
+    result = new EffectNV12(aSource, aFilter);
+    break;
   default:
     NS_WARNING("unhandled program type");
     break;
   }
 
   result->mState = state;
 
   return result.forget();
--- a/gfx/layers/IMFYCbCrImage.cpp
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -84,18 +84,18 @@ InitTextures(IDirect3DDevice9* aDevice,
                                     byRef(tmpTexture), nullptr))) {
     return nullptr;
   }
   if (!tmpTexture) {
     return nullptr;
   }
 
   tmpTexture->GetSurfaceLevel(0, byRef(aSurface));
-  aSurface->LockRect(&aLockedRect, nullptr, 0);
-  if (!aLockedRect.pBits) {
+  if (FAILED(aSurface->LockRect(&aLockedRect, nullptr, 0)) ||
+      !aLockedRect.pBits) {
     NS_WARNING("Could not lock surface");
     return nullptr;
   }
 
   return result.forget();
 }
 
 static bool
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -332,16 +332,17 @@ AppendToString(std::stringstream& aStrea
   switch (format) {
   case SurfaceFormat::B8G8R8A8:  aStream << "SurfaceFormat::B8G8R8A8"; break;
   case SurfaceFormat::B8G8R8X8:  aStream << "SurfaceFormat::B8G8R8X8"; break;
   case SurfaceFormat::R8G8B8A8:  aStream << "SurfaceFormat::R8G8B8A8"; break;
   case SurfaceFormat::R8G8B8X8:  aStream << "SurfaceFormat::R8G8B8X8"; break;
   case SurfaceFormat::R5G6B5:    aStream << "SurfaceFormat::R5G6B5"; break;
   case SurfaceFormat::A8:        aStream << "SurfaceFormat::A8"; break;
   case SurfaceFormat::YUV:       aStream << "SurfaceFormat::YUV"; break;
+  case SurfaceFormat::NV12:      aStream << "SurfaceFormat::NV12"; break;
   case SurfaceFormat::UNKNOWN:   aStream << "SurfaceFormat::UNKNOWN"; break;
   default:
     NS_ERROR("unknown surface format");
     aStream << "???";
   }
 
   aStream << sfx;
 }
--- a/gfx/layers/MacIOSurfaceImage.cpp
+++ b/gfx/layers/MacIOSurfaceImage.cpp
@@ -2,54 +2,102 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MacIOSurfaceImage.h"
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
+#include "YCbCrUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
+using namespace mozilla::gfx;
 
 TextureClient*
 MacIOSurfaceImage::GetTextureClient(CompositableClient* aClient)
 {
   if (!mTextureClient) {
     mTextureClient = MacIOSurfaceTextureClientOGL::Create(aClient->GetForwarder(),
                                                           TextureFlags::DEFAULT,
                                                           mSurface);
   }
   return mTextureClient;
 }
 
-already_AddRefed<gfx::SourceSurface>
+already_AddRefed<SourceSurface>
 MacIOSurfaceImage::GetAsSourceSurface()
 {
+  RefPtr<DataSourceSurface> dataSurface;
   mSurface->Lock();
   size_t bytesPerRow = mSurface->GetBytesPerRow();
   size_t ioWidth = mSurface->GetDevicePixelWidth();
   size_t ioHeight = mSurface->GetDevicePixelHeight();
 
-  unsigned char* ioData = (unsigned char*)mSurface->GetBaseAddress();
+  SurfaceFormat format = mSurface->GetFormat() == SurfaceFormat::NV12 ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
 
-  RefPtr<gfx::DataSourceSurface> dataSurface
-    = gfx::Factory::CreateDataSourceSurface(gfx::IntSize(ioWidth, ioHeight), gfx::SurfaceFormat::B8G8R8A8);
+  dataSurface = Factory::CreateDataSourceSurface(IntSize(ioWidth, ioHeight), format);
   if (NS_WARN_IF(!dataSurface)) {
     return nullptr;
   }
 
-  gfx::DataSourceSurface::MappedSurface mappedSurface;
-  if (!dataSurface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface))
+  DataSourceSurface::MappedSurface mappedSurface;
+  if (!dataSurface->Map(DataSourceSurface::WRITE, &mappedSurface))
     return nullptr;
 
-  for (size_t i = 0; i < ioHeight; ++i) {
-    memcpy(mappedSurface.mData + i * mappedSurface.mStride,
-           ioData + i * bytesPerRow,
-           ioWidth * 4);
+  if (mSurface->GetFormat() == SurfaceFormat::NV12) {
+    if (mSurface->GetDevicePixelWidth() > PlanarYCbCrImage::MAX_DIMENSION ||
+        mSurface->GetDevicePixelHeight() > PlanarYCbCrImage::MAX_DIMENSION) {
+      return nullptr;
+    }
+
+    /* Extract and separate the CbCr planes */
+    size_t cbCrStride = mSurface->GetBytesPerRow(1);
+    size_t cbCrWidth = mSurface->GetDevicePixelWidth(1);
+    size_t cbCrHeight = mSurface->GetDevicePixelHeight(1);
+
+    nsAutoArrayPtr<uint8_t> cbPlane(new uint8_t[cbCrWidth * cbCrHeight]);
+    nsAutoArrayPtr<uint8_t> crPlane(new uint8_t[cbCrWidth * cbCrHeight]);
+
+    uint8_t* src = (uint8_t*)mSurface->GetBaseAddressOfPlane(1);
+    uint8_t* cbDest = cbPlane;
+    uint8_t* crDest = crPlane;
+
+    for (size_t i = 0; i < cbCrHeight; i++) {
+      uint8_t* rowSrc = src + cbCrStride * i;
+      for (size_t j = 0; j < cbCrWidth; j++) {
+        *cbDest = *rowSrc;
+        cbDest++;
+        rowSrc++;
+        *crDest = *rowSrc;
+        crDest++;
+        rowSrc++;
+      }
+    }
+
+    /* Convert to RGB */
+    PlanarYCbCrData data;
+    data.mYChannel = (uint8_t*)mSurface->GetBaseAddressOfPlane(0);
+    data.mYStride = mSurface->GetBytesPerRow(0);
+    data.mYSize = IntSize(mSurface->GetDevicePixelWidth(0), mSurface->GetDevicePixelHeight(0));
+    data.mCbChannel = cbPlane;
+    data.mCrChannel = crPlane;
+    data.mCbCrStride = cbCrWidth;
+    data.mCbCrSize = IntSize(cbCrWidth, cbCrHeight);
+    data.mPicSize = data.mYSize;
+
+    ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+  } else {
+    unsigned char* ioData = (unsigned char*)mSurface->GetBaseAddress();
+
+    for (size_t i = 0; i < ioHeight; ++i) {
+      memcpy(mappedSurface.mData + i * mappedSurface.mStride,
+             ioData + i * bytesPerRow,
+             ioWidth * 4);
+    }
   }
 
   dataSurface->Unmap();
   mSurface->Unlock();
 
   return dataSurface.forget();
 }
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -807,16 +807,20 @@ CompositorOGL::GetShaderConfigFor(Effect
 
   switch(aEffect->mType) {
   case EffectTypes::SOLID_COLOR:
     config.SetRenderColor(true);
     break;
   case EffectTypes::YCBCR:
     config.SetYCbCr(true);
     break;
+  case EffectTypes::NV12:
+    config.SetNV12(true);
+    config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+    break;
   case EffectTypes::COMPONENT_ALPHA:
   {
     config.SetComponentAlpha(true);
     EffectComponentAlpha* effectComponentAlpha =
       static_cast<EffectComponentAlpha*>(aEffect);
     gfx::SurfaceFormat format = effectComponentAlpha->mOnWhite->GetFormat();
     config.SetRBSwap(format == gfx::SurfaceFormat::B8G8R8A8 ||
                      format == gfx::SurfaceFormat::B8G8R8X8);
@@ -1246,16 +1250,50 @@ CompositorOGL::DrawQuad(const Rect& aRec
       }
       didSetBlendMode = SetBlendMode(gl(), blendMode);
       BindAndDrawQuadWithTextureRect(program,
                                      aRect,
                                      effectYCbCr->mTextureCoords,
                                      sourceYCbCr->GetSubSource(Y));
     }
     break;
+  case EffectTypes::NV12: {
+      EffectNV12* effectNV12 =
+        static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
+      TextureSource* sourceNV12 = effectNV12->mTexture;
+      const int Y = 0, CbCr = 1;
+      TextureSourceOGL* sourceY =  sourceNV12->GetSubSource(Y)->AsSourceOGL();
+      TextureSourceOGL* sourceCbCr = sourceNV12->GetSubSource(CbCr)->AsSourceOGL();
+
+      if (!sourceY || !sourceCbCr) {
+        NS_WARNING("Invalid layer texture.");
+        return;
+      }
+
+      sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectNV12->mFilter);
+      sourceCbCr->BindTexture(LOCAL_GL_TEXTURE1, effectNV12->mFilter);
+
+      if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+        // This is used by IOSurface that use 0,0...w,h coordinate rather then 0,0..1,1.
+        program->SetCbCrTexCoordMultiplier(sourceCbCr->GetSize().width, sourceCbCr->GetSize().height);
+      }
+
+      program->SetNV12TextureUnits(Y, CbCr);
+      program->SetTextureTransform(Matrix4x4());
+
+      if (maskType != MaskType::MaskNone) {
+        BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
+      }
+      didSetBlendMode = SetBlendMode(gl(), blendMode);
+      BindAndDrawQuadWithTextureRect(program,
+                                     aRect,
+                                     effectNV12->mTextureCoords,
+                                     sourceNV12->GetSubSource(Y));
+    }
+    break;
   case EffectTypes::RENDER_TARGET: {
       EffectRenderTarget* effectRenderTarget =
         static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
       RefPtr<CompositingRenderTargetOGL> surface
         = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get());
 
       surface->BindTexture(LOCAL_GL_TEXTURE0, mFBOTextureTarget);
 
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -20,58 +20,69 @@ MacIOSurfaceTextureHostOGL::MacIOSurface
                                          !aDescriptor.isOpaque());
 }
 
 MacIOSurfaceTextureHostOGL::~MacIOSurfaceTextureHostOGL()
 {
   MOZ_COUNT_DTOR(MacIOSurfaceTextureHostOGL);
 }
 
+GLTextureSource*
+MacIOSurfaceTextureHostOGL::CreateTextureSourceForPlane(size_t aPlane)
+{
+  GLuint textureHandle;
+  gl::GLContext* gl = mCompositor->gl();
+  gl->fGenTextures(1, &textureHandle);
+  gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
+  gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+  gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+
+  mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext(), aPlane);
+
+  return new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+                             gfx::IntSize(mSurface->GetDevicePixelWidth(aPlane),
+                                          mSurface->GetDevicePixelHeight(aPlane)),
+                             // XXX: This isn't really correct (but isn't used), we should be using the
+                             // format of the individual plane, not of the whole buffer.
+                             mSurface->GetFormat());
+}
+
 bool
 MacIOSurfaceTextureHostOGL::Lock()
 {
   if (!mCompositor || !mSurface) {
     return false;
   }
 
   if (!mTextureSource) {
-    GLuint textureHandle;
-    gl::GLContext* gl = mCompositor->gl();
-    gl->fGenTextures(1, &textureHandle);
-    gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
-    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
-    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-    mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
+    mTextureSource = CreateTextureSourceForPlane(0);
 
-    mTextureSource = new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
-                                         gfx::IntSize(mSurface->GetDevicePixelWidth(),
-                                                      mSurface->GetDevicePixelHeight()),
-                                         mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8:
-                                                                gfx::SurfaceFormat::R8G8B8X8);
+    RefPtr<TextureSource> prev = mTextureSource;
+    for (size_t i = 1; i < mSurface->GetPlaneCount(); i++) {
+      RefPtr<TextureSource> next = CreateTextureSourceForPlane(i);
+      prev->SetNextSibling(next);
+      prev = next;
+    }
   }
   return true;
 }
 
 void
 MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
   CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   mCompositor = glCompositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(glCompositor);
   }
 }
 
 gfx::SurfaceFormat
 MacIOSurfaceTextureHostOGL::GetFormat() const {
-  if (!mSurface) {
-    return gfx::SurfaceFormat::UNKNOWN;
-  }
-  return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
-                              : gfx::SurfaceFormat::R8G8B8X8;
+  return mSurface->GetFormat();
 }
 
 gfx::IntSize
 MacIOSurfaceTextureHostOGL::GetSize() const {
   if (!mSurface) {
     return gfx::IntSize();
   }
   return gfx::IntSize(mSurface->GetDevicePixelWidth(),
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -90,16 +90,18 @@ public:
 
   virtual gfx::IntSize GetSize() const override;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() override { return "MacIOSurfaceTextureHostOGL"; }
 #endif
 
 protected:
+  GLTextureSource* CreateTextureSourceForPlane(size_t aPlane);
+
   RefPtr<CompositorOGL> mCompositor;
   RefPtr<GLTextureSource> mTextureSource;
   RefPtr<MacIOSurface> mSurface;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/opengl/OGLShaderProgram.cpp
+++ b/gfx/layers/opengl/OGLShaderProgram.cpp
@@ -42,16 +42,17 @@ AddUniforms(ProgramProfileOGL& aProfile)
         "uYTexture",
         "uCbTexture",
         "uCrTexture",
         "uBlackTexture",
         "uWhiteTexture",
         "uMaskTexture",
         "uRenderColor",
         "uTexCoordMultiplier",
+        "uCbCrTexCoordMultiplier",
         "uTexturePass2",
         "uColorMatrix",
         "uColorMatrixVector",
         "uBlurRadius",
         "uBlurOffset",
         "uBlurAlpha",
         "uBlurGaussianKernel",
         "uSSEdges",
@@ -103,16 +104,24 @@ ShaderConfigOGL::SetOpacity(bool aEnable
 {
   SetFeature(ENABLE_OPACITY, aEnabled);
 }
 
 void
 ShaderConfigOGL::SetYCbCr(bool aEnabled)
 {
   SetFeature(ENABLE_TEXTURE_YCBCR, aEnabled);
+  MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_NV12));
+}
+
+void
+ShaderConfigOGL::SetNV12(bool aEnabled)
+{
+  SetFeature(ENABLE_TEXTURE_NV12, aEnabled);
+  MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_YCBCR));
 }
 
 void
 ShaderConfigOGL::SetComponentAlpha(bool aEnabled)
 {
   SetFeature(ENABLE_TEXTURE_COMPONENT_ALPHA, aEnabled);
 }
 
@@ -296,28 +305,35 @@ ProgramProfileOGL::GetProfileFor(ShaderC
     }
   }
 
   const char *sampler2D = "sampler2D";
   const char *texture2D = "texture2D";
 
   if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
     fs << "uniform vec2 uTexCoordMultiplier;" << endl;
+    if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+        aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+      fs << "uniform vec2 uCbCrTexCoordMultiplier;" << endl;
+    }
     sampler2D = "sampler2DRect";
     texture2D = "texture2DRect";
   }
 
   if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
     sampler2D = "samplerExternalOES";
   }
 
   if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
     fs << "uniform sampler2D uYTexture;" << endl;
     fs << "uniform sampler2D uCbTexture;" << endl;
     fs << "uniform sampler2D uCrTexture;" << endl;
+  } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+    fs << "uniform " << sampler2D << " uYTexture;" << endl;
+    fs << "uniform " << sampler2D << " uCbTexture;" << endl;
   } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
     fs << "uniform sampler2D uBlackTexture;" << endl;
     fs << "uniform sampler2D uWhiteTexture;" << endl;
     fs << "uniform bool uTexturePass2;" << endl;
   } else {
     fs << "uniform " << sampler2D << " uTexture;" << endl;
   }
 
@@ -329,20 +345,39 @@ ProgramProfileOGL::GetProfileFor(ShaderC
 
   if (aConfig.mFeatures & ENABLE_DEAA) {
     fs << "uniform vec3 uSSEdges[4];" << endl;
   }
 
   if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
     fs << "vec4 sample(vec2 coord) {" << endl;
     fs << "  vec4 color;" << endl;
-    if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
-      fs << "  COLOR_PRECISION float y = texture2D(uYTexture, coord).r;" << endl;
-      fs << "  COLOR_PRECISION float cb = texture2D(uCbTexture, coord).r;" << endl;
-      fs << "  COLOR_PRECISION float cr = texture2D(uCrTexture, coord).r;" << endl;
+    if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+        aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+      if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+        if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+          fs << "  COLOR_PRECISION float y = texture2D(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+          fs << "  COLOR_PRECISION float cb = texture2D(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+          fs << "  COLOR_PRECISION float cr = texture2D(uCrTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+        } else {
+          fs << "  COLOR_PRECISION float y = texture2D(uYTexture, coord).r;" << endl;
+          fs << "  COLOR_PRECISION float cb = texture2D(uCbTexture, coord).r;" << endl;
+          fs << "  COLOR_PRECISION float cr = texture2D(uCrTexture, coord).r;" << endl;
+        }
+      } else {
+        if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+          fs << "  COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+          fs << "  COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+          fs << "  COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).a;" << endl;
+        } else {
+          fs << "  COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord).r;" << endl;
+          fs << "  COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord).r;" << endl;
+          fs << "  COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord).a;" << endl;
+        }
+      }
 
       /* From Rec601:
 [R]   [1.1643835616438356,  0.0,                 1.5960267857142858]      [ Y -  16]
 [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708]    x [Cb - 128]
 [B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [Cr - 128]
 
 For [0,1] instead of [0,255], and to 5 places:
 [R]   [1.16438,  0.00000,  1.59603]   [ Y - 0.06275]
@@ -360,17 +395,21 @@ For [0,1] instead of [0,255], and to 5 p
       fs << "  COLOR_PRECISION vec3 onBlack = texture2D(uBlackTexture, coord).rgb;" << endl;
       fs << "  COLOR_PRECISION vec3 onWhite = texture2D(uWhiteTexture, coord).rgb;" << endl;
       fs << "  COLOR_PRECISION vec4 alphas = (1.0 - onWhite + onBlack).rgbg;" << endl;
       fs << "  if (uTexturePass2)" << endl;
       fs << "    color = vec4(onBlack, alphas.a);" << endl;
       fs << "  else" << endl;
       fs << "    color = alphas;" << endl;
     } else {
-      fs << "  color = " << texture2D << "(uTexture, coord);" << endl;
+      if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+        fs << "  color = " << texture2D << "(uTexture, coord * uTexCoordMultiplier);" << endl;
+      } else {
+        fs << "  color = " << texture2D << "(uTexture, coord);" << endl;
+      }
     }
     if (aConfig.mFeatures & ENABLE_TEXTURE_RB_SWAP) {
       fs << "  color = color.bgra;" << endl;
     }
     if (aConfig.mFeatures & ENABLE_TEXTURE_NO_ALPHA) {
       fs << "  color = vec4(color.rgb, 1.0);" << endl;
     }
     fs << "  return color;" << endl;
@@ -399,21 +438,17 @@ For [0,1] instead of [0,255], and to 5 p
       fs << "  return color;" << endl;
       fs << "}" << endl;
     }
   }
   fs << "void main() {" << endl;
   if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
     fs << "  vec4 color = uRenderColor;" << endl;
   } else {
-    if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
-      fs << "  vec4 color = sample(vTexCoord * uTexCoordMultiplier);" << endl;
-    } else {
-      fs << "  vec4 color = sample(vTexCoord);" << endl;
-    }
+    fs << "  vec4 color = sample(vTexCoord);" << endl;
     if (aConfig.mFeatures & ENABLE_BLUR) {
       fs << "  color = blur(color, vTexCoord);" << endl;
     }
     if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
       fs << "  color = uColorMatrix * vec4(color.rgb / color.a, color.a) + uColorMatrixVector;" << endl;
       fs << "  color.rgb *= color.a;" << endl;
     }
     if (aConfig.mFeatures & ENABLE_OPACITY) {
@@ -450,16 +485,18 @@ For [0,1] instead of [0,255], and to 5 p
   result.mVertexShaderString = vs.str();
   result.mFragmentShaderString = fs.str();
 
   if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
     result.mTextureCount = 0;
   } else {
     if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
       result.mTextureCount = 3;
+    } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+      result.mTextureCount = 2;
     } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
       result.mTextureCount = 2;
     } else {
       result.mTextureCount = 1;
     }
   }
   if (aConfig.mFeatures & ENABLE_MASK_2D ||
       aConfig.mFeatures & ENABLE_MASK_3D) {
--- a/gfx/layers/opengl/OGLShaderProgram.h
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -27,26 +27,27 @@ namespace layers {
 
 class Layer;
 
 enum ShaderFeatures {
   ENABLE_RENDER_COLOR=0x01,
   ENABLE_TEXTURE_RECT=0x02,
   ENABLE_TEXTURE_EXTERNAL=0x04,
   ENABLE_TEXTURE_YCBCR=0x08,
-  ENABLE_TEXTURE_COMPONENT_ALPHA=0x10,
-  ENABLE_TEXTURE_NO_ALPHA=0x20,
-  ENABLE_TEXTURE_RB_SWAP=0x40,
-  ENABLE_OPACITY=0x80,
-  ENABLE_BLUR=0x100,
-  ENABLE_COLOR_MATRIX=0x200,
-  ENABLE_MASK_2D=0x400,
-  ENABLE_MASK_3D=0x800,
-  ENABLE_PREMULTIPLY=0x1000,
-  ENABLE_DEAA=0x2000
+  ENABLE_TEXTURE_NV12=0x10,
+  ENABLE_TEXTURE_COMPONENT_ALPHA=0x20,
+  ENABLE_TEXTURE_NO_ALPHA=0x40,
+  ENABLE_TEXTURE_RB_SWAP=0x80,
+  ENABLE_OPACITY=0x100,
+  ENABLE_BLUR=0x200,
+  ENABLE_COLOR_MATRIX=0x400,
+  ENABLE_MASK_2D=0x800,
+  ENABLE_MASK_3D=0x1000,
+  ENABLE_PREMULTIPLY=0x2000,
+  ENABLE_DEAA=0x4000
 };
 
 class KnownUniform {
 public:
   // this needs to be kept in sync with strings in 'AddUniforms'
   enum KnownUniformName {
     NotAKnownUniform = -1,
 
@@ -63,16 +64,17 @@ public:
     YTexture,
     CbTexture,
     CrTexture,
     BlackTexture,
     WhiteTexture,
     MaskTexture,
     RenderColor,
     TexCoordMultiplier,
+    CbCrTexCoordMultiplier,
     TexturePass2,
     ColorMatrix,
     ColorMatrixVector,
     BlurRadius,
     BlurOffset,
     BlurAlpha,
     BlurGaussianKernel,
     SSEdges,
@@ -210,16 +212,17 @@ public:
     mFeatures(0) {}
 
   void SetRenderColor(bool aEnabled);
   void SetTextureTarget(GLenum aTarget);
   void SetRBSwap(bool aEnabled);
   void SetNoAlpha(bool aEnabled);
   void SetOpacity(bool aEnabled);
   void SetYCbCr(bool aEnabled);
+  void SetNV12(bool aEnabled);
   void SetComponentAlpha(bool aEnabled);
   void SetColorMatrix(bool aEnabled);
   void SetBlur(bool aEnabled);
   void SetMask2D(bool aEnabled);
   void SetMask3D(bool aEnabled);
   void SetPremultiply(bool aEnabled);
   void SetDEAA(bool aEnabled);
 
@@ -410,16 +413,21 @@ public:
   }
 
   void SetYCbCrTextureUnits(GLint aYUnit, GLint aCbUnit, GLint aCrUnit) {
     SetUniform(KnownUniform::YTexture, aYUnit);
     SetUniform(KnownUniform::CbTexture, aCbUnit);
     SetUniform(KnownUniform::CrTexture, aCrUnit);
   }
 
+  void SetNV12TextureUnits(GLint aYUnit, GLint aCbCrUnit) {
+    SetUniform(KnownUniform::YTexture, aYUnit);
+    SetUniform(KnownUniform::CbTexture, aCbCrUnit);
+  }
+
   void SetBlackTextureUnit(GLint aUnit) {
     SetUniform(KnownUniform::BlackTexture, aUnit);
   }
 
   void SetWhiteTextureUnit(GLint aUnit) {
     SetUniform(KnownUniform::WhiteTexture, aUnit);
   }
 
@@ -441,16 +449,21 @@ public:
     SetUniform(KnownUniform::ColorMatrixVector, 4, &aColorMatrix._51);
   }
 
   void SetTexCoordMultiplier(float aWidth, float aHeight) {
     float f[] = {aWidth, aHeight};
     SetUniform(KnownUniform::TexCoordMultiplier, 2, f);
   }
 
+  void SetCbCrTexCoordMultiplier(float aWidth, float aHeight) {
+    float f[] = {aWidth, aHeight};
+    SetUniform(KnownUniform::CbCrTexCoordMultiplier, 2, f);
+  }
+
   // Set whether we want the component alpha shader to return the color
   // vector (pass 1, false) or the alpha vector (pass2, true). With support
   // for multiple render targets we wouldn't need two passes here.
   void SetTexturePass2(bool aFlag) {
     SetUniform(KnownUniform::TexturePass2, aFlag ? 1 : 0);
   }
 
   void SetBlurRadius(float aRX, float aRY);
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -475,23 +475,32 @@ gfxGDIFont::GetGlyph(uint32_t aUnicode, 
     uint32_t gid;
     if (mGlyphIDs->Get(aUnicode, &gid)) {
         return gid;
     }
 
     wchar_t ch = aUnicode;
     WORD glyph;
     DWORD ret = ScriptGetCMap(nullptr, &mScriptCache, &ch, 1, 0, &glyph);
-    if (ret == E_PENDING) {
+    if (ret != S_OK) {
         AutoDC dc;
         AutoSelectFont fs(dc.GetDC(), GetHFONT());
-        ret = ScriptGetCMap(dc.GetDC(), &mScriptCache, &ch, 1, 0, &glyph);
-    }
-    if (ret != S_OK) {
-        glyph = 0;
+        if (ret == E_PENDING) {
+            // Try ScriptGetCMap again now that we've set up the font.
+            ret = ScriptGetCMap(dc.GetDC(), &mScriptCache, &ch, 1, 0, &glyph);
+        }
+        if (ret != S_OK) {
+            // If ScriptGetCMap still failed, fall back to GetGlyphIndicesW
+            // (see bug 1105807).
+            ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
+                                   GGI_MARK_NONEXISTING_GLYPHS);
+            if (ret == GDI_ERROR || glyph == 0xFFFF) {
+                glyph = 0;
+            }
+        }
     }
 
     mGlyphIDs->Put(aUnicode, glyph);
     return glyph;
 }
 
 int32_t
 gfxGDIFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -258,17 +258,20 @@ nsXULPopupManager::Rollup(uint32_t aCoun
           }
         }
       }
 
       // It's possible that some other element is above the anchor at the same
       // position, but the only thing that would happen is that the mouse
       // event will get consumed, so here only a quick coordinates check is
       // done rather than a slower complete check of what is at that location.
-      if (anchorRect.Contains(*pos)) {
+      nsPresContext* presContext = item->Frame()->PresContext();
+      nsIntPoint posCSSPixels(presContext->DevPixelsToIntCSSPixels(pos->x),
+                              presContext->DevPixelsToIntCSSPixels(pos->y));
+      if (anchorRect.Contains(posCSSPixels)) {
         if (consumeResult == ConsumeOutsideClicks_ParentOnly) {
           consume = true;
         }
 
         if (noRollupOnAnchor) {
           rollup = false;
         }
       }
--- a/netwerk/base/nsIPackagedAppService.idl
+++ b/netwerk/base/nsIPackagedAppService.idl
@@ -1,42 +1,50 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsIURI;
+interface nsIPrincipal;
 interface nsILoadContextInfo;
 interface nsICacheEntryOpenCallback;
 
 %{C++
   #define PACKAGED_APP_TOKEN "!//"
 %}
 
 /**
  * nsIPackagedAppService
  */
-[scriptable, builtinclass, uuid(77f9a34d-d082-43f1-9f83-e852d0173cd5)]
+[scriptable, builtinclass, uuid(f35e5229-d08a-46eb-a574-2db4e22aee98)]
 interface nsIPackagedAppService : nsISupports
 {
   /**
-   * @aURI is a URL to a packaged resource
-   *   - format:  package_url + PACKAGED_APP_TOKEN + resource_path
-   *   - example: http://test.com/path/to/package!//resource.html
-   * @aCallback is an object implementing nsICacheEntryOpenCallback
-   *   - this is the target of the async result of the operation
-   *   - aCallback->OnCacheEntryCheck() is called to verify the entry is valid
-   *   - aCallback->OnCacheEntryAvailable() is called with a pointer to the
-   *     the cached entry, if one exists, or an error code otherwise
-   *   - aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable
-   *     is called
-   * @aInfo is an object used to determine the cache jar this resource goes in.
-   *   - usually created by calling GetLoadContextInfo(requestingChannel)
+   * @param aPrincipal
+   *    the principal associated to the URL of a packaged resource
+   *    URL format:  package_url + PACKAGED_APP_TOKEN + resource_path
+   *    example: http://test.com/path/to/package!//resource.html
+   * @param aFlags
+   *    the load flags used for downloading the package
+   * @param aCallback
+   *    an object implementing nsICacheEntryOpenCallback
+   *    this is the target of the async result of the operation
+   *    aCallback->OnCacheEntryCheck() is called to verify the entry is valid
+   *    aCallback->OnCacheEntryAvailable() is called with a pointer to the
+   *    the cached entry, if one exists, or an error code otherwise
+   *    aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable
+   *    is called
+   * @param aInfo
+   *    an object used to determine the cache jar this resource goes in.
+   *    usually created by calling GetLoadContextInfo(requestingChannel)
    *
    * Calling this method will either download the package containing the given
    * resource URI, store it in the cache and pass the cache entry to aCallback,
    * or if that resource has already been downloaded it will be served from
    * the cache.
    */
-  void requestURI(in nsIURI aURI, in nsILoadContextInfo aInfo, in nsICacheEntryOpenCallback aCallback);
+  void getResource(in nsIPrincipal aPrincipal,
+                   in uint32_t aFlags,
+                   in nsILoadContextInfo aInfo,
+                   in nsICacheEntryOpenCallback aCallback);
 };
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -1398,29 +1398,24 @@ nsHostResolver::ThreadFunc(void *arg)
 
         TimeStamp startTime = TimeStamp::Now();
 #if TTL_AVAILABLE
         bool getTtl = rec->mGetTtl;
 #else
         bool getTtl = false;
 #endif
 
-        // We need to remove IPv4 records manually
-        // because PR_GetAddrInfoByName doesn't support PR_AF_INET6.
-        bool disableIPv4 = rec->af == PR_AF_INET6;
-        uint16_t af = disableIPv4 ? PR_AF_UNSPEC : rec->af;
-        nsresult status = GetAddrInfo(rec->host, af, rec->flags, rec->netInterface,
+        nsresult status = GetAddrInfo(rec->host, rec->af, rec->flags, rec->netInterface,
                                       &ai, getTtl);
 #if defined(RES_RETRY_ON_FAILURE)
         if (NS_FAILED(status) && rs.Reset()) {
-            status = GetAddrInfo(rec->host, af, rec->flags, rec->netInterface, &ai,
+            status = GetAddrInfo(rec->host, rec->af, rec->flags, rec->netInterface, &ai,
                                  getTtl);
         }
 #endif
-
         TimeDuration elapsed = TimeStamp::Now() - startTime;
         uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
 
         if (NS_SUCCEEDED(status)) {
             Telemetry::ID histogramID;
             if (!rec->addr_info_gencnt) {
                 // Time for initial lookup.
                 histogramID = Telemetry::DNS_LOOKUP_TIME;
--- a/netwerk/protocol/http/PackagedAppService.cpp
+++ b/netwerk/protocol/http/PackagedAppService.cpp
@@ -600,33 +600,40 @@ PackagedAppService::GetPackageURI(nsIURI
   }
 
   packageURI.forget(aPackageURI);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PackagedAppService::RequestURI(nsIURI *aURI,
-                               nsILoadContextInfo *aInfo,
-                               nsICacheEntryOpenCallback *aCallback)
+PackagedAppService::GetResource(nsIPrincipal *aPrincipal,
+                                uint32_t aLoadFlags,
+                                nsILoadContextInfo *aInfo,
+                                nsICacheEntryOpenCallback *aCallback)
 {
   // Check arguments are not null
-  if (!aURI || !aCallback || !aInfo) {
+  if (!aPrincipal || !aCallback || !aInfo) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  nsresult rv;
 
-  nsresult rv;
-  LogURI("PackagedAppService::RequestURI", this, aURI, aInfo);
+  nsCOMPtr<nsIURI> uri;
+  rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  LogURI("PackagedAppService::GetResource", this, uri, aInfo);
 
   MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
 
   nsCOMPtr<nsIURI> packageURI;
-  rv = GetPackageURI(aURI, getter_AddRefs(packageURI));
+  rv = GetPackageURI(uri, getter_AddRefs(packageURI));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsAutoCString key;
   CacheFileUtils::AppendKeyPrefix(aInfo, key);
 
   {
@@ -638,32 +645,25 @@ PackagedAppService::RequestURI(nsIURI *a
 
   nsRefPtr<PackagedAppDownloader> downloader;
   if (mDownloadingPackages.Get(key, getter_AddRefs(downloader))) {
     // We have determined that the file is not in the cache.
     // If we find that the package that the file belongs to is currently being
     // downloaded, we will add the callback to the package's queue, and it will
     // be called once the file is processed and saved in the cache.
 
-    downloader->AddCallback(aURI, aCallback);
+    downloader->AddCallback(uri, aCallback);
     return NS_OK;
   }
 
-  // We need to set this flag, because the package metadata
-  // needs to have a separate entry for anonymous channels.
-  uint32_t extra_flags = 0;
-  if (aInfo->IsAnonymous()) {
-    extra_flags = nsIRequest::LOAD_ANONYMOUS;
-  }
-
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(
-    getter_AddRefs(channel), packageURI, nsContentUtils::GetSystemPrincipal(),
+    getter_AddRefs(channel), packageURI, aPrincipal,
     nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, nullptr, nullptr,
-    nsIRequest::LOAD_NORMAL | extra_flags);
+    aLoadFlags);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
   if (cacheChan) {
     // Each resource in the package will be put in its own cache entry
@@ -673,17 +673,17 @@ PackagedAppService::RequestURI(nsIURI *a
   }
 
   downloader = new PackagedAppDownloader();
   rv = downloader->Init(aInfo, key);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  downloader->AddCallback(aURI, aCallback);
+  downloader->AddCallback(uri, aCallback);
 
   nsCOMPtr<nsIStreamConverterService> streamconv =
     do_GetService("@mozilla.org/streamConverters;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIStreamListener> mimeConverter;
--- a/netwerk/protocol/http/PackagedAppService.h
+++ b/netwerk/protocol/http/PackagedAppService.h
@@ -18,20 +18,17 @@ class nsHttpResponseHead;
 
 // This service is used to download packages from the web.
 // Individual resources in the package are saved in the browser cache. It also
 // provides an interface to asynchronously request resources from packages,
 // which are either returned from the cache if they exist and are valid,
 // or downloads the package.
 // The package format is defined at:
 //     https://w3ctag.github.io/packaging-on-the-web/#streamable-package-format
-// Downloading the package is triggered by calling requestURI(aURI, aInfo, aCallback)
-//     aURI is the subresource uri - http://domain.com/path/package!//resource.html
-//     aInfo is a nsILoadContextInfo used to pick the cache jar the resource goes into
-//     aCallback is the target of the async call to requestURI
+// Downloading the package is triggered by calling getResource()
 class PackagedAppService final
   : public nsIPackagedAppService
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIPACKAGEDAPPSERVICE
 
   PackagedAppService();
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5192,27 +5192,33 @@ nsHttpChannel::BeginConnect()
     // clear the already recorded AsyncOpen value for consistency.
     if (!mTimingEnabled)
         mAsyncOpenTime = TimeStamp();
 
     if (mIsPackagedAppResource) {
         // If this is a packaged app resource, the content will be fetched
         // by the packaged app service into the cache, and the cache entry will
         // be passed to OnCacheEntryAvailable.
+
+        // Pass the original load flags to the packaged app request.
+        uint32_t loadFlags = mLoadFlags;
+
         mLoadFlags |= LOAD_ONLY_FROM_CACHE;
         mLoadFlags |= LOAD_FROM_CACHE;
         mLoadFlags &= ~VALIDATE_ALWAYS;
         nsCOMPtr<nsIPackagedAppService> pas =
             do_GetService("@mozilla.org/network/packaged-app-service;1", &rv);
         if (NS_WARN_IF(NS_FAILED(rv))) {
             AsyncAbort(rv);
             return rv;
         }
 
-        rv = pas->RequestURI(mURI, GetLoadContextInfo(this), this);
+        nsCOMPtr<nsIPrincipal> principal = GetURIPrincipal();
+        nsCOMPtr<nsILoadContextInfo> loadInfo = GetLoadContextInfo(this);
+        rv = pas->GetResource(principal, loadFlags, loadInfo, this);
         if (NS_FAILED(rv)) {
             AsyncAbort(rv);
         }
         return rv;
     }
 
     // when proxying only use the pipeline bit if ProxyPipelining() allows it.
     if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
--- a/netwerk/test/mochitests/mochitest.ini
+++ b/netwerk/test/mochitests/mochitest.ini
@@ -5,22 +5,24 @@ support-files =
   method.sjs
   partial_content.sjs
   rel_preconnect.sjs
   user_agent.sjs
   user_agent_update.sjs
   redirect_idn.html^headers^
   redirect_idn.html
   empty.html
+  web_packaged_app.sjs
 
 [test_arraybufferinputstream.html]
 [test_partially_cached_content.html]
 [test_rel_preconnect.html]
 skip-if = e10s
 [test_uri_scheme.html]
 [test_user_agent_overrides.html]
 skip-if = e10s
 [test_user_agent_updates.html]
 skip-if = e10s
 [test_user_agent_updates_reset.html]
 [test_xhr_method_case.html]
 skip-if = e10s
 [test_idn_redirect.html]
+[test_web_packaged_app.html]
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/test_web_packaged_app.html
@@ -0,0 +1,210 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title> Web packaged app </title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+var Cu = SpecialPowers.Cu;
+var Cr = SpecialPowers.Cr;
+var LoadContextInfo = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {}).LoadContextInfo;
+var CommonUtils = Cu.import("resource://services-common/utils.js", {}).CommonUtils;
+
+function CacheCallback(expect) {
+  this.expect = expect || true;
+}
+
+CacheCallback.prototype = {
+  QueryInterface: function(iid) {
+    if (!iid.equals(Ci.nsISupports) &&
+        !iid.equals(Ci.nsICacheEntryOpenCallback)) {
+      throw Cr.NS_ERROR_NO_INTERFACE;
+    }
+    return this;
+  },
+  onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+  onCacheEntryAvailable: function(entry, isnew, applicationCache, status) {
+    is(status, this.expect ? Cr.NS_OK : Cr.NS_ERROR_CACHE_KEY_NOT_FOUND, "check status");
+    is(!!entry, this.expect, "check if entry exists");
+    SpecialPowers.executeSoon(continueTest);
+  }
+};
+
+function checkCacheEntry(url, exists, appId) {
+  var loadContext = appId ? LoadContextInfo.custom(false, false, appId, false) : LoadContextInfo.default;
+  var cacheService = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                     .getService(Ci.nsICacheStorageService);
+  var cache = cacheService.diskCacheStorage(loadContext, false);
+  cache.asyncOpenURI(CommonUtils.makeURI(url), "", 0, new CacheCallback(exists));
+}
+
+var gGenerator = runTest();
+
+addLoadEvent(go);
+function go() {
+  SpecialPowers.pushPermissions(
+    [
+     { "type": "webapps-manage", "allow": 1, "context": document },
+     { "type": "embed-apps", "allow": 1, "context": document },
+     { "type": "browser", "allow": 1, "context": document }
+    ],
+    function() {
+      SpecialPowers.pushPrefEnv({"set": [['network.http.enable-packaged-apps', true],
+        ["dom.mozBrowserFramesEnabled", true]]},
+        function() { gGenerator.next(); } );
+    });
+}
+
+function continueTest() {
+  try {
+    gGenerator.next();
+  } catch (e if e instanceof StopIteration) {
+    finish();
+  }
+}
+
+function finish() {
+  SimpleTest.finish();
+}
+
+function cbError(aEvent) {
+  ok(false, "Error callback invoked " +
+            aEvent.target.error.name + " " + aEvent.target.error.message);
+  finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  // Install the app
+  SpecialPowers.setAllAppsLaunchable(true);
+  var manifestURL = "http://test/tests/dom/apps/tests/file_app.sjs?apptype=hosted&getmanifest=true";
+
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  SpecialPowers.autoConfirmAppUninstall(continueTest);
+  yield undefined;
+
+  var request = navigator.mozApps.install(manifestURL, { });
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+
+  var app = request.result;
+  ok(app, "App is non-null");
+  is(app.manifestURL, manifestURL, "App manifest url is correct.");
+
+  // Get the AppID
+  let appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
+  let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
+
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs", false, appId);
+  yield undefined;
+
+  // Check that the entry does not exist
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//index.html", false, appId);
+  yield undefined;
+
+  // Run the app and load a resource from the package
+  runApp(app, continueTest);
+  yield undefined;
+
+  // Check that the cache entry exists after being loaded in the app
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//index.html", true, appId);
+  yield undefined;
+
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs", true, appId);
+  yield undefined;
+
+  request = navigator.mozApps.mgmt.uninstall(app);
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  is(request.result, manifestURL, "App uninstalled.");
+
+  // Check that the cache entry went away after uninstalling the app
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//index.html", false, appId);
+  yield undefined;
+
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs", false);
+  yield undefined;
+
+  // Check that the entry does not exist
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//scripts/app.js", false);
+  yield undefined;
+
+  // Test that we can load a file in an iframe, with default permissions
+  var iframe = document.createElement("iframe");
+  iframe.addEventListener("load", continueTest, false);
+  document.body.appendChild(iframe);
+  iframe.src = "http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//scripts/app.js";
+  yield undefined;
+  ok(iframe.contentWindow.document.body.innerHTML.includes("..."), "This is the right file");
+  document.body.removeChild(iframe);
+
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs", true);
+  yield undefined;
+
+  // Check that the entry now exists
+  checkCacheEntry("http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//scripts/app.js", true);
+  yield undefined;
+
+  // Test that a fetch works
+  fetch('http://mochi.test:8888/tests/netwerk/test/mochitests/web_packaged_app.sjs!//index.html')
+  .then(function(res) {
+    ok(res.ok, "completed fetch");
+    res.text().then(function(body) {
+      ok(body.includes("Web Packaged App Index"), "correct content");
+      continueTest();
+    }); }
+  , function(e) {
+    ok(false, "error in fetch");
+  });
+  yield undefined;
+
+}
+
+function runApp(aApp, aCallback) {
+  var ifr = document.createElement('iframe');
+  ifr.setAttribute('mozbrowser', 'true');
+  ifr.setAttribute('mozapp', aApp.manifestURL);
+  ifr.src = "web_packaged_app.sjs!//index.html";
+
+  ifr.addEventListener('mozbrowsershowmodalprompt', function onAlert(e) {
+    var message = e.detail.message;
+    info("Got message " + message);
+
+    if (message.startsWith("OK:")) {
+      ok(true, message.substring(3, message.length));
+    } else if (message.startsWith("ERROR:")) {
+      ok(false, message.substring(6, message.length));
+    } else if (message == "DONE") {
+      ok(true, "Done test");
+      ifr.removeEventListener('mozbrowsershowmodalprompt', onAlert, false);
+      document.body.removeChild(ifr);
+      continueTest();
+    }
+  }, false);
+
+  document.body.appendChild(ifr);
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/web_packaged_app.sjs
@@ -0,0 +1,35 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "application/package", false);
+  response.write(octetStreamData.getData());
+  return;
+}
+
+// The package content
+// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
+var octetStreamData = {
+  content: [
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script> alert('OK: hello'); alert('DONE'); </script>\r\n</head>\r\n  Web Packaged App Index\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_dns_disable_ipv4.js
@@ -0,0 +1,40 @@
+//
+// Tests that calling asyncResolve with the RESOLVE_DISABLE_IPV4 flag doesn't
+// return any IPv4 addresses.
+//
+
+var dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService);
+var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+var listener = {
+  onLookupComplete: function(inRequest, inRecord, inStatus) {
+    if (inStatus != Cr.NS_OK) {
+      do_check_eq(inStatus, Cr.NS_ERROR_UNKNOWN_HOST);
+      do_test_finished();
+      return;
+    }
+
+    while (true) {
+      try {
+        var answer = inRecord.getNextAddrAsString();
+        // If there is an answer it should be an IPv6  address
+        dump(answer);
+        do_check_true(answer.indexOf(':') != -1);
+      } catch (e) {
+        break;
+      }
+    }
+    do_test_finished();
+  }
+};
+
+function run_test() {
+  do_test_pending();
+  try {
+    dns.asyncResolve("example.org", Ci.nsIDNSService.RESOLVE_DISABLE_IPV4, listener, null);
+  } catch (e) {
+    dump(e);
+    do_check_true(false);
+    do_test_finished();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_dns_disable_ipv6.js
@@ -0,0 +1,41 @@
+//
+// Tests that calling asyncResolve with the RESOLVE_DISABLE_IPV6 flag doesn't
+// return any IPv6 addresses.
+//
+
+var dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService);
+var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+var listener = {
+  onLookupComplete: function(inRequest, inRecord, inStatus) {
+    if (inStatus != Cr.NS_OK) {
+      do_check_eq(inStatus, Cr.NS_ERROR_UNKNOWN_HOST);
+      do_test_finished();
+      return;
+    }
+
+    while (true) {
+      try {
+        var answer = inRecord.getNextAddrAsString();
+        // If there is an answer it should be an IPv4  address
+        dump(answer);
+        do_check_true(answer.indexOf(':') == -1);
+        do_check_true(answer.indexOf('.') != -1);
+      } catch (e) {
+        break;
+      }
+    }
+    do_test_finished();
+  }
+};
+
+function run_test() {
+  do_test_pending();
+  try {
+    dns.asyncResolve("example.com", Ci.nsIDNSService.RESOLVE_DISABLE_IPV6, listener, null);
+  } catch (e) {
+    dump(e);
+    do_check_true(false);
+    do_test_finished();
+  }
+}
--- a/netwerk/test/unit/test_packaged_app_service.js
+++ b/netwerk/test/unit/test_packaged_app_service.js
@@ -2,24 +2,24 @@
 // This file tests the packaged app service - nsIPackagedAppService
 // NOTE: The order in which tests are run is important
 //       If you need to add more tests, it's best to define them at the end
 //       of the file and to add them at the end of run_test
 //
 // ----------------------------------------------------------------------------
 //
 // test_bad_args
-//     - checks that calls to nsIPackagedAppService::requestURI do not accept a null argument
+//     - checks that calls to nsIPackagedAppService::GetResource do not accept a null argument
 // test_callback_gets_called
 //     - checks the regular use case -> requesting a resource should asynchronously return an entry
 // test_same_content
 //     - makes another request for the same file, and checks that the same content is returned
 // test_request_number
 //     - this test does not make a request, but checks that the package has only
-//       been requested once. The entry returned by the call to requestURI in
+//       been requested once. The entry returned by the call to getResource in
 //       test_same_content should be returned from the cache.
 //
 // test_package_does_not_exist
 //     - checks that requesting a file from a <package that does not exist>
 //       calls the listener with an error code
 // test_file_does_not_exist
 //     - checks that requesting a <subresource that doesn't exist> inside a
 //       package calls the listener with an error code
@@ -54,16 +54,23 @@ function packagedAppContentHandler(metad
 
   if (packagedAppRequestsMade == 3) {
     // The third request returns a 200 OK response with a slightly different content
     body = body.replace(/\.\.\./g, 'xxx');
   }
   response.bodyOutputStream.write(body, body.length);
 }
 
+function getPrincipal(url) {
+  let uri = createURI(url);
+  return Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+         .getService(Ci.nsIScriptSecurityManager)
+         .getNoAppCodebasePrincipal(uri);
+}
+
 // The package content
 // getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
 var testData = {
   content: [
    { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
    { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
    { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
   ],
@@ -87,17 +94,17 @@ var testData = {
 XPCOMUtils.defineLazyGetter(this, "uri", function() {
   return "http://localhost:" + httpserver.identity.primaryPort;
 });
 
 // The active http server initialized in run_test
 var httpserver = null;
 // The packaged app service initialized in run_test
 var paservice = null;
-// This variable is set before requestURI is called. The listener uses this variable
+// This variable is set before getResource is called. The listener uses this variable
 // to check the correct resource path for the returned entry
 var packagePath = null;
 
 function run_test()
 {
   // setup test
   httpserver = new HttpServer();
   httpserver.registerPathHandler("/package", packagedAppContentHandler);
@@ -165,47 +172,47 @@ packagedResourceListener.prototype = {
   }
 };
 
 var cacheListener = new packagedResourceListener(testData.content[0].data);
 // ----------------------------------------------------------------------------
 
 // These calls should fail, since one of the arguments is invalid or null
 function test_bad_args() {
-  Assert.throws(() => { paservice.requestURI(createURI("http://test.com"), LoadContextInfo.default, cacheListener); }, "url's with no !// aren't allowed");
-  Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), LoadContextInfo.default, null); }, "should have a callback");
-  Assert.throws(() => { paservice.requestURI(null, LoadContextInfo.default, cacheListener); }, "should have a URI");
-  Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
+  Assert.throws(() => { paservice.getResource(getPrincipal("http://test.com"), 0, LoadContextInfo.default, cacheListener); }, "url's with no !// aren't allowed");
+  Assert.throws(() => { paservice.getResource(getPrincipal("http://test.com/package!//test"), 0, LoadContextInfo.default, null); }, "should have a callback");
+  Assert.throws(() => { paservice.getResource(null, 0, LoadContextInfo.default, cacheListener); }, "should have a URI");
+  Assert.throws(() => { paservice.getResource(getPrincipal("http://test.com/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
   run_next_test();
 }
 
 // ----------------------------------------------------------------------------
 
 // This tests that the callback gets called, and the cacheListener gets the proper content.
 function test_callback_gets_called() {
   packagePath = "/package";
-  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+  paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, cacheListener);
 }
 
 // Tests that requesting the same resource returns the same content
 function test_same_content() {
   packagePath = "/package";
-  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+  paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, cacheListener);
 }
 
 // Check the content handler has been called the expected number of times.
 function test_request_number() {
   equal(packagedAppRequestsMade, 2, "2 requests are expected. First with content, second is a 304 not modified.");
   run_next_test();
 }
 
 // This tests that new content is returned if the package has been updated
 function test_updated_package() {
   packagePath = "/package";
-  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default,
+  paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default,
       new packagedResourceListener(testData.content[0].data.replace(/\.\.\./g, 'xxx')));
 }
 
 // ----------------------------------------------------------------------------
 
 // This listener checks that the requested resources are not returned
 // either because the package does not exist, or because the requested resource
 // is not contained in the package.
@@ -219,23 +226,23 @@ var listener404 = {
     ok(!entry, "There should be no entry");
     run_next_test();
   }
 };
 
 // Tests that an error is returned for a non existing package
 function test_package_does_not_exist() {
   packagePath = "/package_non_existent";
-  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, listener404);
+  paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, listener404);
 }
 
 // Tests that an error is returned for a non existing resource in a package
 function test_file_does_not_exist() {
   packagePath = "/package"; // This package exists
-  paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
+  paservice.getResource(getPrincipal(uri + packagePath + "!//file_non_existent.html"), 0, LoadContextInfo.default, listener404);
 }
 
 // ----------------------------------------------------------------------------
 
 // Broken package. The first and last resources do not contain a "Content-Location" header
 // and should be ignored.
 var badTestData = {
   content: [
@@ -266,18 +273,18 @@ function packagedAppBadContentHandler(me
   response.setHeader("Content-Type", 'application/package');
   var body = badTestData.getData();
   response.bodyOutputStream.write(body, body.length);
 }
 
 // Checks that the resource with the proper headers inside the bad package is still returned
 function test_bad_package() {
   packagePath = "/badPackage";
-  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+  paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, cacheListener);
 }
 
 // Checks that the request for a non-existent resource doesn't hang for a bad package
 function test_bad_package_404() {
   packagePath = "/badPackage";
-  paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
+  paservice.getResource(getPrincipal(uri + packagePath + "!//file_non_existent.html"), 0, LoadContextInfo.default, listener404);
 }
 
 // ----------------------------------------------------------------------------
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -319,8 +319,10 @@ skip-if = os != "win"
 # The local cert service used by this test is not currently shipped on Android
 skip-if = os == "android"
 [test_1073747.js]
 [test_multipart_streamconv_application_package.js]
 [test_safeoutputstream_append.js]
 [test_packaged_app_service.js]
 [test_suspend_channel_before_connect.js]
 [test_inhibit_caching.js]
+[test_dns_disable_ipv4.js]
+[test_dns_disable_ipv6.js]
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -156,30 +156,31 @@ this.BrowserTestUtils = {
   /**
    * Waits for the next tab to open and load a given URL.
    *
    * The method doesn't wait for the tab contents to load.
    *
    * @param {tabbrowser} tabbrowser
    *        The tabbrowser to look for the next new tab in.
    * @param {string} url
-   *        A string URL to look for in the new tab.
+   *        A string URL to look for in the new tab. If null, allows any non-blank URL.
    *
    * @return {Promise}
    * @resolves With the {xul:tab} when a tab is opened and its location changes to the given URL.
    */
   waitForNewTab(tabbrowser, url) {
     return new Promise((resolve, reject) => {
       tabbrowser.tabContainer.addEventListener("TabOpen", function onTabOpen(openEvent) {
         tabbrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
 
         let progressListener = {
           onLocationChange(aBrowser) {
             if (aBrowser != openEvent.target.linkedBrowser ||
-                aBrowser.currentURI.spec != url) {
+                (url && aBrowser.currentURI.spec != url) ||
+                (!url && aBrowser.currentURI.spec == "about:blank")) {
               return;
             }
 
             tabbrowser.removeTabsProgressListener(progressListener);
             resolve(openEvent.target);
           },
         };
         tabbrowser.addTabsProgressListener(progressListener);
--- a/testing/mozharness/configs/android/androidarm_4_3.py
+++ b/testing/mozharness/configs/android/androidarm_4_3.py
@@ -80,16 +80,72 @@ config = {
         "jsreftest-5": {
             "category": "jsreftest",
             "extra_args": ["--this-chunk=5"],
         },
         "jsreftest-6": {
             "category": "jsreftest",
             "extra_args": ["--this-chunk=6"],
         },
+        "jsreftest-7": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=7"],
+        },
+        "jsreftest-8": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=8"],
+        },
+        "jsreftest-9": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=9"],
+        },
+        "jsreftest-10": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=10"],
+        },
+        "jsreftest-11": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=11"],
+        },
+        "jsreftest-12": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=12"],
+        },
+        "jsreftest-13": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=13"],
+        },
+        "jsreftest-14": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=14"],
+        },
+        "jsreftest-15": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=15"],
+        },
+        "jsreftest-16": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=16"],
+        },
+        "jsreftest-17": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=17"],
+        },
+        "jsreftest-18": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=18"],
+        },
+        "jsreftest-19": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=19"],
+        },
+        "jsreftest-20": {
+            "category": "jsreftest",
+            "extra_args": ["--this-chunk=20"],
+        },
         "mochitest-1": {
             "category": "mochitest",
             "extra_args": ["--total-chunks=16", "--this-chunk=1"],
         },
         "mochitest-2": {
             "category": "mochitest",
             "extra_args": ["--total-chunks=16", "--this-chunk=2"],
         },
@@ -166,102 +222,270 @@ config = {
             "extra_args": ["--this-chunk=3"],
         },
         "mochitest-gl-4": {
             "category": "mochitest-gl",
             "extra_args": ["--this-chunk=4"],
         },
         "reftest-1": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=1",
+            "extra_args": ["--total-chunks=48", "--this-chunk=1",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-2": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=2",
+            "extra_args": ["--total-chunks=48", "--this-chunk=2",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-3": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=3",
+            "extra_args": ["--total-chunks=48", "--this-chunk=3",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-4": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=4",
+            "extra_args": ["--total-chunks=48", "--this-chunk=4",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-5": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=5",
+            "extra_args": ["--total-chunks=48", "--this-chunk=5",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-6": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=6",
+            "extra_args": ["--total-chunks=48", "--this-chunk=6",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-7": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=7",
+            "extra_args": ["--total-chunks=48", "--this-chunk=7",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-8": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=8",
+            "extra_args": ["--total-chunks=48", "--this-chunk=8",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-9": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=9",
+            "extra_args": ["--total-chunks=48", "--this-chunk=9",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-10": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=10",
+            "extra_args": ["--total-chunks=48", "--this-chunk=10",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-11": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=11",
+            "extra_args": ["--total-chunks=48", "--this-chunk=11",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-12": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=12",
+            "extra_args": ["--total-chunks=48", "--this-chunk=12",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-13": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=13",
+            "extra_args": ["--total-chunks=48", "--this-chunk=13",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-14": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=14",
+            "extra_args": ["--total-chunks=48", "--this-chunk=14",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-15": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=15",
+            "extra_args": ["--total-chunks=48", "--this-chunk=15",
                 "tests/layout/reftests/reftest.list"]
         },
         "reftest-16": {
             "category": "reftest",
-            "extra_args": ["--total-chunks=16", "--this-chunk=16",
+            "extra_args": ["--total-chunks=48", "--this-chunk=16",
                 "tests/layout/reftests/reftest.list"]
         },
+        "reftest-17": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=17",
+            "tests/layout/reftests/reftest.list"]
+        },
+       "reftest-18": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=18",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-19": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=19",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-20": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=20",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-21": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=21",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-22": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=22",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-23": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=23",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-24": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=24",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-25": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=25",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-26": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=26",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-27": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=27",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-28": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=28",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-29": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=29",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-30": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=30",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-31": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=31",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-32": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=32",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-33": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=33",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-34": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=34",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-35": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=35",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-36": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=36",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-37": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=37",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-38": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=38",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-39": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=39",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-40": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=40",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-41": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=41",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-42": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=42",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-43": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=43",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-44": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=44",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-45": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=45",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-46": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=46",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-47": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=47",
+            "tests/layout/reftests/reftest.list"]
+        },
+        "reftest-48": {
+            "category": "reftest",
+            "extra args": ["--total-chunks=48", "--this-chunk=48",
+            "tests/layout/reftests/reftest.list"]
+        },
         "crashtest-1": {
             "category": "crashtest",
             "extra_args": ["--this-chunk=1"],
         },
         "crashtest-2": {
             "category": "crashtest",
             "extra_args": ["--this-chunk=2"],
         },
+        "crashtest-3": {
+            "category": "crashtest",
+            "extra_args": ["--this-chunk=3"],
+        },
+        "crashtest-4": {
+            "category": "crashtest",
+            "extra_args": ["--this-chunk=4"],
+        },
         "xpcshell-1": {
             "category": "xpcshell",
             "extra_args": ["--total-chunks=3", "--this-chunk=1"],
         },
         "xpcshell-2": {
             "category": "xpcshell",
             "extra_args": ["--total-chunks=3", "--this-chunk=2"],
         },
--- a/toolkit/components/viewsource/ViewSourceBrowser.jsm
+++ b/toolkit/components/viewsource/ViewSourceBrowser.jsm
@@ -8,28 +8,18 @@ const { utils: Cu, interfaces: Ci, class
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
   "resource://gre/modules/Deprecated.jsm");
 
-const NS_XHTML = "http://www.w3.org/1999/xhtml";
-const VIEW_SOURCE_CSS = "resource://gre-resources/viewsource.css";
 const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
 
-// These are markers used to delimit the selection during processing. They
-// are removed from the final rendering.
-// We use noncharacter Unicode codepoints to minimize the risk of clashing
-// with anything that might legitimately be present in the document.
-// U+FDD0..FDEF <noncharacters>
-const MARK_SELECTION_START = "\uFDD0";
-const MARK_SELECTION_END = "\uFDEF";
-
 const FRAME_SCRIPT = "chrome://global/content/viewSource-content.js";
 
 this.EXPORTED_SYMBOLS = ["ViewSourceBrowser"];
 
 // Keep a set of browsers we've seen before, so we can load our frame script as
 // needed into any new ones.
 let gKnownBrowsers = new WeakSet();
 
@@ -154,30 +144,16 @@ ViewSourceBrowser.prototype = {
   /**
    * Send a message to the view source browser.
    */
   sendAsyncMessage(...args) {
     this.browser.messageManager.sendAsyncMessage(...args);
   },
 
   /**
-   * Getter for the nsIWebNavigation of the view source browser.
-   */
-  get webNav() {
-    return this.browser.webNavigation;
-  },
-
-  /**
-   * Getter for whether long lines should be wrapped.
-   */
-  get wrapLongLines() {
-    return Services.prefs.getBoolPref("view_source.wrap_long_lines");
-  },
-
-  /**
    * A getter for the view source string bundle.
    */
   get bundle() {
     if (this._bundle) {
       return this._bundle;
     }
     return this._bundle = Services.strings.createBundle(BUNDLE_URL);
   },
@@ -219,16 +195,29 @@ ViewSourceBrowser.prototype = {
       }
     }
 
     this.sendAsyncMessage("ViewSource:LoadSource",
                           { URL, outerWindowID, lineNumber });
   },
 
   /**
+   * Loads a view source selection showing the given view-source url and
+   * highlight the selection.
+   *
+   * @param uri view-source uri to show
+   * @param drawSelection true to highlight the selection
+   * @param baseURI base URI of the original document
+   */
+  loadViewSourceFromSelection(URL, drawSelection, baseURI) {
+    this.sendAsyncMessage("ViewSource:LoadSourceWithSelection",
+                          { URL, drawSelection, baseURI });
+  },
+
+  /**
    * Updates the "remote" attribute of the view source browser. This
    * will remove the browser from the DOM, and then re-add it in the
    * same place it was taken from.
    *
    * @param shouldBeRemote
    *        True if the browser should be made remote. If the browsers
    *        remoteness already matches this value, this function does
    *        nothing.
@@ -239,361 +228,16 @@ ViewSourceBrowser.prototype = {
       // managing, we don't know for sure that it's safe to toggle remoteness.
       // For view source in a window, this is overridden to actually do the
       // flip if needed.
       throw new Error("View source browser's remoteness mismatch");
     }
   },
 
   /**
-   * Load the view source browser from a selection in some document.
-   *
-   * @param selection
-   *        A Selection object for the content of interest.
-   */
-  loadViewSourceFromSelection(selection) {
-    var range = selection.getRangeAt(0);
-    var ancestorContainer = range.commonAncestorContainer;
-    var doc = ancestorContainer.ownerDocument;
-
-    var startContainer = range.startContainer;
-    var endContainer = range.endContainer;
-    var startOffset = range.startOffset;
-    var endOffset = range.endOffset;
-
-    // let the ancestor be an element
-    var Node = doc.defaultView.Node;
-    if (ancestorContainer.nodeType == Node.TEXT_NODE ||
-        ancestorContainer.nodeType == Node.CDATA_SECTION_NODE)
-      ancestorContainer = ancestorContainer.parentNode;
-
-    // for selectAll, let's use the entire document, including <html>...</html>
-    // @see nsDocumentViewer::SelectAll() for how selectAll is implemented
-    try {
-      if (ancestorContainer == doc.body)
-        ancestorContainer = doc.documentElement;
-    } catch (e) { }
-
-    // each path is a "child sequence" (a.k.a. "tumbler") that
-    // descends from the ancestor down to the boundary point
-    var startPath = this._getPath(ancestorContainer, startContainer);
-    var endPath = this._getPath(ancestorContainer, endContainer);
-
-    // clone the fragment of interest and reset everything to be relative to it
-    // note: it is with the clone that we operate/munge from now on.  Also note
-    // that we clone into a data document to prevent images in the fragment from
-    // loading and the like.  The use of importNode here, as opposed to adoptNode,
-    // is _very_ important.
-    // XXXbz wish there were a less hacky way to create an untrusted document here
-    var isHTML = (doc.createElement("div").tagName == "DIV");
-    var dataDoc = isHTML ?
-      ancestorContainer.ownerDocument.implementation.createHTMLDocument("") :
-      ancestorContainer.ownerDocument.implementation.createDocument("", "", null);
-    ancestorContainer = dataDoc.importNode(ancestorContainer, true);
-    startContainer = ancestorContainer;
-    endContainer = ancestorContainer;
-
-    // Only bother with the selection if it can be remapped. Don't mess with
-    // leaf elements (such as <isindex>) that secretly use anynomous content
-    // for their display appearance.
-    var canDrawSelection = ancestorContainer.hasChildNodes();
-    var tmpNode;
-    if (canDrawSelection) {
-      var i;
-      for (i = startPath ? startPath.length-1 : -1; i >= 0; i--) {
-        startContainer = startContainer.childNodes.item(startPath[i]);
-      }
-      for (i = endPath ? endPath.length-1 : -1; i >= 0; i--) {
-        endContainer = endContainer.childNodes.item(endPath[i]);
-      }
-
-      // add special markers to record the extent of the selection
-      // note: |startOffset| and |endOffset| are interpreted either as
-      // offsets in the text data or as child indices (see the Range spec)
-      // (here, munging the end point first to keep the start point safe...)
-      if (endContainer.nodeType == Node.TEXT_NODE ||
-          endContainer.nodeType == Node.CDATA_SECTION_NODE) {
-        // do some extra tweaks to try to avoid the view-source output to look like
-        // ...<tag>]... or ...]</tag>... (where ']' marks the end of the selection).
-        // To get a neat output, the idea here is to remap the end point from:
-        // 1. ...<tag>]...   to   ...]<tag>...
-        // 2. ...]</tag>...  to   ...</tag>]...
-        if ((endOffset > 0 && endOffset < endContainer.data.length) ||
-            !endContainer.parentNode || !endContainer.parentNode.parentNode)
-          endContainer.insertData(endOffset, MARK_SELECTION_END);
-        else {
-          tmpNode = dataDoc.createTextNode(MARK_SELECTION_END);
-          endContainer = endContainer.parentNode;
-          if (endOffset === 0)
-            endContainer.parentNode.insertBefore(tmpNode, endContainer);
-          else
-            endContainer.parentNode.insertBefore(tmpNode, endContainer.nextSibling);
-        }
-      }
-      else {
-        tmpNode = dataDoc.createTextNode(MARK_SELECTION_END);
-        endContainer.insertBefore(tmpNode, endContainer.childNodes.item(endOffset));
-      }
-
-      if (startContainer.nodeType == Node.TEXT_NODE ||
-          startContainer.nodeType == Node.CDATA_SECTION_NODE) {
-        // do some extra tweaks to try to avoid the view-source output to look like
-        // ...<tag>[... or ...[</tag>... (where '[' marks the start of the selection).
-        // To get a neat output, the idea here is to remap the start point from:
-        // 1. ...<tag>[...   to   ...[<tag>...
-        // 2. ...[</tag>...  to   ...</tag>[...
-        if ((startOffset > 0 && startOffset < startContainer.data.length) ||
-            !startContainer.parentNode || !startContainer.parentNode.parentNode ||
-            startContainer != startContainer.parentNode.lastChild)
-          startContainer.insertData(startOffset, MARK_SELECTION_START);
-        else {
-          tmpNode = dataDoc.createTextNode(MARK_SELECTION_START);
-          startContainer = startContainer.parentNode;
-          if (startOffset === 0)
-            startContainer.parentNode.insertBefore(tmpNode, startContainer);
-          else
-            startContainer.parentNode.insertBefore(tmpNode, startContainer.nextSibling);
-        }
-      }
-      else {
-        tmpNode = dataDoc.createTextNode(MARK_SELECTION_START);
-        startContainer.insertBefore(tmpNode, startContainer.childNodes.item(startOffset));
-      }
-    }
-
-    // now extract and display the syntax highlighted source
-    tmpNode = dataDoc.createElementNS(NS_XHTML, "div");
-    tmpNode.appendChild(ancestorContainer);
-
-    // Tell content to draw a selection after the load below
-    if (canDrawSelection) {
-      this.sendAsyncMessage("ViewSource:ScheduleDrawSelection");
-    }
-
-    // all our content is held by the data:URI and URIs are internally stored as utf-8 (see nsIURI.idl)
-    var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
-    var referrerPolicy = Components.interfaces.nsIHttpChannel.REFERRER_POLICY_DEFAULT;
-    this.webNav.loadURIWithOptions((isHTML ?
-                                    "view-source:data:text/html;charset=utf-8," :
-                                    "view-source:data:application/xml;charset=utf-8,")
-                                   + encodeURIComponent(tmpNode.innerHTML),
-                                   loadFlags,
-                                   null, referrerPolicy,  // referrer
-                                   null, null,  // postData, headers
-                                   Services.io.newURI(doc.baseURI, null, null));
-  },
-
-  /**
-   * A helper to get a path like FIXptr, but with an array instead of the
-   * "tumbler" notation.
-   * See FIXptr: http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
-   */
-  _getPath(ancestor, node) {
-    var n = node;
-    var p = n.parentNode;
-    if (n == ancestor || !p)
-      return null;
-    var path = new Array();
-    if (!path)
-      return null;
-    do {
-      for (var i = 0; i < p.childNodes.length; i++) {
-        if (p.childNodes.item(i) == n) {
-          path.push(i);
-          break;
-        }
-      }
-      n = p;
-      p = n.parentNode;
-    } while (n != ancestor && p);
-    return path;
-  },
-
-  /**
-   * Load the view source browser from a fragment of some document, as in
-   * markups such as MathML where reformatting the output is helpful.
-   *
-   * @param aNode
-   *        Some element within the fragment of interest.
-   * @param aContext
-   *        A string denoting the type of fragment.  Currently, "mathml" is the
-   *        only accepted value.
-   */
-  loadViewSourceFromFragment(node, context) {
-    var Node = node.ownerDocument.defaultView.Node;
-    this._lineCount = 0;
-    this._startTargetLine = 0;
-    this._endTargetLine = 0;
-    this._targetNode = node;
-    if (this._targetNode && this._targetNode.nodeType == Node.TEXT_NODE)
-      this._targetNode = this._targetNode.parentNode;
-
-    // walk up the tree to the top-level element (e.g., <math>, <svg>)
-    var topTag;
-    if (context == "mathml")
-      topTag = "math";
-    else
-      throw "not reached";
-    var topNode = this._targetNode;
-    while (topNode && topNode.localName != topTag)
-      topNode = topNode.parentNode;
-    if (!topNode)
-      return;
-
-    // serialize
-    var title = this.bundle.GetStringFromName("viewMathMLSourceTitle");
-    var wrapClass = this.wrapLongLines ? ' class="wrap"' : '';
-    var source =
-      '<!DOCTYPE html>'
-    + '<html>'
-    + '<head><title>' + title + '</title>'
-    + '<link rel="stylesheet" type="text/css" href="' + VIEW_SOURCE_CSS + '">'
-    + '<style type="text/css">'
-    + '#target { border: dashed 1px; background-color: lightyellow; }'
-    + '</style>'
-    + '</head>'
-    + '<body id="viewsource"' + wrapClass
-    +        ' onload="document.title=\''+title+'\'; document.getElementById(\'target\').scrollIntoView(true)">'
-    + '<pre>'
-    + this._getOuterMarkup(topNode, 0)
-    + '</pre></body></html>'
-    ; // end
-
-    // display
-    this.browser.loadURI("data:text/html;charset=utf-8," +
-                         encodeURIComponent(source));
-  },
-
-  _getInnerMarkup(node, indent) {
-    var str = '';
-    for (var i = 0; i < node.childNodes.length; i++) {
-      str += this._getOuterMarkup(node.childNodes.item(i), indent);
-    }
-    return str;
-  },
-