зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1574952 - Support calls to CGPathApply after diverging from the recording, r=loganfsmyth.
Differential Revision: https://phabricator.services.mozilla.com/D42576 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
fdb08df9ff
Коммит
a7817c43e7
|
@ -949,6 +949,7 @@ async function queuePauseData(point, trackCached, shouldSkipCallback) {
|
|||
// When the logpoint's text is resolved at this point then the pause data
|
||||
// will be fetched as well.
|
||||
if (
|
||||
point.position &&
|
||||
gLogpoints.some(({ position }) =>
|
||||
positionSubsumes(position, point.position)
|
||||
)
|
||||
|
@ -1476,6 +1477,14 @@ function handleResumeManifestResponse({
|
|||
const checkpoint = getSavedCheckpoint(point.checkpoint);
|
||||
getCheckpointInfo(checkpoint).debuggerStatements.push(point);
|
||||
}
|
||||
|
||||
// In repaint stress mode, the child process creates a checkpoint before every
|
||||
// paint. By gathering the pause data at these checkpoints, we will perform a
|
||||
// repaint at all of these checkpoints, ensuring that all the normal paints
|
||||
// can be repainted.
|
||||
if (RecordReplayControl.inRepaintStressMode()) {
|
||||
queuePauseData(point);
|
||||
}
|
||||
}
|
||||
|
||||
// If necessary, continue executing in the main child.
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace recordreplay {
|
|||
#define FOR_EACH_ORIGINAL_FUNCTION(MACRO) \
|
||||
MACRO(__workq_kernreturn) \
|
||||
MACRO(CFDataGetLength) \
|
||||
MACRO(CGPathApply) \
|
||||
MACRO(close) \
|
||||
MACRO(lseek) \
|
||||
MACRO(mach_absolute_time) \
|
||||
|
@ -88,7 +87,6 @@ FOR_EACH_ORIGINAL_FUNCTION(DECLARE_ORIGINAL_FUNCTION)
|
|||
|
||||
enum CallbackEvent {
|
||||
CallbackEvent_CFRunLoopPerformCallBack,
|
||||
CallbackEvent_CGPathApplierFunction
|
||||
};
|
||||
|
||||
typedef void (*CFRunLoopPerformCallBack)(void*);
|
||||
|
@ -102,54 +100,12 @@ static void CFRunLoopPerformCallBackWrapper(void* aInfo) {
|
|||
PauseMainThreadAndServiceCallbacks();
|
||||
}
|
||||
|
||||
static size_t CGPathElementPointCount(CGPathElement* aElement) {
|
||||
switch (aElement->type) {
|
||||
case kCGPathElementCloseSubpath:
|
||||
return 0;
|
||||
case kCGPathElementMoveToPoint:
|
||||
case kCGPathElementAddLineToPoint:
|
||||
return 1;
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
return 2;
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
return 3;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
static void CGPathApplierFunctionWrapper(void* aInfo, CGPathElement* aElement) {
|
||||
RecordReplayCallback(CGPathApplierFunction, &aInfo);
|
||||
|
||||
CGPathElement replayElement;
|
||||
if (IsReplaying()) {
|
||||
aElement = &replayElement;
|
||||
}
|
||||
|
||||
aElement->type = (CGPathElementType)RecordReplayValue(aElement->type);
|
||||
|
||||
size_t npoints = CGPathElementPointCount(aElement);
|
||||
if (IsReplaying()) {
|
||||
aElement->points = new CGPoint[npoints];
|
||||
}
|
||||
RecordReplayBytes(aElement->points, npoints * sizeof(CGPoint));
|
||||
|
||||
rrc.mFunction(aInfo, aElement);
|
||||
|
||||
if (IsReplaying()) {
|
||||
delete[] aElement->points;
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayInvokeCallback(size_t aCallbackId) {
|
||||
MOZ_RELEASE_ASSERT(IsReplaying());
|
||||
switch (aCallbackId) {
|
||||
case CallbackEvent_CFRunLoopPerformCallBack:
|
||||
CFRunLoopPerformCallBackWrapper(nullptr);
|
||||
break;
|
||||
case CallbackEvent_CGPathApplierFunction:
|
||||
CGPathApplierFunctionWrapper(nullptr, nullptr);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
@ -1716,27 +1672,106 @@ static void MM_CGDataProviderCreateWithData(MiddlemanCallContext& aCx) {
|
|||
}
|
||||
}
|
||||
|
||||
static size_t CGPathElementPointCount(CGPathElementType aType) {
|
||||
switch (aType) {
|
||||
case kCGPathElementCloseSubpath:
|
||||
return 0;
|
||||
case kCGPathElementMoveToPoint:
|
||||
case kCGPathElementAddLineToPoint:
|
||||
return 1;
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
return 2;
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
return 3;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
static void CGPathDataCallback(void* aData, const CGPathElement* aElement) {
|
||||
InfallibleVector<char>* data = (InfallibleVector<char>*) aData;
|
||||
|
||||
data->append((char) aElement->type);
|
||||
|
||||
for (size_t i = 0; i < CGPathElementPointCount(aElement->type); i++) {
|
||||
data->append((char*) &aElement->points[i], sizeof(CGPoint));
|
||||
}
|
||||
}
|
||||
|
||||
static void GetCGPathData(CGPathRef aPath, InfallibleVector<char>& aData) {
|
||||
CGPathApply(aPath, &aData, CGPathDataCallback);
|
||||
}
|
||||
|
||||
static void CGPathApplyWithData(const InfallibleVector<char>& aData,
|
||||
void* aInfo, CGPathApplierFunction aFunction) {
|
||||
size_t offset = 0;
|
||||
while (offset < aData.length()) {
|
||||
CGPathElement element;
|
||||
element.type = (CGPathElementType) aData[offset++];
|
||||
|
||||
CGPoint points[3];
|
||||
element.points = points;
|
||||
size_t pointCount = CGPathElementPointCount(element.type);
|
||||
MOZ_RELEASE_ASSERT(pointCount <= ArrayLength(points));
|
||||
for (size_t i = 0; i < pointCount; i++) {
|
||||
memcpy(&points[i], &aData[offset], sizeof(CGPoint));
|
||||
offset += sizeof(CGPoint);
|
||||
}
|
||||
|
||||
aFunction(aInfo, &element);
|
||||
}
|
||||
}
|
||||
|
||||
static PreambleResult Preamble_CGPathApply(CallArguments* aArguments) {
|
||||
if (AreThreadEventsPassedThrough()) {
|
||||
if (AreThreadEventsPassedThrough() || HasDivergedFromRecording()) {
|
||||
return PreambleResult::Redirect;
|
||||
}
|
||||
|
||||
auto& path = aArguments->Arg<0, CGPathRef>();
|
||||
auto& data = aArguments->Arg<1, void*>();
|
||||
auto& info = aArguments->Arg<1, void*>();
|
||||
auto& function = aArguments->Arg<2, CGPathApplierFunction>();
|
||||
|
||||
RegisterCallbackData(BitwiseCast<void*>(function));
|
||||
RegisterCallbackData(data);
|
||||
PassThroughThreadEventsAllowCallbacks([&]() {
|
||||
CallbackWrapperData wrapperData(function, data);
|
||||
CallFunction<void>(gOriginal_CGPathApply, path, &wrapperData,
|
||||
CGPathApplierFunctionWrapper);
|
||||
});
|
||||
RemoveCallbackData(data);
|
||||
InfallibleVector<char> pathData;
|
||||
if (IsRecording()) {
|
||||
AutoPassThroughThreadEvents pt;
|
||||
GetCGPathData(path, pathData);
|
||||
}
|
||||
size_t len = RecordReplayValue(pathData.length());
|
||||
if (IsReplaying()) {
|
||||
pathData.appendN(0, len);
|
||||
}
|
||||
RecordReplayBytes(pathData.begin(), len);
|
||||
|
||||
CGPathApplyWithData(pathData, info, function);
|
||||
|
||||
return PreambleResult::Veto;
|
||||
}
|
||||
|
||||
static void MM_CGPathApply(MiddlemanCallContext& aCx) {
|
||||
MM_CFTypeArg<0>(aCx);
|
||||
MM_SkipInMiddleman(aCx);
|
||||
|
||||
if (aCx.AccessOutput()) {
|
||||
InfallibleVector<char> pathData;
|
||||
if (IsMiddleman()) {
|
||||
auto path = aCx.mArguments->Arg<0, CGPathRef>();
|
||||
GetCGPathData(path, pathData);
|
||||
}
|
||||
size_t len = pathData.length();
|
||||
aCx.ReadOrWriteOutputBytes(&len, sizeof(len));
|
||||
if (IsReplaying()) {
|
||||
pathData.appendN(0, len);
|
||||
}
|
||||
aCx.ReadOrWriteOutputBytes(pathData.begin(), len);
|
||||
|
||||
if (IsReplaying()) {
|
||||
auto& data = aCx.mArguments->Arg<1, void*>();
|
||||
auto& function = aCx.mArguments->Arg<2, CGPathApplierFunction>();
|
||||
CGPathApplyWithData(pathData, data, function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: We only redirect CTRunGetGlyphsPtr, not CTRunGetGlyphs. The latter may
|
||||
// be implemented with a loop that jumps back into the code we overwrite with a
|
||||
// jump, a pattern which ProcessRedirect.cpp does not handle. For the moment,
|
||||
|
@ -2301,7 +2336,7 @@ static SystemRedirection gSystemRedirections[] = {
|
|||
{"CGImageRelease", RR_ScalarRval, nullptr, nullptr, Preamble_Veto<0>},
|
||||
{"CGMainDisplayID", RR_ScalarRval},
|
||||
{"CGPathAddPath"},
|
||||
{"CGPathApply", nullptr, Preamble_CGPathApply},
|
||||
{"CGPathApply", nullptr, Preamble_CGPathApply, MM_CGPathApply },
|
||||
{"CGPathContainsPoint", RR_ScalarRval},
|
||||
{"CGPathCreateMutable", RR_ScalarRval},
|
||||
{"CGPathCreateWithRoundedRect", RR_ScalarRval, nullptr,
|
||||
|
|
|
@ -152,13 +152,6 @@ static bool HandleMessageInMiddleman(ipc::Side aSide,
|
|||
// Return whether a message should be sent to the recording child, even if it
|
||||
// is not currently active.
|
||||
static bool AlwaysForwardMessage(const IPC::Message& aMessage) {
|
||||
// Always forward messages in repaint stress mode, as the active child is
|
||||
// almost always a replaying child and lost messages make it hard to load
|
||||
// pages completely.
|
||||
if (InRepaintStressMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IPC::Message::msgid_t type = aMessage.type();
|
||||
|
||||
// Forward close messages so that the tab shuts down properly even if it is
|
||||
|
|
Загрузка…
Ссылка в новой задаче