Bug 1508693 - Add middleman call support for functions used in rendering widgets, r=lsmyth.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 23 Nov 2018 09:06:16 -1000
changeset 504622 70f4239b2fab7f622de52ba287e6eaa105dd604d
parent 504621 98430e6d541b8e5ffd0492d84afd5c031eb79905
child 504623 3f77f0486954f769e7870d02386ea15ca53f82ff
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsmyth
bugs1508693
milestone65.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
Bug 1508693 - Add middleman call support for functions used in rendering widgets, r=lsmyth.
toolkit/recordreplay/MiddlemanCall.cpp
toolkit/recordreplay/ProcessRedirect.h
toolkit/recordreplay/ProcessRedirectDarwin.cpp
--- a/toolkit/recordreplay/MiddlemanCall.cpp
+++ b/toolkit/recordreplay/MiddlemanCall.cpp
@@ -39,25 +39,31 @@ InitializeMiddlemanCalls()
 // not been sent to the middleman yet, filling aOutgoingCalls with the set of
 // such calls.
 static bool
 GatherDependentCalls(InfallibleVector<MiddlemanCall*>& aOutgoingCalls, MiddlemanCall* aCall)
 {
   MOZ_RELEASE_ASSERT(!aCall->mSent);
   aCall->mSent = true;
 
+  const Redirection& redirection = gRedirections[aCall->mCallId];
+
   CallArguments arguments;
   aCall->mArguments.CopyTo(&arguments);
 
   InfallibleVector<MiddlemanCall*> dependentCalls;
 
   MiddlemanCallContext cx(aCall, &arguments, MiddlemanCallPhase::ReplayInput);
   cx.mDependentCalls = &dependentCalls;
-  gRedirections[aCall->mCallId].mMiddlemanCall(cx);
+  redirection.mMiddlemanCall(cx);
   if (cx.mFailed) {
+    if (child::CurrentRepaintCannotFail()) {
+      child::ReportFatalError(Nothing(), "Middleman call input failed: %s\n",
+                              redirection.mName);
+    }
     return false;
   }
 
   for (MiddlemanCall* dependent : dependentCalls) {
     if (!dependent->mSent && !GatherDependentCalls(aOutgoingCalls, dependent)) {
       return false;
     }
   }
@@ -86,16 +92,20 @@ SendCallToMiddleman(size_t aCallId, Call
 
   // Perform the ReplayPreface phase on the new call.
   {
     MiddlemanCallContext cx(newCall, aArguments, MiddlemanCallPhase::ReplayPreface);
     redirection.mMiddlemanCall(cx);
     if (cx.mFailed) {
       delete newCall;
       gMiddlemanCalls.popBack();
+      if (child::CurrentRepaintCannotFail()) {
+        child::ReportFatalError(Nothing(), "Middleman call preface failed: %s\n",
+                                redirection.mName);
+      }
       return false;
     }
   }
 
   // Other phases will not run if we have not diverged from the recording.
   // Any outputs for the call have been handled by the SaveOutput hook.
   if (!aDiverged) {
     return true;
--- a/toolkit/recordreplay/ProcessRedirect.h
+++ b/toolkit/recordreplay/ProcessRedirect.h
@@ -109,37 +109,45 @@ public:
 // were initially invoked.
 //
 // Arguments and return value indexes refer to the register contents as passed
 // to the function originally. For functions with complex or floating point
 // arguments and return values, the right index to use might be different than
 // expected, per the requirements of the System V x64 ABI.
 struct CallArguments : public CallRegisterArguments
 {
+  // The maximum number of stack arguments that can be captured.
+  static const size_t NumStackArguments = 64;
+
 protected:
-  size_t stack[64]; // 104
-                    // Size: 616
+  size_t stack[NumStackArguments]; // 104
+                                   // Size: 616
 
 public:
-  template <size_t Index, typename T>
-  T& Arg() {
+  template <typename T>
+  T& Arg(size_t aIndex) {
     static_assert(sizeof(T) == sizeof(size_t), "Size must match");
     static_assert(IsFloatingPoint<T>::value == false, "FloatArg NYI");
-    static_assert(Index < 70, "Bad index");
-    switch (Index) {
+    MOZ_RELEASE_ASSERT(aIndex < 70);
+    switch (aIndex) {
     case 0: return (T&)arg0;
     case 1: return (T&)arg1;
     case 2: return (T&)arg2;
     case 3: return (T&)arg3;
     case 4: return (T&)arg4;
     case 5: return (T&)arg5;
-    default: return (T&)stack[Index - 6];
+    default: return (T&)stack[aIndex - 6];
     }
   }
 
+  template <size_t Index, typename T>
+  T& Arg() {
+    return Arg<T>(Index);
+  }
+
   template <size_t Offset>
   size_t* StackAddress() {
     static_assert(Offset % sizeof(size_t) == 0, "Bad stack offset");
     return &stack[Offset / sizeof(size_t)];
   }
 
   template <typename T, size_t Index = 0>
   T& Rval() {
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp
+++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp
@@ -342,70 +342,100 @@ namespace recordreplay {
   MACRO(CFURLCreateWithFileSystemPath, RR_ScalarRval)            \
   MACRO(CFURLCreateWithString, RR_ScalarRval)                    \
   MACRO(CFURLGetFileSystemRepresentation, RR_Compose<RR_ScalarRval, RR_WriteBuffer<2, 3>>) \
   MACRO(CFURLGetFSRef, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<1, sizeof(FSRef)>>) \
   MACRO(CFUUIDCreate, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
   MACRO(CFUUIDCreateString, RR_ScalarRval)                       \
   MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGAffineTransformConcat, RR_OversizeRval<sizeof(CGAffineTransform)>) \
-  MACRO(CGBitmapContextCreateImage, RR_ScalarRval)               \
+  MACRO(CGBitmapContextCreateImage, RR_ScalarRval, nullptr,      \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGBitmapContextCreateWithData,                           \
         RR_Compose<RR_ScalarRval, RR_CGBitmapContextCreateWithData>, nullptr, \
         Middleman_CGBitmapContextCreateWithData)                 \
   MACRO(CGBitmapContextGetBytesPerRow, RR_ScalarRval)            \
   MACRO(CGBitmapContextGetHeight, RR_ScalarRval)                 \
   MACRO(CGBitmapContextGetWidth, RR_ScalarRval)                  \
   MACRO(CGColorRelease, RR_ScalarRval)                           \
   MACRO(CGColorSpaceCopyICCProfile, RR_ScalarRval)               \
   MACRO(CGColorSpaceCreateDeviceGray, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
   MACRO(CGColorSpaceCreateDeviceRGB, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
   MACRO(CGColorSpaceCreatePattern, RR_ScalarRval)                \
   MACRO(CGColorSpaceRelease, RR_ScalarRval, nullptr, nullptr, Preamble_Veto<0>) \
-  MACRO(CGContextBeginTransparencyLayerWithRect)                 \
-  MACRO(CGContextClipToRects, RR_ScalarRval, nullptr,            \
+  MACRO(CGContextAddPath, nullptr, nullptr,                      \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>>) \
+  MACRO(CGContextBeginTransparencyLayerWithRect, nullptr, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_StackArgumentData<sizeof(CGRect)>, \
+                          Middleman_CFTypeArg<1>>)               \
+  MACRO(CGContextClipToRect, nullptr, nullptr,                   \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_StackArgumentData<sizeof(CGRect)>>) \
+  MACRO(CGContextClipToRects, nullptr, nullptr,                  \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_Buffer<1, 2, CGRect>>) \
   MACRO(CGContextConcatCTM, nullptr, nullptr,                    \
         Middleman_Compose<Middleman_CFTypeArg<0>,                \
                           Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
-  MACRO(CGContextDrawImage, RR_FlushCGContext<0>)                \
-  MACRO(CGContextEndTransparencyLayer)                           \
+  MACRO(CGContextDrawImage, RR_FlushCGContext<0>, nullptr,       \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_StackArgumentData<sizeof(CGRect)>, \
+                          Middleman_CFTypeArg<1>,                \
+                          Middleman_FlushCGContext<0>>)          \
+  MACRO(CGContextDrawLinearGradient, RR_FlushCGContext<0>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_CFTypeArg<1>,                \
+                          Middleman_StackArgumentData<2 * sizeof(CGPoint)>, \
+                          Middleman_FlushCGContext<0>>)          \
+  MACRO(CGContextEndTransparencyLayer, nullptr, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGContextFillPath, RR_FlushCGContext<0>, nullptr,        \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_FlushCGContext<0>>) \
   MACRO(CGContextFillRect, RR_FlushCGContext<0>, nullptr,        \
         Middleman_Compose<Middleman_CFTypeArg<0>,                \
                           Middleman_StackArgumentData<sizeof(CGRect)>, \
                           Middleman_FlushCGContext<0>>)          \
   MACRO(CGContextGetClipBoundingBox, RR_OversizeRval<sizeof(CGRect)>) \
   MACRO(CGContextGetCTM, RR_OversizeRval<sizeof(CGAffineTransform)>) \
   MACRO(CGContextGetType, RR_ScalarRval)                         \
-  MACRO(CGContextGetUserSpaceToDeviceSpaceTransform, RR_OversizeRval<sizeof(CGAffineTransform)>) \
+  MACRO(CGContextGetUserSpaceToDeviceSpaceTransform, RR_OversizeRval<sizeof(CGAffineTransform)>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<1>, Middleman_OversizeRval<sizeof(CGAffineTransform)>>) \
   MACRO(CGContextRestoreGState, nullptr, Preamble_CGContextRestoreGState, \
         Middleman_UpdateCFTypeArg<0>)                            \
+  MACRO(CGContextRotateCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSaveGState, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetAllowsFontSubpixelPositioning, nullptr, nullptr, \
         Middleman_UpdateCFTypeArg<0>)                            \
   MACRO(CGContextSetAllowsFontSubpixelQuantization, nullptr, nullptr, \
         Middleman_UpdateCFTypeArg<0>)                            \
   MACRO(CGContextSetAlpha, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetBaseCTM, nullptr, nullptr,                   \
         Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
                           Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
   MACRO(CGContextSetCTM, nullptr, nullptr,                       \
         Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
                           Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
   MACRO(CGContextSetGrayFillColor, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
-  MACRO(CGContextSetRGBFillColor)                                \
+  MACRO(CGContextSetRGBFillColor, nullptr, nullptr,              \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                           Middleman_StackArgumentData<sizeof(CGFloat)>>) \
+  MACRO(CGContextSetRGBStrokeColor, nullptr, nullptr,            \
+        Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
+                          Middleman_StackArgumentData<sizeof(CGFloat)>>) \
   MACRO(CGContextSetShouldAntialias, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetShouldSmoothFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetShouldSubpixelPositionFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetShouldSubpixelQuantizeFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetTextDrawingMode, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetTextMatrix, nullptr, nullptr,                \
         Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
                           Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
   MACRO(CGContextScaleCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
+  MACRO(CGContextStrokeLineSegments, RR_FlushCGContext<0>, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_Buffer<1, 2, CGPoint>,       \
+                          Middleman_FlushCGContext<0>>)          \
   MACRO(CGContextTranslateCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGDataProviderCreateWithData, RR_Compose<RR_ScalarRval, RR_CGDataProviderCreateWithData>, \
         nullptr, Middleman_CGDataProviderCreateWithData)         \
   MACRO(CGDataProviderRelease, nullptr, nullptr, nullptr, Preamble_Veto<0>) \
   MACRO(CGDisplayCopyColorSpace, RR_ScalarRval)                  \
   MACRO(CGDisplayIOServicePort, RR_ScalarRval)                   \
   MACRO(CGEventSourceCounterForEventType, RR_ScalarRval)         \
   MACRO(CGFontCopyTableForTag, RR_ScalarRval, nullptr,           \
@@ -435,24 +465,33 @@ namespace recordreplay {
   MACRO(CGFontGetGlyphBBoxes, RR_Compose<RR_ScalarRval, RR_WriteBuffer<3, 2, CGRect>>, nullptr, \
         Middleman_Compose<Middleman_CFTypeArg<0>,                \
                           Middleman_Buffer<1, 2, CGGlyph>,       \
                           Middleman_WriteBuffer<3, 2, CGRect>>)  \
   MACRO(CGFontGetGlyphPath, RR_ScalarRval)                       \
   MACRO(CGFontGetLeading, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGFontGetUnitsPerEm, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGFontGetXHeight, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
+  MACRO(CGGradientCreateWithColorComponents, RR_ScalarRval, nullptr, \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_Buffer<1, 3, CGFloat>,       \
+                          Middleman_Buffer<2, 3, CGFloat>,       \
+                          Middleman_CreateCFTypeRval>)           \
   MACRO(CGImageGetHeight, RR_ScalarRval)                         \
   MACRO(CGImageGetWidth, RR_ScalarRval)                          \
-  MACRO(CGImageRelease, RR_ScalarRval)                           \
+  MACRO(CGImageRelease, RR_ScalarRval, nullptr, nullptr, Preamble_Veto<0>) \
   MACRO(CGMainDisplayID, RR_ScalarRval)                          \
   MACRO(CGPathAddPath)                                           \
   MACRO(CGPathApply, nullptr, Preamble_CGPathApply)              \
   MACRO(CGPathContainsPoint, RR_ScalarRval)                      \
   MACRO(CGPathCreateMutable, RR_ScalarRval)                      \
+  MACRO(CGPathCreateWithRoundedRect, RR_ScalarRval, nullptr,     \
+        Middleman_Compose<Middleman_StackArgumentData<sizeof(CGRect)>, \
+                          Middleman_BufferFixedSize<0, sizeof(CGAffineTransform)>, \
+                          Middleman_CreateCFTypeRval>)           \
   MACRO(CGPathGetBoundingBox, RR_OversizeRval<sizeof(CGRect)>)   \
   MACRO(CGPathGetCurrentPoint, RR_ComplexFloatRval)              \
   MACRO(CGPathIsEmpty, RR_ScalarRval)                            \
   MACRO(CGSSetDebugOptions, RR_ScalarRval)                       \
   MACRO(CGSShutdownServerConnections)                            \
   MACRO(CTFontCopyFamilyName, RR_ScalarRval, nullptr,            \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCopyFeatures, RR_ScalarRval, nullptr,              \
@@ -570,29 +609,68 @@ namespace recordreplay {
   MACRO(Gestalt, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<1, sizeof(SInt32)>>) \
   MACRO(GetEventClass, RR_ScalarRval)                            \
   MACRO(GetCurrentEventQueue, RR_ScalarRval)                     \
   MACRO(GetCurrentProcess,                                       \
         RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<0, sizeof(ProcessSerialNumber)>>) \
   MACRO(GetEventAttributes, RR_ScalarRval)                       \
   MACRO(GetEventDispatcherTarget, RR_ScalarRval)                 \
   MACRO(GetEventKind, RR_ScalarRval)                             \
-  MACRO(HIThemeDrawButton, RR_ScalarRval)                        \
-  MACRO(HIThemeDrawFrame, RR_ScalarRval)                         \
-  MACRO(HIThemeDrawGroupBox, RR_ScalarRval)                      \
-  MACRO(HIThemeDrawGrowBox, RR_ScalarRval)                       \
-  MACRO(HIThemeDrawMenuBackground, RR_ScalarRval)                \
-  MACRO(HIThemeDrawMenuItem, RR_ScalarRval)                      \
-  MACRO(HIThemeDrawMenuSeparator, RR_ScalarRval)                 \
-  MACRO(HIThemeDrawSeparator, RR_ScalarRval)                     \
-  MACRO(HIThemeDrawTabPane, RR_ScalarRval)                       \
-  MACRO(HIThemeDrawTrack, RR_ScalarRval)                         \
+  MACRO(HIThemeDrawButton,                                       \
+        RR_Compose<RR_WriteBufferFixedSize<4, sizeof(HIRect)>, RR_ScalarRval>, nullptr, \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeButtonDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>,          \
+                          Middleman_WriteBufferFixedSize<4, sizeof(HIRect)>>) \
+  MACRO(HIThemeDrawFrame, RR_ScalarRval, nullptr,                \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeFrameDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
+  MACRO(HIThemeDrawGroupBox, RR_ScalarRval, nullptr,             \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeGroupBoxDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
+  MACRO(HIThemeDrawGrowBox, RR_ScalarRval, nullptr,              \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIPoint)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeGrowBoxDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
+  MACRO(HIThemeDrawMenuBackground, RR_ScalarRval, nullptr,       \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeMenuDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
+  MACRO(HIThemeDrawMenuItem,                                     \
+        RR_Compose<RR_WriteBufferFixedSize<5, sizeof(HIRect)>, RR_ScalarRval>, nullptr, \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<2, sizeof(HIThemeMenuItemDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<3>,          \
+                          Middleman_WriteBufferFixedSize<5, sizeof(HIRect)>>) \
+  MACRO(HIThemeDrawMenuSeparator, RR_ScalarRval, nullptr,        \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<2, sizeof(HIThemeMenuItemDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<3>>)         \
+  MACRO(HIThemeDrawSeparator, RR_ScalarRval, nullptr,            \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeSeparatorDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
+  MACRO(HIThemeDrawTabPane, RR_ScalarRval, nullptr,              \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIRect)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeTabPaneDrawInfo)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
+  MACRO(HIThemeDrawTrack, RR_ScalarRval, nullptr,                \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIThemeTrackDrawInfo)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIRect)>, \
+                          Middleman_UpdateCFTypeArg<2>>)         \
   MACRO(HIThemeGetGrowBoxBounds,                                 \
-        RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<2, sizeof(HIRect)>>) \
-  MACRO(HIThemeSetFill, RR_ScalarRval)                           \
+        RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<2, sizeof(HIRect)>>, nullptr, \
+        Middleman_Compose<Middleman_BufferFixedSize<0, sizeof(HIPoint)>, \
+                          Middleman_BufferFixedSize<1, sizeof(HIThemeGrowBoxDrawInfo)>, \
+                          Middleman_WriteBufferFixedSize<2, sizeof(HIRect)>>) \
+  MACRO(HIThemeSetFill, RR_ScalarRval, nullptr, Middleman_UpdateCFTypeArg<2>) \
   MACRO(IORegistryEntrySearchCFProperty, RR_ScalarRval)          \
   MACRO(LSCopyAllHandlersForURLScheme, RR_ScalarRval)            \
   MACRO(LSCopyApplicationForMIMEType,                            \
         RR_Compose<RR_ScalarRval, RR_WriteOptionalBufferFixedSize<2, sizeof(CFURLRef)>>) \
   MACRO(LSCopyItemAttribute,                                     \
         RR_Compose<RR_ScalarRval, RR_WriteOptionalBufferFixedSize<3, sizeof(CFTypeRef)>>) \
   MACRO(LSCopyKindStringForMIMEType,                             \
         RR_Compose<RR_ScalarRval, RR_WriteOptionalBufferFixedSize<1, sizeof(CFStringRef)>>) \
@@ -601,19 +679,19 @@ namespace recordreplay {
                                    RR_WriteOptionalBufferFixedSize<4, sizeof(FSRef)>, \
                                    RR_WriteOptionalBufferFixedSize<5, sizeof(CFURLRef)>>) \
   MACRO(LSGetApplicationForURL, RR_Compose<                      \
                                    RR_ScalarRval,                \
                                    RR_WriteOptionalBufferFixedSize<2, sizeof(FSRef)>, \
                                    RR_WriteOptionalBufferFixedSize<3, sizeof(CFURLRef)>>) \
   MACRO(NSClassFromString, RR_ScalarRval, nullptr,               \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeRval>) \
-  MACRO(NSRectFill)                                              \
+  MACRO(NSRectFill, nullptr, nullptr, Middleman_NoOp)            \
   MACRO(NSSearchPathForDirectoriesInDomains, RR_ScalarRval)      \
-  MACRO(NSSetFocusRingStyle, RR_ScalarRval)                      \
+  MACRO(NSSetFocusRingStyle, nullptr, nullptr, Middleman_NoOp)   \
   MACRO(NSTemporaryDirectory, RR_ScalarRval)                     \
   MACRO(OSSpinLockLock, nullptr, Preamble_OSSpinLockLock)        \
   MACRO(ReleaseEvent, RR_ScalarRval)                             \
   MACRO(RemoveEventFromQueue, RR_ScalarRval)                     \
   MACRO(RetainEvent, RR_ScalarRval)                              \
   MACRO(SCDynamicStoreCopyProxies, RR_ScalarRval)                \
   MACRO(SCDynamicStoreCreate, RR_ScalarRval)                     \
   MACRO(SCDynamicStoreCreateRunLoopSource, RR_ScalarRval)        \
@@ -749,24 +827,38 @@ Middleman_ObjCInput(MiddlemanCallContext
 
   MOZ_RELEASE_ASSERT(aCx.AccessInput());
 
   if (aCx.mPhase == MiddlemanCallPhase::ReplayInput) {
     // Try to determine where this object came from.
 
     // List of the Objective C classes which messages might be sent to directly.
     static const char* gStaticClasses[] = {
-      "NSAutoreleasePool",
+      // Standard classes.
+      "NSBezierPath",
+      "NSButtonCell",
       "NSColor",
+      "NSComboBoxCell",
       "NSDictionary",
+      "NSGraphicsContext",
       "NSFont",
       "NSFontManager",
+      "NSLevelIndicatorCell",
       "NSNumber",
+      "NSPopUpButtonCell",
+      "NSProgressBarCell",
       "NSString",
       "NSWindow",
+
+      // Gecko defined classes.
+      "CellDrawView",
+      "CheckboxCell",
+      "RadioButtonCell",
+      "SearchFieldCellWithFocusRing",
+      "ToolbarSearchFieldCellWithFocusRing",
     };
 
     // Watch for messages sent to particular classes.
     for (const char* className : gStaticClasses) {
       Class cls = objc_lookUpClass(className);
       if (cls == (Class) *aThingPtr) {
         aCx.WriteInputScalar((size_t) ObjCInputKind::StaticClass);
         size_t len = strlen(className) + 1;
@@ -1668,25 +1760,30 @@ RR_objc_msgSend(Stream& aEvents, CallArg
     }
     aEvents.RecordOrReplayBytes(rval, len + 1);
   }
 }
 
 static PreambleResult
 MiddlemanPreamble_objc_msgSend(CallArguments* aArguments)
 {
+  auto obj = aArguments->Arg<0, id>();
   auto message = aArguments->Arg<1, const char*>();
 
+  // Fake object value which allows null checks in the caller to pass.
+  static const size_t FakeId = 1;
+
   // Ignore uses of NSAutoreleasePool after diverging from the recording.
   // These are not performed in the middleman because the middleman has its
   // own autorelease pool, and because the middleman can process calls from
   // multiple threads which will cause these messages to behave differently.
-  if (!strcmp(message, "alloc") ||
+  // release messages are also ignored, as for CFRelease.
+  if ((!strcmp(message, "alloc") && obj == (id) objc_lookUpClass("NSAutoreleasePool")) ||
+      (!strcmp(message, "init") && obj == (id) FakeId) ||
       !strcmp(message, "drain") ||
-      !strcmp(message, "init") ||
       !strcmp(message, "release")) {
     // Fake a return value in case the caller null checks it.
     aArguments->Rval<size_t>() = 1;
     return PreambleResult::Veto;
   }
 
   // Other messages will be handled by Middleman_objc_msgSend.
   return PreambleResult::Redirect;
@@ -1707,16 +1804,40 @@ Middleman_PerformSelector(MiddlemanCallC
       return;
     }
   }
 
   Middleman_AutoreleaseCFTypeRval(aCx);
 }
 
 static void
+Middleman_DictionaryWithObjectsAndKeys(MiddlemanCallContext& aCx)
+{
+  // Copy over all possible stack arguments.
+  Middleman_StackArgumentData<CallArguments::NumStackArguments * sizeof(size_t)>(aCx);
+
+  if (aCx.AccessPreface()) {
+    // Advance through the arguments until there is a null value. If there are
+    // too many arguments for the underlying CallArguments, we will safely
+    // crash when we hit their extent.
+    for (size_t i = 2;; i += 2) {
+      auto& value = aCx.mArguments->Arg<id>(i);
+      if (!value) {
+        break;
+      }
+      auto& key = aCx.mArguments->Arg<id>(i + 1);
+      Middleman_ObjCInput(aCx, &value);
+      Middleman_ObjCInput(aCx, &key);
+    }
+  }
+
+  Middleman_AutoreleaseCFTypeRval(aCx);
+}
+
+static void
 Middleman_DictionaryWithObjects(MiddlemanCallContext& aCx)
 {
   Middleman_Buffer<2, 4, const void*>(aCx);
   Middleman_Buffer<3, 4, const void*>(aCx);
 
   if (aCx.AccessPreface()) {
     auto objects = aCx.mArguments->Arg<2, const void**>();
     auto keys = aCx.mArguments->Arg<3, const void**>();
@@ -1752,33 +1873,90 @@ Middleman_NSStringGetCharacters(Middlema
     aCx.ReadOrWriteOutputBytes(buffer, len * sizeof(UniChar));
   }
 }
 
 struct ObjCMessageInfo
 {
   const char* mMessage;
   MiddlemanCallFn mMiddlemanCall;
+  bool mUpdatesObject;
 };
 
 // All Objective C messages that can be called in the middleman, and hooks for
-// capturing any inputs and outputs other than the object and message.
+// capturing any inputs and outputs other than the object, message, and scalar
+// arguments / return values.
 static ObjCMessageInfo gObjCMiddlemanCallMessages[] = {
+  // Generic
+  { "alloc", Middleman_CreateCFTypeRval },
+  { "init", Middleman_AutoreleaseCFTypeRval },
   { "performSelector:withObject:", Middleman_PerformSelector },
   { "respondsToSelector:", Middleman_CString<2> },
 
+  // NSAppearance
+  { "_drawInRect:context:options:",
+    Middleman_Compose<Middleman_StackArgumentData<sizeof(CGRect)>,
+                      Middleman_CFTypeArg<2>,
+                      Middleman_CFTypeArg<3>> },
+
   // NSArray
   { "count" },
   { "objectAtIndex:", Middleman_AutoreleaseCFTypeRval },
 
+  // NSBezierPath
+  { "addClip", Middleman_NoOp, true },
+  { "bezierPathWithRoundedRect:xRadius:yRadius:", Middleman_AutoreleaseCFTypeRval },
+
+  // NSCell
+  { "drawFocusRingMaskWithFrame:inView:",
+    Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_StackArgumentData<sizeof(CGRect)>> },
+  { "drawWithFrame:inView:",
+    Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_StackArgumentData<sizeof(CGRect)>> },
+  { "initTextCell:",
+    Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_AutoreleaseCFTypeRval> },
+  { "initTextCell:pullsDown:",
+    Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_AutoreleaseCFTypeRval> },
+  { "setAllowsMixedState:", Middleman_NoOp, true },
+  { "setBezeled:", Middleman_NoOp, true },
+  { "setBezelStyle:", Middleman_NoOp, true },
+  { "setButtonType:", Middleman_NoOp, true },
+  { "setControlSize:", Middleman_NoOp, true },
+  { "setControlTint:", Middleman_NoOp, true },
+  { "setCriticalValue:", Middleman_NoOp, true },
+  { "setDoubleValue:", Middleman_NoOp, true },
+  { "setEditable:", Middleman_NoOp, true },
+  { "setEnabled:", Middleman_NoOp, true },
+  { "setFocusRingType:", Middleman_NoOp, true },
+  { "setHighlighted:", Middleman_NoOp, true },
+  { "setHighlightsBy:", Middleman_NoOp, true },
+  { "setHorizontal:", Middleman_NoOp, true },
+  { "setIndeterminate:", Middleman_NoOp, true },
+  { "setMax:", Middleman_NoOp, true },
+  { "setMaxValue:", Middleman_NoOp, true },
+  { "setMinValue:", Middleman_NoOp, true },
+  { "setPlaceholderString:", Middleman_NoOp, true },
+  { "setPullsDown:", Middleman_NoOp, true },
+  { "setShowsFirstResponder:", Middleman_NoOp, true },
+  { "setState:", Middleman_NoOp, true },
+  { "setValue:", Middleman_NoOp, true },
+  { "setWarningValue:", Middleman_NoOp, true },
+  { "showsFirstResponder" },
+
   // NSColor
+  { "alphaComponent" },
+  { "colorWithDeviceRed:green:blue:alpha:",
+    Middleman_Compose<Middleman_StackArgumentData<sizeof(CGFloat)>, Middleman_AutoreleaseCFTypeRval> },
   { "currentControlTint" },
+  { "set", Middleman_NoOp, true },
 
   // NSDictionary
+  { "dictionaryWithObjectsAndKeys:", Middleman_DictionaryWithObjectsAndKeys },
   { "dictionaryWithObjects:forKeys:count:", Middleman_DictionaryWithObjects },
+  { "mutableCopy", Middleman_AutoreleaseCFTypeRval },
+  { "setObject:forKey:", Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_CFTypeArg<3>>, true },
 
   // NSFont
   { "boldSystemFontOfSize:", Middleman_AutoreleaseCFTypeRval },
   { "controlContentFontOfSize:", Middleman_AutoreleaseCFTypeRval },
   { "familyName", Middleman_AutoreleaseCFTypeRval },
   { "fontDescriptor", Middleman_AutoreleaseCFTypeRval },
   { "menuBarFontOfSize:", Middleman_AutoreleaseCFTypeRval },
   { "pointSize" },
@@ -1786,16 +1964,25 @@ static ObjCMessageInfo gObjCMiddlemanCal
   { "systemFontOfSize:", Middleman_AutoreleaseCFTypeRval },
   { "toolTipsFontOfSize:", Middleman_AutoreleaseCFTypeRval },
   { "userFontOfSize:", Middleman_AutoreleaseCFTypeRval },
 
   // NSFontManager
   { "availableMembersOfFontFamily:", Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_AutoreleaseCFTypeRval> },
   { "sharedFontManager", Middleman_AutoreleaseCFTypeRval },
 
+  // NSGraphicsContext
+  { "currentContext", Middleman_AutoreleaseCFTypeRval },
+  { "graphicsContextWithGraphicsPort:flipped:",
+    Middleman_Compose<Middleman_CFTypeArg<2>, Middleman_AutoreleaseCFTypeRval> },
+  { "graphicsPort", Middleman_AutoreleaseCFTypeRval },
+  { "restoreGraphicsState" },
+  { "saveGraphicsState" },
+  { "setCurrentContext:", Middleman_CFTypeArg<2> },
+
   // NSNumber
   { "numberWithBool:", Middleman_AutoreleaseCFTypeRval },
   { "unsignedIntValue" },
 
   // NSString
   { "getCharacters:", Middleman_NSStringGetCharacters },
   { "hasSuffix:", Middleman_CFTypeArg<2> },
   { "isEqualToString:", Middleman_CFTypeArg<2> },
@@ -1809,33 +1996,40 @@ static ObjCMessageInfo gObjCMiddlemanCal
 
   // UIFontDescriptor
   { "symbolicTraits" },
 };
 
 static void
 Middleman_objc_msgSend(MiddlemanCallContext& aCx)
 {
-  auto& object = aCx.mArguments->Arg<0, id>();
   auto message = aCx.mArguments->Arg<1, const char*>();
 
   for (const ObjCMessageInfo& info : gObjCMiddlemanCallMessages) {
     if (!strcmp(info.mMessage, message)) {
-      if (aCx.AccessPreface()) {
-        Middleman_ObjCInput(aCx, &object);
+      if (info.mUpdatesObject) {
+        Middleman_UpdateCFTypeArg<0>(aCx);
+      } else {
+        Middleman_CFTypeArg<0>(aCx);
       }
       if (info.mMiddlemanCall && !aCx.mFailed) {
         info.mMiddlemanCall(aCx);
       }
+      if (child::CurrentRepaintCannotFail() && aCx.mFailed) {
+        child::ReportFatalError(Nothing(), "Middleman message failure: %s\n", message);
+      }
       return;
     }
   }
 
   if (aCx.mPhase == MiddlemanCallPhase::ReplayPreface) {
     aCx.MarkAsFailed();
+    if (child::CurrentRepaintCannotFail()) {
+      child::ReportFatalError(Nothing(), "Could not perform middleman message: %s\n", message);
+    }
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Cocoa redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 static void