forked from mirrors/gecko-dev
Backed out changeset d407a28318e6 (bug 1609815) for causing windows ming bustages CLOSED TREE
--HG-- extra : histedit_source : b2c748e31e0f6ba8fcf9960a336e0bbd361b07e6
This commit is contained in:
parent
236d721acb
commit
00dd87f6f4
276 changed files with 19236 additions and 403 deletions
|
|
@ -116,10 +116,15 @@ Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc)
|
||||||
mHideEventTarget(false) {
|
mHideEventTarget(false) {
|
||||||
mBits.groupInfo = nullptr;
|
mBits.groupInfo = nullptr;
|
||||||
mInt.mIndexOfEmbeddedChild = -1;
|
mInt.mIndexOfEmbeddedChild = -1;
|
||||||
|
|
||||||
|
// Assign an ID to this Accessible for use in UniqueID().
|
||||||
|
recordreplay::RegisterThing(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessible::~Accessible() {
|
Accessible::~Accessible() {
|
||||||
NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
|
NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
|
||||||
|
|
||||||
|
recordreplay::UnregisterThing(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ENameValueFlag Accessible::Name(nsString& aName) const {
|
ENameValueFlag Accessible::Name(nsString& aName) const {
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,15 @@ class Accessible : public nsISupports {
|
||||||
/**
|
/**
|
||||||
* Return the unique identifier of the accessible.
|
* Return the unique identifier of the accessible.
|
||||||
*/
|
*/
|
||||||
void* UniqueID() { return static_cast<void*>(this); }
|
void* UniqueID() {
|
||||||
|
// When recording or replaying, use an ID which will be consistent when
|
||||||
|
// recording/replaying (pointer values are not consistent), so that IPC
|
||||||
|
// messages from the parent process can be handled when replaying.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return reinterpret_cast<void*>(recordreplay::ThingIndex(this));
|
||||||
|
}
|
||||||
|
return static_cast<void*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return language associated with the accessible.
|
* Return language associated with the accessible.
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ async function testToggleToolboxButtons() {
|
||||||
];
|
];
|
||||||
|
|
||||||
// Filter out all the buttons which are not supported on the current target.
|
// Filter out all the buttons which are not supported on the current target.
|
||||||
// (DevTools Fission Preferences etc...)
|
// (WebReplay, DevTools Fission Preferences etc...)
|
||||||
const target = await TargetFactory.forTab(gBrowser.selectedTab);
|
const target = await TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
const toolbarButtons = toolbox.toolbarButtons.filter(tool =>
|
const toolbarButtons = toolbox.toolbarButtons.filter(tool =>
|
||||||
tool.isTargetSupported(target)
|
tool.isTargetSupported(target)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const {
|
||||||
getCSSStyleRules,
|
getCSSStyleRules,
|
||||||
} = require("devtools/shared/inspector/css-logic");
|
} = require("devtools/shared/inspector/css-logic");
|
||||||
const InspectorUtils = require("InspectorUtils");
|
const InspectorUtils = require("InspectorUtils");
|
||||||
|
const Debugger = require("Debugger");
|
||||||
|
|
||||||
// Set up a dummy environment so that EventUtils works. We need to be careful to
|
// Set up a dummy environment so that EventUtils works. We need to be careful to
|
||||||
// pass a window object into each EventUtils method we call rather than having
|
// pass a window object into each EventUtils method we call rather than having
|
||||||
|
|
@ -501,6 +502,13 @@ var TestActor = (exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
|
||||||
* Get the window which mouse events on node should be delivered to.
|
* Get the window which mouse events on node should be delivered to.
|
||||||
*/
|
*/
|
||||||
windowForMouseEvent: function(node) {
|
windowForMouseEvent: function(node) {
|
||||||
|
// When replaying, the node is a proxy for an element in the replaying
|
||||||
|
// process. Use the window which the server is running against, which is
|
||||||
|
// able to receive events. We can't use isReplaying here because this actor
|
||||||
|
// is loaded into its own sandbox.
|
||||||
|
if (Debugger.recordReplayProcessKind() == "Middleman") {
|
||||||
|
return this.targetActor.window;
|
||||||
|
}
|
||||||
return node.ownerDocument.defaultView;
|
return node.ownerDocument.defaultView;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace mozilla {
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver);
|
NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver);
|
||||||
|
|
||||||
StaticMutex TimelineConsumers::sMutex;
|
StaticMutexNotRecorded TimelineConsumers::sMutex;
|
||||||
|
|
||||||
// Manually manage this singleton's lifetime and destroy it before shutdown.
|
// Manually manage this singleton's lifetime and destroy it before shutdown.
|
||||||
// This avoids the leakchecker detecting false-positive memory leaks when
|
// This avoids the leakchecker detecting false-positive memory leaks when
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ class TimelineConsumers : public nsIObserver {
|
||||||
LinkedList<MarkersStorage> mMarkersStores;
|
LinkedList<MarkersStorage> mMarkersStores;
|
||||||
|
|
||||||
// Protects this class's data structures.
|
// Protects this class's data structures.
|
||||||
static StaticMutex sMutex;
|
static StaticMutexNotRecorded sMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,11 @@ TabGroup* TabGroup::GetFromWindow(mozIDOMWindowProxy* aWindow) {
|
||||||
TabGroup* TabGroup::GetFromActor(BrowserChild* aBrowserChild) {
|
TabGroup* TabGroup::GetFromActor(BrowserChild* aBrowserChild) {
|
||||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
// Middleman processes do not assign event targets to their tab children.
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
return GetChromeTabGroup();
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIEventTarget> target =
|
nsCOMPtr<nsIEventTarget> target =
|
||||||
aBrowserChild->Manager()->GetEventTargetFor(aBrowserChild);
|
aBrowserChild->Manager()->GetEventTargetFor(aBrowserChild);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,7 @@
|
||||||
#include "mozilla/HangAnnotations.h"
|
#include "mozilla/HangAnnotations.h"
|
||||||
#include "mozilla/Encoding.h"
|
#include "mozilla/Encoding.h"
|
||||||
#include "nsXULElement.h"
|
#include "nsXULElement.h"
|
||||||
|
#include "mozilla/RecordReplay.h"
|
||||||
#include "nsThreadManager.h"
|
#include "nsThreadManager.h"
|
||||||
#include "nsIBidiKeyboard.h"
|
#include "nsIBidiKeyboard.h"
|
||||||
#include "ReferrerInfo.h"
|
#include "ReferrerInfo.h"
|
||||||
|
|
@ -9904,6 +9905,12 @@ uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
|
||||||
MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
|
MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
|
||||||
uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
|
uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
|
||||||
|
|
||||||
|
// Set the high bit for middleman processes so it doesn't conflict with the
|
||||||
|
// content process's generated IDs.
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
bits |= uint64_t(1) << (kIdBits - 1);
|
||||||
|
}
|
||||||
|
|
||||||
return (processBits << kIdBits) | bits;
|
return (processBits << kIdBits) | bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@
|
||||||
#include "mozilla/dom/ipc/StructuredCloneData.h"
|
#include "mozilla/dom/ipc/StructuredCloneData.h"
|
||||||
#include "mozilla/dom/DOMStringList.h"
|
#include "mozilla/dom/DOMStringList.h"
|
||||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
#include "nsPrintfCString.h"
|
#include "nsPrintfCString.h"
|
||||||
#include "nsXULAppAPI.h"
|
#include "nsXULAppAPI.h"
|
||||||
#include "nsQueryObject.h"
|
#include "nsQueryObject.h"
|
||||||
|
|
@ -593,11 +594,35 @@ class MMListenerRemover {
|
||||||
RefPtr<nsFrameMessageManager> mMM;
|
RefPtr<nsFrameMessageManager> mMM;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When recording or replaying, return whether a message should be received in
|
||||||
|
// the middleman process instead of the recording/replaying process.
|
||||||
|
static bool DirectMessageToMiddleman(const nsAString& aMessage) {
|
||||||
|
// Middleman processes run developer tools server code and need to receive
|
||||||
|
// debugger related messages. The session store flush message needs to be
|
||||||
|
// received in order to cleanly shutdown the process.
|
||||||
|
return (StringBeginsWith(aMessage, NS_LITERAL_STRING("debug:")) &&
|
||||||
|
recordreplay::parent::DebuggerRunsInMiddleman()) ||
|
||||||
|
aMessage.EqualsLiteral("SessionStore:flush");
|
||||||
|
}
|
||||||
|
|
||||||
void nsFrameMessageManager::ReceiveMessage(
|
void nsFrameMessageManager::ReceiveMessage(
|
||||||
nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed,
|
nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed,
|
||||||
const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData,
|
const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData,
|
||||||
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
|
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
|
||||||
nsTArray<StructuredCloneData>* aRetVal, ErrorResult& aError) {
|
nsTArray<StructuredCloneData>* aRetVal, ErrorResult& aError) {
|
||||||
|
// If we are recording or replaying, we will end up here in both the
|
||||||
|
// middleman process and the recording/replaying process. Ignore the message
|
||||||
|
// in one of the processes, so that it is only received in one place.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
if (DirectMessageToMiddleman(aMessage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (recordreplay::IsMiddleman()) {
|
||||||
|
if (!DirectMessageToMiddleman(aMessage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(aTarget);
|
MOZ_ASSERT(aTarget);
|
||||||
|
|
||||||
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
||||||
|
|
|
||||||
|
|
@ -530,7 +530,8 @@ class ScriptErrorEvent : public Runnable {
|
||||||
JS::Rooted<JSObject*> stackGlobal(rootingCx);
|
JS::Rooted<JSObject*> stackGlobal(rootingCx);
|
||||||
xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack, &stack,
|
xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack, &stack,
|
||||||
&stackGlobal);
|
&stackGlobal);
|
||||||
mReport->LogToConsoleWithStack(stack, stackGlobal);
|
mReport->LogToConsoleWithStack(stack, stackGlobal,
|
||||||
|
JS::ExceptionTimeWarpTarget(mError));
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,11 @@ void nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper) {
|
||||||
if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
|
if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
|
||||||
CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
|
CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Never collect the wrapper object while recording or replaying, to avoid
|
||||||
|
// non-deterministic behaviors if the cache is emptied and then refilled at
|
||||||
|
// a different point when replaying.
|
||||||
|
recordreplay::HoldJSObject(aWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) {
|
void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) {
|
||||||
|
|
|
||||||
|
|
@ -4052,6 +4052,13 @@ void DeprecationWarning(JSContext* aCx, JSObject* aObject,
|
||||||
void DeprecationWarning(const GlobalObject& aGlobal,
|
void DeprecationWarning(const GlobalObject& aGlobal,
|
||||||
Document::DeprecatedOperations aOperation) {
|
Document::DeprecatedOperations aOperation) {
|
||||||
if (NS_IsMainThread()) {
|
if (NS_IsMainThread()) {
|
||||||
|
// After diverging from the recording, a replaying process is not able to
|
||||||
|
// report warnings and will be forced to rewind. Avoid reporting warnings
|
||||||
|
// in this case so that the debugger can access deprecated properties.
|
||||||
|
if (recordreplay::HasDivergedFromRecording()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||||
do_QueryInterface(aGlobal.GetAsSupports());
|
do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
if (window && window->GetExtantDoc()) {
|
if (window && window->GetExtantDoc()) {
|
||||||
|
|
|
||||||
|
|
@ -2653,6 +2653,11 @@ class MOZ_STACK_CLASS BindingJSObjectCreator {
|
||||||
void InitializationSucceeded() {
|
void InitializationSucceeded() {
|
||||||
T* pointer;
|
T* pointer;
|
||||||
mNative.forget(&pointer);
|
mNative.forget(&pointer);
|
||||||
|
|
||||||
|
// Never collect binding objects while recording or replaying, to avoid
|
||||||
|
// non-deterministically releasing references during finalization.
|
||||||
|
recordreplay::HoldJSObject(mReflector);
|
||||||
|
|
||||||
mReflector = nullptr;
|
mReflector = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,12 @@ interface nsIScriptError : nsIConsoleMessage
|
||||||
|
|
||||||
readonly attribute nsIArray notes;
|
readonly attribute nsIArray notes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we are recording or replaying, this value may identify the point
|
||||||
|
* where the error was generated, otherwise zero.
|
||||||
|
*/
|
||||||
|
attribute unsigned long long timeWarpTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the ScriptError is a CSS parser error, this value will contain the
|
* If the ScriptError is a CSS parser error, this value will contain the
|
||||||
* CSS selectors of the CSS ruleset where the error occured.
|
* CSS selectors of the CSS ruleset where the error occured.
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ nsScriptErrorBase::nsScriptErrorBase()
|
||||||
mOuterWindowID(0),
|
mOuterWindowID(0),
|
||||||
mInnerWindowID(0),
|
mInnerWindowID(0),
|
||||||
mTimeStamp(0),
|
mTimeStamp(0),
|
||||||
|
mTimeWarpTarget(0),
|
||||||
mInitializedOnMainThread(false),
|
mInitializedOnMainThread(false),
|
||||||
mIsFromPrivateWindow(false),
|
mIsFromPrivateWindow(false),
|
||||||
mIsFromChromeContext(false) {}
|
mIsFromChromeContext(false) {}
|
||||||
|
|
@ -385,6 +386,18 @@ nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsScriptErrorBase::SetTimeWarpTarget(uint64_t aTarget) {
|
||||||
|
mTimeWarpTarget = aTarget;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsScriptErrorBase::GetTimeWarpTarget(uint64_t* aTarget) {
|
||||||
|
*aTarget = mTimeWarpTarget;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsScriptErrorBase::GetIsFromChromeContext(bool* aIsFromChromeContext) {
|
nsScriptErrorBase::GetIsFromChromeContext(bool* aIsFromChromeContext) {
|
||||||
NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
|
NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ class nsScriptErrorBase : public nsIScriptError {
|
||||||
uint64_t mOuterWindowID;
|
uint64_t mOuterWindowID;
|
||||||
uint64_t mInnerWindowID;
|
uint64_t mInnerWindowID;
|
||||||
int64_t mTimeStamp;
|
int64_t mTimeStamp;
|
||||||
|
uint64_t mTimeWarpTarget;
|
||||||
// mInitializedOnMainThread, mIsFromPrivateWindow and mIsFromChromeContext are
|
// mInitializedOnMainThread, mIsFromPrivateWindow and mIsFromChromeContext are
|
||||||
// set on the main thread from InitializeOnMainThread().
|
// set on the main thread from InitializeOnMainThread().
|
||||||
mozilla::Atomic<bool> mInitializedOnMainThread;
|
mozilla::Atomic<bool> mInitializedOnMainThread;
|
||||||
|
|
|
||||||
|
|
@ -321,6 +321,17 @@ bool WebGLContext::CreateAndInitGL(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebGL can't be used when recording/replaying.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
FailureReason reason;
|
||||||
|
reason.info =
|
||||||
|
"Can't use WebGL when recording or replaying "
|
||||||
|
"(https://bugzil.la/1506467).";
|
||||||
|
out_failReasons->push_back(reason);
|
||||||
|
GenerateWarning("%s", reason.info.BeginReading());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// WebGL2 is separately blocked:
|
// WebGL2 is separately blocked:
|
||||||
if (IsWebGL2() && !forceEnabled) {
|
if (IsWebGL2() && !forceEnabled) {
|
||||||
const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
||||||
|
|
|
||||||
|
|
@ -1179,6 +1179,13 @@ HTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
||||||
return NS_BINDING_ABORTED;
|
return NS_BINDING_ABORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Media element playback is not currently supported when recording or
|
||||||
|
// replaying. See bug 1304146.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
mElement->ReportLoadError("Media elements not available when recording");
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
// The element is only needed until we've had a chance to call
|
// The element is only needed until we've had a chance to call
|
||||||
// InitializeDecoderForChannel. So make sure mElement is cleared here.
|
// InitializeDecoderForChannel. So make sure mElement is cleared here.
|
||||||
RefPtr<HTMLMediaElement> element;
|
RefPtr<HTMLMediaElement> element;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "mozilla/Decimal.h"
|
#include "mozilla/Decimal.h"
|
||||||
#include "mozilla/Maybe.h"
|
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "nsIConstraintValidation.h"
|
#include "nsIConstraintValidation.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,12 @@ interface nsIRemoteTab : nsISupports
|
||||||
*/
|
*/
|
||||||
void stopApzAutoscroll(in nsViewID aScrollId, in uint32_t aPresShellId);
|
void stopApzAutoscroll(in nsViewID aScrollId, in uint32_t aPresShellId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a recording of the associated content process' behavior to the
|
||||||
|
* specified filename. Returns whether the process is being recorded.
|
||||||
|
*/
|
||||||
|
bool saveRecording(in AString aFileName);
|
||||||
|
|
||||||
cenum NavigationType : 8 {
|
cenum NavigationType : 8 {
|
||||||
NAVIGATE_BACK = 0,
|
NAVIGATE_BACK = 0,
|
||||||
NAVIGATE_FORWARD = 1,
|
NAVIGATE_FORWARD = 1,
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@
|
||||||
#include "mozilla/layers/ShadowLayers.h"
|
#include "mozilla/layers/ShadowLayers.h"
|
||||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||||
#include "mozilla/plugins/PPluginWidgetChild.h"
|
#include "mozilla/plugins/PPluginWidgetChild.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
#include "nsBrowserStatusFilter.h"
|
#include "nsBrowserStatusFilter.h"
|
||||||
#include "nsColorPickerProxy.h"
|
#include "nsColorPickerProxy.h"
|
||||||
#include "nsCommandParams.h"
|
#include "nsCommandParams.h"
|
||||||
|
|
@ -290,7 +291,11 @@ class BrowserChild::DelayedDeleteRunnable final : public Runnable,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check in case ActorDestroy was called after RecvDestroy message.
|
// Check in case ActorDestroy was called after RecvDestroy message.
|
||||||
if (mBrowserChild->IPCOpen()) {
|
// Middleman processes with their own recording child process avoid
|
||||||
|
// sending a delete message, so that the parent process does not
|
||||||
|
// receive two deletes for the same actor.
|
||||||
|
if (mBrowserChild->IPCOpen() &&
|
||||||
|
!recordreplay::parent::IsMiddlemanWithRecordingChild()) {
|
||||||
Unused << PBrowserChild::Send__delete__(mBrowserChild);
|
Unused << PBrowserChild::Send__delete__(mBrowserChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,6 +589,11 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
|
||||||
|
|
||||||
mIPCOpen = true;
|
mIPCOpen = true;
|
||||||
|
|
||||||
|
// Recording/replaying processes use their own compositor.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
mPuppetWidget->CreateCompositor();
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_THUNDERBIRD) && \
|
#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_THUNDERBIRD) && \
|
||||||
!defined(MOZ_SUITE)
|
!defined(MOZ_SUITE)
|
||||||
mSessionStoreListener = new TabListener(docShell, nullptr);
|
mSessionStoreListener = new TabListener(docShell, nullptr);
|
||||||
|
|
@ -1175,6 +1185,13 @@ mozilla::ipc::IPCResult BrowserChild::RecvShow(
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have now done enough initialization for the record/replay system to
|
||||||
|
// create checkpoints. Create a checkpoint now, in case this process never
|
||||||
|
// paints later on (the usual place where checkpoints occur).
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
recordreplay::child::CreateCheckpoint();
|
||||||
|
}
|
||||||
|
|
||||||
UpdateVisibility();
|
UpdateVisibility();
|
||||||
|
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
|
|
@ -1222,7 +1239,9 @@ mozilla::ipc::IPCResult BrowserChild::RecvCompositorOptionsChanged(
|
||||||
|
|
||||||
mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions(
|
mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions(
|
||||||
const DimensionInfo& aDimensionInfo) {
|
const DimensionInfo& aDimensionInfo) {
|
||||||
if (mLayersConnected.isNothing()) {
|
// When recording/replaying we need to make sure the dimensions are up to
|
||||||
|
// date on the compositor used in this process.
|
||||||
|
if (mLayersConnected.isNothing() && !recordreplay::IsRecordingOrReplaying()) {
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2217,6 +2236,20 @@ mozilla::ipc::IPCResult BrowserChild::RecvActivateFrameEvent(
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return whether a remote script should be loaded in middleman processes in
|
||||||
|
// addition to any child recording process they have.
|
||||||
|
static bool LoadScriptInMiddleman(const nsString& aURL) {
|
||||||
|
return // Middleman processes run devtools server side scripts.
|
||||||
|
(StringBeginsWith(aURL, NS_LITERAL_STRING("resource://devtools/")) &&
|
||||||
|
recordreplay::parent::DebuggerRunsInMiddleman())
|
||||||
|
// This script includes event listeners needed to propagate document
|
||||||
|
// title changes.
|
||||||
|
|| aURL.EqualsLiteral("chrome://global/content/browser-child.js")
|
||||||
|
// This script is needed to respond to session store requests from the
|
||||||
|
// UI process.
|
||||||
|
|| aURL.EqualsLiteral("chrome://browser/content/content-sessionStore.js");
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript(
|
mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript(
|
||||||
const nsString& aURL, const bool& aRunInGlobalScope) {
|
const nsString& aURL, const bool& aRunInGlobalScope) {
|
||||||
if (!InitBrowserChildMessageManager())
|
if (!InitBrowserChildMessageManager())
|
||||||
|
|
@ -2231,6 +2264,11 @@ mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript(
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we only load whitelisted scripts in middleman processes.
|
||||||
|
if (recordreplay::IsMiddleman() && !LoadScriptInMiddleman(aURL)) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
LoadScriptInternal(mm, aURL, !aRunInGlobalScope);
|
LoadScriptInternal(mm, aURL, !aRunInGlobalScope);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
@ -2806,7 +2844,10 @@ void BrowserChild::InitAPZState() {
|
||||||
|
|
||||||
void BrowserChild::NotifyPainted() {
|
void BrowserChild::NotifyPainted() {
|
||||||
if (!mNotified) {
|
if (!mNotified) {
|
||||||
SendNotifyCompositorTransaction();
|
// Recording/replaying processes have a compositor but not a remote frame.
|
||||||
|
if (!recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
SendNotifyCompositorTransaction();
|
||||||
|
}
|
||||||
mNotified = true;
|
mNotified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,20 @@ BrowserHost::StopApzAutoscroll(nsViewID aScrollId, uint32_t aPresShellId) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* bool saveRecording (in AString aFileName); */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BrowserHost::SaveRecording(const nsAString& aFileName, bool* _retval) {
|
||||||
|
if (!mRoot) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
nsCOMPtr<nsIFile> file;
|
||||||
|
nsresult rv = NS_NewLocalFile(aFileName, false, getter_AddRefs(file));
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
return GetContentParent()->SaveRecording(file, _retval);
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
BrowserHost::MaybeCancelContentJSExecutionFromScript(
|
BrowserHost::MaybeCancelContentJSExecutionFromScript(
|
||||||
nsIRemoteTab::NavigationType aNavigationType,
|
nsIRemoteTab::NavigationType aNavigationType,
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,8 @@ BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId,
|
||||||
mHasPresented(false),
|
mHasPresented(false),
|
||||||
mIsReadyToHandleInputEvents(false),
|
mIsReadyToHandleInputEvents(false),
|
||||||
mIsMouseEnterIntoWidgetEventSuppressed(false),
|
mIsMouseEnterIntoWidgetEventSuppressed(false),
|
||||||
mIsDestroyingForProcessSwitch(false) {
|
mIsDestroyingForProcessSwitch(false),
|
||||||
|
mIsActiveRecordReplayTab(false) {
|
||||||
MOZ_ASSERT(aManager);
|
MOZ_ASSERT(aManager);
|
||||||
// When the input event queue is disabled, we don't need to handle the case
|
// When the input event queue is disabled, we don't need to handle the case
|
||||||
// that some input events are dispatched before PBrowserConstructor.
|
// that some input events are dispatched before PBrowserConstructor.
|
||||||
|
|
@ -645,6 +646,8 @@ void BrowserParent::DestroyInternal() {
|
||||||
->ParentDestroy();
|
->ParentDestroy();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SetIsActiveRecordReplayTab(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserParent::Destroy() {
|
void BrowserParent::Destroy() {
|
||||||
|
|
@ -3340,6 +3343,11 @@ void BrowserParent::SetDocShellIsActive(bool isActive) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Keep track of how many active recording/replaying tabs there are.
|
||||||
|
if (Manager()->IsRecordingOrReplaying()) {
|
||||||
|
SetIsActiveRecordReplayTab(isActive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserParent::GetHasPresented() { return mHasPresented; }
|
bool BrowserParent::GetHasPresented() { return mHasPresented; }
|
||||||
|
|
@ -3921,6 +3929,16 @@ void BrowserParent::SetBrowserHost(BrowserHost* aBrowser) {
|
||||||
mBrowserHost = aBrowser;
|
mBrowserHost = aBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
size_t BrowserParent::gNumActiveRecordReplayTabs;
|
||||||
|
|
||||||
|
void BrowserParent::SetIsActiveRecordReplayTab(bool aIsActive) {
|
||||||
|
if (aIsActive != mIsActiveRecordReplayTab) {
|
||||||
|
gNumActiveRecordReplayTabs += aIsActive ? 1 : -1;
|
||||||
|
mIsActiveRecordReplayTab = aIsActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult BrowserParent::RecvSetSystemFont(
|
mozilla::ipc::IPCResult BrowserParent::RecvSetSystemFont(
|
||||||
const nsCString& aFontName) {
|
const nsCString& aFontName) {
|
||||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,10 @@ class BrowserParent final : public PBrowserParent,
|
||||||
|
|
||||||
static TabId GetTabIdFrom(nsIDocShell* docshell);
|
static TabId GetTabIdFrom(nsIDocShell* docshell);
|
||||||
|
|
||||||
|
static bool AreRecordReplayTabsActive() {
|
||||||
|
return gNumActiveRecordReplayTabs != 0;
|
||||||
|
}
|
||||||
|
|
||||||
const TabId GetTabId() const { return mTabId; }
|
const TabId GetTabId() const { return mTabId; }
|
||||||
|
|
||||||
ContentParent* Manager() const { return mManager; }
|
ContentParent* Manager() const { return mManager; }
|
||||||
|
|
@ -989,6 +993,15 @@ class BrowserParent final : public PBrowserParent,
|
||||||
// BrowserParent with a new one connected to a different process, and we
|
// BrowserParent with a new one connected to a different process, and we
|
||||||
// should ignore nsIWebProgressListener stop requests.
|
// should ignore nsIWebProgressListener stop requests.
|
||||||
bool mIsDestroyingForProcessSwitch : 1;
|
bool mIsDestroyingForProcessSwitch : 1;
|
||||||
|
|
||||||
|
// How many record/replay tabs have active docshells in this process.
|
||||||
|
static size_t gNumActiveRecordReplayTabs;
|
||||||
|
|
||||||
|
// Whether this tab is contributing to gNumActiveRecordReplayTabs.
|
||||||
|
bool mIsActiveRecordReplayTab : 1;
|
||||||
|
|
||||||
|
// Update whether this is an active record/replay tab.
|
||||||
|
void SetIsActiveRecordReplayTab(bool aIsActive);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MOZ_STACK_CLASS BrowserParent::AutoUseNewTab final {
|
struct MOZ_STACK_CLASS BrowserParent::AutoUseNewTab final {
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@
|
||||||
#include "mozilla/PerformanceUtils.h"
|
#include "mozilla/PerformanceUtils.h"
|
||||||
#include "mozilla/plugins/PluginInstanceParent.h"
|
#include "mozilla/plugins/PluginInstanceParent.h"
|
||||||
#include "mozilla/plugins/PluginModuleParent.h"
|
#include "mozilla/plugins/PluginModuleParent.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
#include "mozilla/widget/ScreenManager.h"
|
#include "mozilla/widget/ScreenManager.h"
|
||||||
#include "mozilla/widget/WidgetMessageUtils.h"
|
#include "mozilla/widget/WidgetMessageUtils.h"
|
||||||
#include "nsBaseDragService.h"
|
#include "nsBaseDragService.h"
|
||||||
|
|
@ -687,6 +688,17 @@ bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Middleman processes use a special channel for forwarding messages to
|
||||||
|
// their own children.
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
SetMiddlemanIPCChannel(recordreplay::parent::ChannelToUIProcess());
|
||||||
|
|
||||||
|
// Eagerly mark this child as connected, as using another IPC channel will
|
||||||
|
// cause that channel's protocol to be marked as connected instead and
|
||||||
|
// prevent this one from being able to send IPDL messages.
|
||||||
|
ActorConnected();
|
||||||
|
}
|
||||||
|
|
||||||
if (!Open(std::move(aChannel), aParentPid, aIOLoop)) {
|
if (!Open(std::move(aChannel), aParentPid, aIOLoop)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1367,8 +1379,12 @@ void ContentChild::InitXPCOM(
|
||||||
RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(),
|
RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(),
|
||||||
aXPCOMInit.haveBidiKeyboards());
|
aXPCOMInit.haveBidiKeyboards());
|
||||||
|
|
||||||
// Create the CPOW manager as soon as possible.
|
// Create the CPOW manager as soon as possible. Middleman processes don't use
|
||||||
SendPJavaScriptConstructor();
|
// CPOWs, because their recording child will also have a CPOW manager that
|
||||||
|
// communicates with the UI process.
|
||||||
|
if (!recordreplay::IsMiddleman()) {
|
||||||
|
SendPJavaScriptConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
if (aXPCOMInit.domainPolicy().active()) {
|
if (aXPCOMInit.domainPolicy().active()) {
|
||||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||||
|
|
@ -1686,7 +1702,8 @@ static bool StartMacOSContentSandbox() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the sandbox is already enabled, there's nothing more to do here.
|
// If the sandbox is already enabled, there's nothing more to do here.
|
||||||
if (Preferences::GetBool("security.sandbox.content.mac.earlyinit")) {
|
if (Preferences::GetBool("security.sandbox.content.mac.earlyinit") &&
|
||||||
|
!recordreplay::IsRecordingOrReplaying()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1857,7 +1874,11 @@ static void FirstIdle(void) {
|
||||||
MOZ_ASSERT(gFirstIdleTask);
|
MOZ_ASSERT(gFirstIdleTask);
|
||||||
gFirstIdleTask = nullptr;
|
gFirstIdleTask = nullptr;
|
||||||
|
|
||||||
ContentChild::GetSingleton()->SendFirstIdle();
|
// When recording or replaying, the middleman process will send this message
|
||||||
|
// instead.
|
||||||
|
if (!recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
ContentChild::GetSingleton()->SendFirstIdle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mozilla::jsipc::PJavaScriptChild* ContentChild::AllocPJavaScriptChild() {
|
mozilla::jsipc::PJavaScriptChild* ContentChild::AllocPJavaScriptChild() {
|
||||||
|
|
@ -2078,6 +2099,9 @@ jsipc::CPOWManager* ContentChild::GetCPOWManager() {
|
||||||
LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
|
LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
|
||||||
return CPOWManagerFor(c);
|
return CPOWManagerFor(c);
|
||||||
}
|
}
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return CPOWManagerFor(SendPJavaScriptConstructor());
|
return CPOWManagerFor(SendPJavaScriptConstructor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3618,6 +3642,12 @@ mozilla::ipc::IPCResult ContentChild::RecvAddDynamicScalars(
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult ContentChild::RecvSaveRecording(
|
||||||
|
const FileDescriptor& aFile) {
|
||||||
|
recordreplay::parent::SaveRecording(aFile);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect(
|
mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect(
|
||||||
RedirectToRealChannelArgs&& aArgs,
|
RedirectToRealChannelArgs&& aArgs,
|
||||||
CrossProcessRedirectResolver&& aResolve) {
|
CrossProcessRedirectResolver&& aResolve) {
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@
|
||||||
#include "mozilla/net/PCookieServiceParent.h"
|
#include "mozilla/net/PCookieServiceParent.h"
|
||||||
#include "mozilla/plugins/PluginBridge.h"
|
#include "mozilla/plugins/PluginBridge.h"
|
||||||
#include "mozilla/psm/PSMContentListener.h"
|
#include "mozilla/psm/PSMContentListener.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
#include "mozilla/widget/ScreenManager.h"
|
#include "mozilla/widget/ScreenManager.h"
|
||||||
#include "nsAnonymousTemporaryFile.h"
|
#include "nsAnonymousTemporaryFile.h"
|
||||||
#include "nsAppRunner.h"
|
#include "nsAppRunner.h"
|
||||||
|
|
@ -643,7 +644,9 @@ bool ContentParent::sEarlySandboxInit = false;
|
||||||
/*static*/ RefPtr<ContentParent::LaunchPromise>
|
/*static*/ RefPtr<ContentParent::LaunchPromise>
|
||||||
ContentParent::PreallocateProcess() {
|
ContentParent::PreallocateProcess() {
|
||||||
RefPtr<ContentParent> process = new ContentParent(
|
RefPtr<ContentParent> process = new ContentParent(
|
||||||
/* aOpener = */ nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
|
/* aOpener = */ nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
|
||||||
|
eNotRecordingOrReplaying,
|
||||||
|
/* aRecordingFile = */ EmptyString());
|
||||||
|
|
||||||
return process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC);
|
return process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC);
|
||||||
}
|
}
|
||||||
|
|
@ -824,6 +827,44 @@ already_AddRefed<ContentParent> ContentParent::MinTabSelect(
|
||||||
return candidate.forget();
|
return candidate.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool CreateTemporaryRecordingFile(nsAString& aResult) {
|
||||||
|
static int sNumTemporaryRecordings;
|
||||||
|
nsCOMPtr<nsIFile> file;
|
||||||
|
return !NS_FAILED(
|
||||||
|
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file))) &&
|
||||||
|
!NS_FAILED(file->AppendNative(
|
||||||
|
nsPrintfCString("TempRecording.%d.%d", base::GetCurrentProcId(),
|
||||||
|
++sNumTemporaryRecordings))) &&
|
||||||
|
!NS_FAILED(file->GetPath(aResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
Maybe<ContentParent::RecordReplayState> ContentParent::GetRecordReplayState(
|
||||||
|
Element* aFrameElement, nsAString& aRecordingFile) {
|
||||||
|
if (!aFrameElement) {
|
||||||
|
return Some(eNotRecordingOrReplaying);
|
||||||
|
}
|
||||||
|
aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ReplayExecution,
|
||||||
|
aRecordingFile);
|
||||||
|
if (!aRecordingFile.IsEmpty()) {
|
||||||
|
return Some(eReplaying);
|
||||||
|
}
|
||||||
|
aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::RecordExecution,
|
||||||
|
aRecordingFile);
|
||||||
|
if (aRecordingFile.IsEmpty() &&
|
||||||
|
recordreplay::parent::SaveAllRecordingsDirectory()) {
|
||||||
|
aRecordingFile.AssignLiteral("*");
|
||||||
|
}
|
||||||
|
if (!aRecordingFile.IsEmpty()) {
|
||||||
|
if (aRecordingFile.EqualsLiteral("*") &&
|
||||||
|
!CreateTemporaryRecordingFile(aRecordingFile)) {
|
||||||
|
return Nothing();
|
||||||
|
}
|
||||||
|
return Some(eRecording);
|
||||||
|
}
|
||||||
|
return Some(eNotRecordingOrReplaying);
|
||||||
|
}
|
||||||
|
|
||||||
/*static*/
|
/*static*/
|
||||||
already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
||||||
ContentParent* aOpener, const nsAString& aRemoteType,
|
ContentParent* aOpener, const nsAString& aRemoteType,
|
||||||
|
|
@ -1494,6 +1535,21 @@ void ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
|
||||||
// other methods. We first call Shutdown() in the child. After the child is
|
// other methods. We first call Shutdown() in the child. After the child is
|
||||||
// ready, it calls FinishShutdown() on us. Then we close the channel.
|
// ready, it calls FinishShutdown() on us. Then we close the channel.
|
||||||
if (aMethod == SEND_SHUTDOWN_MESSAGE) {
|
if (aMethod == SEND_SHUTDOWN_MESSAGE) {
|
||||||
|
if (const char* directory =
|
||||||
|
recordreplay::parent::SaveAllRecordingsDirectory()) {
|
||||||
|
// Save a recording for the child process before it shuts down.
|
||||||
|
static int sNumSavedRecordings;
|
||||||
|
nsCOMPtr<nsIFile> file;
|
||||||
|
if (!NS_FAILED(NS_NewNativeLocalFile(nsDependentCString(directory), false,
|
||||||
|
getter_AddRefs(file))) &&
|
||||||
|
!NS_FAILED(file->AppendNative(
|
||||||
|
nsPrintfCString("Recording.%d.%d", base::GetCurrentProcId(),
|
||||||
|
++sNumSavedRecordings)))) {
|
||||||
|
bool unused;
|
||||||
|
SaveRecording(file, &unused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mIPCOpen && !mShutdownPending) {
|
if (mIPCOpen && !mShutdownPending) {
|
||||||
// Stop sending input events with input priority when shutting down.
|
// Stop sending input events with input priority when shutting down.
|
||||||
SetInputPriorityEventEnabled(false);
|
SetInputPriorityEventEnabled(false);
|
||||||
|
|
@ -1754,6 +1810,14 @@ void ContentParent::ActorDestroy(ActorDestroyReason why) {
|
||||||
[subprocess = mSubprocess] { subprocess->Destroy(); }));
|
[subprocess = mSubprocess] { subprocess->Destroy(); }));
|
||||||
mSubprocess = nullptr;
|
mSubprocess = nullptr;
|
||||||
|
|
||||||
|
// Delete any remaining replaying children.
|
||||||
|
for (auto& replayingProcess : mReplayingChildren) {
|
||||||
|
if (replayingProcess) {
|
||||||
|
replayingProcess->Destroy();
|
||||||
|
replayingProcess = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||||
cpm->RemoveContentProcess(this->ChildID());
|
cpm->RemoveContentProcess(this->ChildID());
|
||||||
|
|
||||||
|
|
@ -1823,6 +1887,12 @@ bool ContentParent::ShouldKeepProcessAlive() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recording/replaying content parents cannot be reused and should not be
|
||||||
|
// kept alive.
|
||||||
|
if (this->IsRecordingOrReplaying()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto contentParents = sBrowserContentParents->Get(mRemoteType);
|
auto contentParents = sBrowserContentParents->Get(mRemoteType);
|
||||||
if (!contentParents) {
|
if (!contentParents) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1920,6 +1990,84 @@ void ContentParent::NotifyTabDestroyed(const TabId& aTabId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult ContentParent::RecvOpenRecordReplayChannel(
|
||||||
|
const uint32_t& aChannelId, FileDescriptor* aConnection) {
|
||||||
|
// We should only get this message from the child if it is recording or
|
||||||
|
// replaying.
|
||||||
|
if (!this->IsRecordingOrReplaying()) {
|
||||||
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
recordreplay::parent::OpenChannel(Pid(), aChannelId, aConnection);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult ContentParent::RecvCreateReplayingProcess(
|
||||||
|
const uint32_t& aChannelId) {
|
||||||
|
// We should only get this message from the child if it is recording or
|
||||||
|
// replaying.
|
||||||
|
if (!this->IsRecordingOrReplaying()) {
|
||||||
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordreplay::parent::UseCloudForReplayingProcesses()) {
|
||||||
|
recordreplay::parent::CreateReplayingCloudProcess(Pid(), aChannelId);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (aChannelId >= mReplayingChildren.length()) {
|
||||||
|
if (!mReplayingChildren.append(nullptr)) {
|
||||||
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mReplayingChildren[aChannelId]) {
|
||||||
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> extraArgs;
|
||||||
|
recordreplay::parent::GetArgumentsForChildProcess(
|
||||||
|
Pid(), aChannelId, NS_ConvertUTF16toUTF8(mRecordingFile).get(),
|
||||||
|
/* aRecording = */ false, extraArgs);
|
||||||
|
|
||||||
|
GeckoChildProcessHost* child =
|
||||||
|
new GeckoChildProcessHost(GeckoProcessType_Content);
|
||||||
|
mReplayingChildren[aChannelId] = child;
|
||||||
|
if (!child->LaunchAndWaitForProcessHandle(extraArgs)) {
|
||||||
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replaying processes can fork themselves, and we can get crashes for
|
||||||
|
// them that correspond with one of those forked processes. When the crash
|
||||||
|
// reporter tries to read exception time annotations for one of these crashes,
|
||||||
|
// it hangs because the original replaying process hasn't actually crashed.
|
||||||
|
// Workaround this by removing the file descriptor for exception time
|
||||||
|
// annotations in replaying processes, so that the crash reporter will not
|
||||||
|
// attempt to read them.
|
||||||
|
ProcessId pid = base::GetProcId(child->GetChildProcessHandle());
|
||||||
|
CrashReporter::DeregisterChildCrashAnnotationFileDescriptor(pid);
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult ContentParent::RecvGenerateReplayCrashReport(
|
||||||
|
const uint32_t& aChannelId) {
|
||||||
|
if (aChannelId >= mReplayingChildren.length()) {
|
||||||
|
return IPC_FAIL(this, "invalid channel ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
GeckoChildProcessHost* child = mReplayingChildren[aChannelId];
|
||||||
|
if (!child) {
|
||||||
|
return IPC_FAIL(this, "invalid channel ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCrashReporter) {
|
||||||
|
ProcessId pid = base::GetProcId(child->GetChildProcessHandle());
|
||||||
|
mCrashReporter->GenerateCrashReport(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
jsipc::CPOWManager* ContentParent::GetCPOWManager() {
|
jsipc::CPOWManager* ContentParent::GetCPOWManager() {
|
||||||
if (PJavaScriptParent* p =
|
if (PJavaScriptParent* p =
|
||||||
LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
|
LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
|
||||||
|
|
@ -2107,8 +2255,10 @@ bool ContentParent::BeginSubprocessLaunch(bool aIsSync,
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
||||||
|
// If we're launching a middleman process for a
|
||||||
|
// recording or replay, start the sandbox later.
|
||||||
bool sandboxEnabled = IsContentSandboxEnabled();
|
bool sandboxEnabled = IsContentSandboxEnabled();
|
||||||
if (sandboxEnabled && sEarlySandboxInit) {
|
if (sandboxEnabled && sEarlySandboxInit && !IsRecordingOrReplaying()) {
|
||||||
AppendSandboxParams(extraArgs);
|
AppendSandboxParams(extraArgs);
|
||||||
}
|
}
|
||||||
if (sandboxEnabled) {
|
if (sandboxEnabled) {
|
||||||
|
|
@ -2241,7 +2391,10 @@ RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentParent::ContentParent(ContentParent* aOpener,
|
ContentParent::ContentParent(ContentParent* aOpener,
|
||||||
const nsAString& aRemoteType, int32_t aJSPluginID)
|
const nsAString& aRemoteType,
|
||||||
|
RecordReplayState aRecordReplayState,
|
||||||
|
const nsAString& aRecordingFile,
|
||||||
|
int32_t aJSPluginID)
|
||||||
: mSelfRef(nullptr),
|
: mSelfRef(nullptr),
|
||||||
mSubprocess(nullptr),
|
mSubprocess(nullptr),
|
||||||
mLaunchTS(TimeStamp::Now()),
|
mLaunchTS(TimeStamp::Now()),
|
||||||
|
|
@ -2256,6 +2409,8 @@ ContentParent::ContentParent(ContentParent* aOpener,
|
||||||
mNumDestroyingTabs(0),
|
mNumDestroyingTabs(0),
|
||||||
mLifecycleState(LifecycleState::LAUNCHING),
|
mLifecycleState(LifecycleState::LAUNCHING),
|
||||||
mIsForBrowser(!mRemoteType.IsEmpty()),
|
mIsForBrowser(!mRemoteType.IsEmpty()),
|
||||||
|
mRecordReplayState(aRecordReplayState),
|
||||||
|
mRecordingFile(aRecordingFile),
|
||||||
mCalledClose(false),
|
mCalledClose(false),
|
||||||
mCalledKillHard(false),
|
mCalledKillHard(false),
|
||||||
mCreatedPairedMinidumps(false),
|
mCreatedPairedMinidumps(false),
|
||||||
|
|
@ -5761,6 +5916,30 @@ void ContentParent::DeallocPSHistoryParent(PSHistoryParent* aActor) {
|
||||||
delete static_cast<SHistoryParent*>(aActor);
|
delete static_cast<SHistoryParent*>(aActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult ContentParent::SaveRecording(nsIFile* aFile, bool* aRetval) {
|
||||||
|
if (mRecordReplayState != eRecording) {
|
||||||
|
*aRetval = false;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRFileDesc* prfd;
|
||||||
|
nsresult rv = aFile->OpenNSPRFileDesc(
|
||||||
|
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE, 0644, &prfd);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptor::PlatformHandleType handle =
|
||||||
|
FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd));
|
||||||
|
|
||||||
|
Unused << SendSaveRecording(FileDescriptor(handle));
|
||||||
|
|
||||||
|
PR_Close(prfd);
|
||||||
|
|
||||||
|
*aRetval = true;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult ContentParent::RecvMaybeReloadPlugins() {
|
mozilla::ipc::IPCResult ContentParent::RecvMaybeReloadPlugins() {
|
||||||
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
|
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
|
||||||
pluginHost->ReloadPlugins();
|
pluginHost->ReloadPlugins();
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,13 @@ class ContentParent final
|
||||||
// Let managees query if it is safe to send messages.
|
// Let managees query if it is safe to send messages.
|
||||||
bool IsDestroyed() const { return !mIPCOpen; }
|
bool IsDestroyed() const { return !mIPCOpen; }
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvOpenRecordReplayChannel(
|
||||||
|
const uint32_t& channelId, FileDescriptor* connection);
|
||||||
|
mozilla::ipc::IPCResult RecvCreateReplayingProcess(
|
||||||
|
const uint32_t& aChannelId);
|
||||||
|
mozilla::ipc::IPCResult RecvGenerateReplayCrashReport(
|
||||||
|
const uint32_t& aChannelId);
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvCreateGMPService();
|
mozilla::ipc::IPCResult RecvCreateGMPService();
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvLoadPlugin(
|
mozilla::ipc::IPCResult RecvLoadPlugin(
|
||||||
|
|
@ -738,13 +745,20 @@ class ContentParent final
|
||||||
nsIReferrerInfo* aReferrerInfo, bool aLoadUri,
|
nsIReferrerInfo* aReferrerInfo, bool aLoadUri,
|
||||||
nsIContentSecurityPolicy* aCsp);
|
nsIContentSecurityPolicy* aCsp);
|
||||||
|
|
||||||
|
enum RecordReplayState { eNotRecordingOrReplaying, eRecording, eReplaying };
|
||||||
|
|
||||||
explicit ContentParent(int32_t aPluginID)
|
explicit ContentParent(int32_t aPluginID)
|
||||||
: ContentParent(nullptr, EmptyString(), aPluginID) {}
|
: ContentParent(nullptr, EmptyString(), eNotRecordingOrReplaying,
|
||||||
ContentParent(ContentParent* aOpener, const nsAString& aRemoteType)
|
EmptyString(), aPluginID) {}
|
||||||
: ContentParent(aOpener, aRemoteType, nsFakePluginTag::NOT_JSPLUGIN) {}
|
ContentParent(ContentParent* aOpener, const nsAString& aRemoteType,
|
||||||
|
RecordReplayState aRecordReplayState = eNotRecordingOrReplaying,
|
||||||
|
const nsAString& aRecordingFile = EmptyString())
|
||||||
|
: ContentParent(aOpener, aRemoteType, aRecordReplayState, aRecordingFile,
|
||||||
|
nsFakePluginTag::NOT_JSPLUGIN) {}
|
||||||
|
|
||||||
ContentParent(ContentParent* aOpener, const nsAString& aRemoteType,
|
ContentParent(ContentParent* aOpener, const nsAString& aRemoteType,
|
||||||
int32_t aPluginID);
|
RecordReplayState aRecordReplayState,
|
||||||
|
const nsAString& aRecordingFile, int32_t aPluginID);
|
||||||
|
|
||||||
// Launch the subprocess and associated initialization.
|
// Launch the subprocess and associated initialization.
|
||||||
// Returns false if the process fails to start.
|
// Returns false if the process fails to start.
|
||||||
|
|
@ -1287,6 +1301,12 @@ class ContentParent final
|
||||||
const bool& aMinimizeMemoryUsage,
|
const bool& aMinimizeMemoryUsage,
|
||||||
const Maybe<FileDescriptor>& aDMDFile) override;
|
const Maybe<FileDescriptor>& aDMDFile) override;
|
||||||
|
|
||||||
|
nsresult SaveRecording(nsIFile* aFile, bool* aRetval);
|
||||||
|
|
||||||
|
bool IsRecordingOrReplaying() const {
|
||||||
|
return mRecordReplayState != eNotRecordingOrReplaying;
|
||||||
|
}
|
||||||
|
|
||||||
void OnBrowsingContextGroupSubscribe(BrowsingContextGroup* aGroup);
|
void OnBrowsingContextGroupSubscribe(BrowsingContextGroup* aGroup);
|
||||||
void OnBrowsingContextGroupUnsubscribe(BrowsingContextGroup* aGroup);
|
void OnBrowsingContextGroupUnsubscribe(BrowsingContextGroup* aGroup);
|
||||||
|
|
||||||
|
|
@ -1300,6 +1320,12 @@ class ContentParent final
|
||||||
static bool ShouldSyncPreference(const char16_t* aData);
|
static bool ShouldSyncPreference(const char16_t* aData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Determine the recording/replaying state for this frame.
|
||||||
|
// Return `Nothing` in case of error, typically if we need
|
||||||
|
// to create a temporary recording file but could not.
|
||||||
|
static Maybe<RecordReplayState> GetRecordReplayState(
|
||||||
|
Element* aFrameElement, nsAString& aRecordingFile);
|
||||||
|
|
||||||
// Return an existing ContentParent if possible. Otherwise, `nullptr`.
|
// Return an existing ContentParent if possible. Otherwise, `nullptr`.
|
||||||
static already_AddRefed<ContentParent> GetUsedBrowserProcess(
|
static already_AddRefed<ContentParent> GetUsedBrowserProcess(
|
||||||
ContentParent* aOpener, const nsAString& aRemoteType,
|
ContentParent* aOpener, const nsAString& aRemoteType,
|
||||||
|
|
@ -1376,6 +1402,16 @@ class ContentParent final
|
||||||
|
|
||||||
bool mIsForBrowser;
|
bool mIsForBrowser;
|
||||||
|
|
||||||
|
// Whether this process is recording or replaying its execution, and any
|
||||||
|
// associated recording file.
|
||||||
|
RecordReplayState mRecordReplayState;
|
||||||
|
nsString mRecordingFile;
|
||||||
|
|
||||||
|
// When recording or replaying, the child process is a middleman. This vector
|
||||||
|
// stores any replaying children we have spawned on behalf of that middleman,
|
||||||
|
// indexed by their record/replay channel ID.
|
||||||
|
Vector<mozilla::ipc::GeckoChildProcessHost*> mReplayingChildren;
|
||||||
|
|
||||||
// These variables track whether we've called Close() and KillHard() on our
|
// These variables track whether we've called Close() and KillHard() on our
|
||||||
// channel.
|
// channel.
|
||||||
bool mCalledClose;
|
bool mCalledClose;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "ContentProcess.h"
|
#include "ContentProcess.h"
|
||||||
#include "base/shared_memory.h"
|
#include "base/shared_memory.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
|
|
||||||
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
||||||
# include <stdlib.h>
|
# include <stdlib.h>
|
||||||
|
|
@ -175,6 +176,12 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
recordreplay::parent::InitializeMiddleman(aArgc, aArgv, ParentPid(),
|
||||||
|
deserializer.GetPrefsHandle(),
|
||||||
|
deserializer.GetPrefMapHandle());
|
||||||
|
}
|
||||||
|
|
||||||
mContent.Init(IOThreadChild::message_loop(), ParentPid(), *parentBuildID,
|
mContent.Init(IOThreadChild::message_loop(), ParentPid(), *parentBuildID,
|
||||||
IOThreadChild::TakeChannel(), *childID, *isForBrowser);
|
IOThreadChild::TakeChannel(), *childID, *isForBrowser);
|
||||||
|
|
||||||
|
|
@ -182,8 +189,11 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
||||||
#if (defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
|
#if (defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
|
||||||
mContent.SetProfileDir(profileDir);
|
mContent.SetProfileDir(profileDir);
|
||||||
# if defined(DEBUG)
|
# if defined(DEBUG)
|
||||||
|
// For WebReplay middleman processes, the sandbox is
|
||||||
|
// started after receiving the SetProcessSandbox message.
|
||||||
if (IsContentSandboxEnabled() &&
|
if (IsContentSandboxEnabled() &&
|
||||||
Preferences::GetBool("security.sandbox.content.mac.earlyinit")) {
|
Preferences::GetBool("security.sandbox.content.mac.earlyinit") &&
|
||||||
|
!recordreplay::IsMiddleman()) {
|
||||||
AssertMacSandboxEnabled();
|
AssertMacSandboxEnabled();
|
||||||
}
|
}
|
||||||
# endif /* DEBUG */
|
# endif /* DEBUG */
|
||||||
|
|
|
||||||
|
|
@ -831,6 +831,9 @@ child:
|
||||||
*/
|
*/
|
||||||
async PClientOpenWindowOp(ClientOpenWindowArgs aArgs);
|
async PClientOpenWindowOp(ClientOpenWindowArgs aArgs);
|
||||||
|
|
||||||
|
/* Save the execution up to the current point in a recording process. */
|
||||||
|
async SaveRecording(FileDescriptor file);
|
||||||
|
|
||||||
// This message is sent to content processes, and triggers the creation of a
|
// This message is sent to content processes, and triggers the creation of a
|
||||||
// new HttpChannelChild that will be connected to the parent channel
|
// new HttpChannelChild that will be connected to the parent channel
|
||||||
// represented by registrarId.
|
// represented by registrarId.
|
||||||
|
|
@ -877,6 +880,11 @@ child:
|
||||||
parent:
|
parent:
|
||||||
async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
|
async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
|
||||||
|
|
||||||
|
sync OpenRecordReplayChannel(uint32_t channelId)
|
||||||
|
returns (FileDescriptor connection);
|
||||||
|
async CreateReplayingProcess(uint32_t channelId);
|
||||||
|
async GenerateReplayCrashReport(uint32_t channelId);
|
||||||
|
|
||||||
async CreateGMPService();
|
async CreateGMPService();
|
||||||
|
|
||||||
async InitStreamFilter(uint64_t channelId, nsString addonId)
|
async InitStreamFilter(uint64_t channelId, nsString addonId)
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,11 @@ class HangMonitorChild : public PProcessHangMonitorChild,
|
||||||
private:
|
private:
|
||||||
void ShutdownOnThread();
|
void ShutdownOnThread();
|
||||||
|
|
||||||
static Atomic<HangMonitorChild*, SequentiallyConsistent> sInstance;
|
// Ordering of this atomic is not preserved while recording/replaying, as it
|
||||||
|
// may be accessed during the JS interrupt callback.
|
||||||
|
static Atomic<HangMonitorChild*, SequentiallyConsistent,
|
||||||
|
recordreplay::Behavior::DontPreserve>
|
||||||
|
sInstance;
|
||||||
|
|
||||||
const RefPtr<ProcessHangMonitor> mHangMonitor;
|
const RefPtr<ProcessHangMonitor> mHangMonitor;
|
||||||
Monitor mMonitor;
|
Monitor mMonitor;
|
||||||
|
|
@ -173,7 +177,9 @@ class HangMonitorChild : public PProcessHangMonitorChild,
|
||||||
Atomic<bool> mPaintWhileInterruptingJSActive;
|
Atomic<bool> mPaintWhileInterruptingJSActive;
|
||||||
};
|
};
|
||||||
|
|
||||||
Atomic<HangMonitorChild*, SequentiallyConsistent> HangMonitorChild::sInstance;
|
Atomic<HangMonitorChild*, SequentiallyConsistent,
|
||||||
|
recordreplay::Behavior::DontPreserve>
|
||||||
|
HangMonitorChild::sInstance;
|
||||||
|
|
||||||
/* Parent process objects */
|
/* Parent process objects */
|
||||||
|
|
||||||
|
|
@ -304,7 +310,9 @@ class HangMonitorParent : public PProcessHangMonitorParent,
|
||||||
|
|
||||||
HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
|
HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
|
||||||
: mHangMonitor(aMonitor),
|
: mHangMonitor(aMonitor),
|
||||||
mMonitor("HangMonitorChild lock"),
|
// Ordering of this atomic is not preserved while recording/replaying, as
|
||||||
|
// it may be accessed during the JS interrupt callback.
|
||||||
|
mMonitor("HangMonitorChild lock", recordreplay::Behavior::DontPreserve),
|
||||||
mSentReport(false),
|
mSentReport(false),
|
||||||
mTerminateScript(false),
|
mTerminateScript(false),
|
||||||
mTerminateGlobal(false),
|
mTerminateGlobal(false),
|
||||||
|
|
@ -333,6 +341,13 @@ HangMonitorChild::~HangMonitorChild() {
|
||||||
bool HangMonitorChild::InterruptCallback() {
|
bool HangMonitorChild::InterruptCallback() {
|
||||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
// The interrupt callback is triggered at non-deterministic points when
|
||||||
|
// recording/replaying, so don't perform any operations that can interact
|
||||||
|
// with the recording.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't start painting if we're not in a good place to run script. We run
|
// Don't start painting if we're not in a good place to run script. We run
|
||||||
// chrome script during layout and such, and it wouldn't be good to interrupt
|
// chrome script during layout and such, and it wouldn't be good to interrupt
|
||||||
// painting code from there.
|
// painting code from there.
|
||||||
|
|
|
||||||
|
|
@ -433,6 +433,10 @@ cubeb* GetCubebContextUnlocked() {
|
||||||
PREF_CUBEB_FORCE_NULL_CONTEXT));
|
PREF_CUBEB_FORCE_NULL_CONTEXT));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
// Media is not supported when recording or replaying. See bug 1304146.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
if (sCubebState != CubebState::Uninitialized) {
|
if (sCubebState != CubebState::Uninitialized) {
|
||||||
// If we have already passed the initialization point (below), just return
|
// If we have already passed the initialization point (below), just return
|
||||||
// the current context, which may be null (e.g., after error or shutdown.)
|
// the current context, which may be null (e.g., after error or shutdown.)
|
||||||
|
|
@ -605,7 +609,7 @@ void InitLibrary() {
|
||||||
NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName));
|
NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName));
|
||||||
#endif
|
#endif
|
||||||
#ifdef MOZ_CUBEB_REMOTING
|
#ifdef MOZ_CUBEB_REMOTING
|
||||||
if (sCubebSandbox && XRE_IsContentProcess()) {
|
if (sCubebSandbox && XRE_IsContentProcess() && !recordreplay::IsMiddleman()) {
|
||||||
InitAudioIPCConnection();
|
InitAudioIPCConnection();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,16 @@ MediaResult MediaFormatReader::DecoderFactory::DoCreateDecoder(Data& aData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Media playback is not supported when recording or replaying. See bug
|
||||||
|
// 1304146.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return MediaResult(
|
||||||
|
NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||||
|
nsPrintfCString("error creating %s decoder: "
|
||||||
|
"media playback is disabled while recording/replaying",
|
||||||
|
TrackTypeToStr(aData.mTrack)));
|
||||||
|
}
|
||||||
|
|
||||||
// result may not be updated by PDMFactory::CreateDecoder, as such it must be
|
// result may not be updated by PDMFactory::CreateDecoder, as such it must be
|
||||||
// initialized to a fatal error by default.
|
// initialized to a fatal error by default.
|
||||||
MediaResult result =
|
MediaResult result =
|
||||||
|
|
|
||||||
|
|
@ -295,8 +295,8 @@ class SourceListener : public SupportsWeakPtr<SourceListener> {
|
||||||
SourceListenerPromise;
|
SourceListenerPromise;
|
||||||
|
|
||||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SourceListener)
|
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SourceListener)
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION_AND_RECORDING(
|
||||||
SourceListener)
|
SourceListener, recordreplay::Behavior::Preserve)
|
||||||
|
|
||||||
SourceListener();
|
SourceListener();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,13 @@ JSObject* AudioContext::WrapObject(JSContext* aCx,
|
||||||
already_AddRefed<AudioContext> AudioContext::Constructor(
|
already_AddRefed<AudioContext> AudioContext::Constructor(
|
||||||
const GlobalObject& aGlobal, const AudioContextOptions& aOptions,
|
const GlobalObject& aGlobal, const AudioContextOptions& aOptions,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
|
// Audio playback is not yet supported when recording or replaying. See bug
|
||||||
|
// 1304147.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||||
do_QueryInterface(aGlobal.GetAsSupports());
|
do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
if (!window) {
|
if (!window) {
|
||||||
|
|
@ -295,6 +302,13 @@ already_AddRefed<AudioContext> AudioContext::Constructor(
|
||||||
already_AddRefed<AudioContext> AudioContext::Constructor(
|
already_AddRefed<AudioContext> AudioContext::Constructor(
|
||||||
const GlobalObject& aGlobal, uint32_t aNumberOfChannels, uint32_t aLength,
|
const GlobalObject& aGlobal, uint32_t aNumberOfChannels, uint32_t aLength,
|
||||||
float aSampleRate, ErrorResult& aRv) {
|
float aSampleRate, ErrorResult& aRv) {
|
||||||
|
// Audio playback is not yet supported when recording or replaying. See bug
|
||||||
|
// 1304147.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||||
do_QueryInterface(aGlobal.GetAsSupports());
|
do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
if (!window) {
|
if (!window) {
|
||||||
|
|
|
||||||
|
|
@ -690,6 +690,12 @@ nsresult nsPluginHost::InstantiatePluginInstance(
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plugins are not supported when recording or replaying executions.
|
||||||
|
// See bug 1483232.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
|
RefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
|
||||||
if (!instanceOwner) {
|
if (!instanceOwner) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
|
||||||
|
|
@ -542,6 +542,13 @@ bool InterruptCallback(JSContext* aCx) {
|
||||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||||
MOZ_ASSERT(worker);
|
MOZ_ASSERT(worker);
|
||||||
|
|
||||||
|
// As with the main thread, the interrupt callback is triggered
|
||||||
|
// non-deterministically when recording/replaying, so return early to avoid
|
||||||
|
// performing any recorded events.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Now is a good time to turn on profiling if it's pending.
|
// Now is a good time to turn on profiling if it's pending.
|
||||||
PROFILER_JS_INTERRUPT_CALLBACK();
|
PROFILER_JS_INTERRUPT_CALLBACK();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "RecordingTypes.h"
|
#include "RecordingTypes.h"
|
||||||
|
|
|
||||||
|
|
@ -1341,6 +1341,13 @@ APZEventResult APZCTreeManager::ReceiveInputEvent(InputData& aEvent) {
|
||||||
APZThreadUtils::AssertOnControllerThread();
|
APZThreadUtils::AssertOnControllerThread();
|
||||||
APZEventResult result;
|
APZEventResult result;
|
||||||
|
|
||||||
|
// Ignore input events when there are active tabs that are recording or
|
||||||
|
// replaying. APZ does not work with the special layers constructed by
|
||||||
|
// the middleman processes being communicated with here.
|
||||||
|
if (dom::BrowserParent::AreRecordReplayTabsActive()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Use a RAII class for updating the focus sequence number of this event
|
// Use a RAII class for updating the focus sequence number of this event
|
||||||
AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
|
AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ CompositorBridgeChild* ClientLayerManager::GetRemoteRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositorBridgeChild* ClientLayerManager::GetCompositorBridgeChild() {
|
CompositorBridgeChild* ClientLayerManager::GetCompositorBridgeChild() {
|
||||||
if (!XRE_IsParentProcess()) {
|
if (!XRE_IsParentProcess() && !recordreplay::IsRecordingOrReplaying()) {
|
||||||
return CompositorBridgeChild::Get();
|
return CompositorBridgeChild::Get();
|
||||||
}
|
}
|
||||||
return GetRemoteRenderer();
|
return GetRemoteRenderer();
|
||||||
|
|
@ -495,6 +495,8 @@ void ClientLayerManager::DidComposite(TransactionId aTransactionId,
|
||||||
const TimeStamp& aCompositeStart,
|
const TimeStamp& aCompositeStart,
|
||||||
const TimeStamp& aCompositeEnd) {
|
const TimeStamp& aCompositeEnd) {
|
||||||
if (!mWidget) {
|
if (!mWidget) {
|
||||||
|
// When recording/replaying this manager may have already been destroyed.
|
||||||
|
MOZ_ASSERT(recordreplay::IsRecordingOrReplaying());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@ CompositorBridgeParent::CompositorBridgeParent(
|
||||||
|
|
||||||
void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
|
void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
|
||||||
const LayersId& aLayerTreeId) {
|
const LayersId& aLayerTreeId) {
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
mWidget = aWidget;
|
mWidget = aWidget;
|
||||||
|
|
@ -1021,7 +1021,11 @@ void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
|
||||||
bool requestNextFrame =
|
bool requestNextFrame =
|
||||||
mCompositionManager->TransformShadowTree(time, mVsyncRate);
|
mCompositionManager->TransformShadowTree(time, mVsyncRate);
|
||||||
|
|
||||||
if (requestNextFrame) {
|
// Don't eagerly schedule new compositions here when recording or replaying.
|
||||||
|
// Recording/replaying processes schedule composites at the top of the main
|
||||||
|
// thread's event loop rather than via PVsync, which can cause the composites
|
||||||
|
// scheduled here to pile up without any drawing actually happening.
|
||||||
|
if (requestNextFrame && !recordreplay::IsRecordingOrReplaying()) {
|
||||||
ScheduleComposition();
|
ScheduleComposition();
|
||||||
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
||||||
// If we have visible windowed plugins then we need to wait for content (and
|
// If we have visible windowed plugins then we need to wait for content (and
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ CompositorManagerChild::CreateWidgetCompositorBridge(
|
||||||
already_AddRefed<CompositorBridgeChild>
|
already_AddRefed<CompositorBridgeChild>
|
||||||
CompositorManagerChild::CreateSameProcessWidgetCompositorBridge(
|
CompositorManagerChild::CreateSameProcessWidgetCompositorBridge(
|
||||||
LayerManager* aLayerManager, uint32_t aNamespace) {
|
LayerManager* aLayerManager, uint32_t aNamespace) {
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
|
if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ StaticAutoPtr<nsTArray<CompositorManagerParent*>>
|
||||||
/* static */
|
/* static */
|
||||||
already_AddRefed<CompositorManagerParent>
|
already_AddRefed<CompositorManagerParent>
|
||||||
CompositorManagerParent::CreateSameProcess() {
|
CompositorManagerParent::CreateSameProcess() {
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
StaticMutexAutoLock lock(sMutex);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
|
|
||||||
|
|
@ -77,7 +77,7 @@ already_AddRefed<CompositorBridgeParent>
|
||||||
CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
|
CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
|
||||||
CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
|
CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
|
||||||
bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
|
bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
// When we are in a combined UI / GPU process, InProcessCompositorSession
|
// When we are in a combined UI / GPU process, InProcessCompositorSession
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,8 @@ CompositorVsyncScheduler::CompositorVsyncScheduler(
|
||||||
// but is only accessed after on the compositor thread.
|
// but is only accessed after on the compositor thread.
|
||||||
mAsapScheduling =
|
mAsapScheduling =
|
||||||
StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
|
StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
|
||||||
gfxPlatform::IsInLayoutAsapMode();
|
gfxPlatform::IsInLayoutAsapMode() ||
|
||||||
|
recordreplay::IsRecordingOrReplaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositorVsyncScheduler::~CompositorVsyncScheduler() {
|
CompositorVsyncScheduler::~CompositorVsyncScheduler() {
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,12 @@ mozilla::ipc::IPCResult LayerTransactionParent::RecvPaintTime(
|
||||||
|
|
||||||
mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
|
mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
|
||||||
const TransactionInfo& aInfo) {
|
const TransactionInfo& aInfo) {
|
||||||
|
auto guard = MakeScopeExit([&] {
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
recordreplay::child::NotifyPaintComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
AUTO_PROFILER_TRACING_MARKER("Paint", "LayerTransaction", GRAPHICS);
|
AUTO_PROFILER_TRACING_MARKER("Paint", "LayerTransaction", GRAPHICS);
|
||||||
AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS);
|
AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS);
|
||||||
PerfStats::AutoMetricRecording<PerfStats::Metric::LayerTransactions>
|
PerfStats::AutoMetricRecording<PerfStats::Metric::LayerTransactions>
|
||||||
|
|
@ -492,6 +498,11 @@ mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
|
||||||
(TimeStamp::Now() - updateStart).ToMilliseconds());
|
(TimeStamp::Now() - updateStart).ToMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compose after every update when recording/replaying.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
mCompositorBridge->ForceComposeToTarget(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -712,6 +712,10 @@ bool ShadowLayerForwarder::EndTransaction(
|
||||||
mShadowManager->SendRecordPaintTimes(mPaintTiming);
|
mShadowManager->SendRecordPaintTimes(mPaintTiming);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
recordreplay::child::NotifyPaintStart();
|
||||||
|
}
|
||||||
|
|
||||||
*aSent = true;
|
*aSent = true;
|
||||||
mIsFirstPaint = false;
|
mIsFirstPaint = false;
|
||||||
mFocusTarget = FocusTarget();
|
mFocusTarget = FocusTarget();
|
||||||
|
|
|
||||||
|
|
@ -48,44 +48,49 @@ struct DeleteOnMainThreadTask : public Runnable {
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION( \
|
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION_AND_RECORDING( \
|
||||||
_class) \
|
_class, _recording) \
|
||||||
public: \
|
public: \
|
||||||
NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
|
NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
|
||||||
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
|
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
|
||||||
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
|
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
|
||||||
nsrefcnt count = ++mRefCnt; \
|
nsrefcnt count = ++mRefCnt; \
|
||||||
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
|
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
|
||||||
return (nsrefcnt)count; \
|
return (nsrefcnt)count; \
|
||||||
} \
|
} \
|
||||||
void DeleteToBeCalledOnMainThread() { \
|
void DeleteToBeCalledOnMainThread() { \
|
||||||
MOZ_ASSERT(NS_IsMainThread()); \
|
MOZ_ASSERT(NS_IsMainThread()); \
|
||||||
NS_LOG_RELEASE(this, 0, #_class); \
|
NS_LOG_RELEASE(this, 0, #_class); \
|
||||||
delete this; \
|
delete this; \
|
||||||
} \
|
} \
|
||||||
NS_METHOD_(MozExternalRefCountType) Release(void) { \
|
NS_METHOD_(MozExternalRefCountType) Release(void) { \
|
||||||
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
|
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
|
||||||
nsrefcnt count = --mRefCnt; \
|
nsrefcnt count = --mRefCnt; \
|
||||||
if (count == 0) { \
|
if (count == 0) { \
|
||||||
if (NS_IsMainThread()) { \
|
if (NS_IsMainThread()) { \
|
||||||
DeleteToBeCalledOnMainThread(); \
|
DeleteToBeCalledOnMainThread(); \
|
||||||
} else { \
|
} else { \
|
||||||
NS_DispatchToMainThread( \
|
NS_DispatchToMainThread( \
|
||||||
new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \
|
new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \
|
||||||
} \
|
} \
|
||||||
} else { \
|
} else { \
|
||||||
NS_LOG_RELEASE(this, count, #_class); \
|
NS_LOG_RELEASE(this, count, #_class); \
|
||||||
} \
|
} \
|
||||||
return count; \
|
return count; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
protected: \
|
protected: \
|
||||||
::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
|
::mozilla::ThreadSafeAutoRefCntWithRecording<_recording> mRefCnt; \
|
||||||
\
|
\
|
||||||
private: \
|
private: \
|
||||||
::mozilla::layers::HelperForMainThreadDestruction \
|
::mozilla::layers::HelperForMainThreadDestruction \
|
||||||
mHelperForMainThreadDestruction; \
|
mHelperForMainThreadDestruction; \
|
||||||
\
|
\
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION( \
|
||||||
|
_class) \
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION_AND_RECORDING( \
|
||||||
|
_class, recordreplay::Behavior::DontPreserve)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -891,7 +891,7 @@ void gfxPlatform::Init() {
|
||||||
|
|
||||||
gfxConfig::Init();
|
gfxConfig::Init();
|
||||||
|
|
||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying()) {
|
||||||
GPUProcessManager::Initialize();
|
GPUProcessManager::Initialize();
|
||||||
RDDProcessManager::Initialize();
|
RDDProcessManager::Initialize();
|
||||||
|
|
||||||
|
|
@ -1392,12 +1392,12 @@ void gfxPlatform::InitLayersIPC() {
|
||||||
sLayersIPCIsUp = true;
|
sLayersIPCIsUp = true;
|
||||||
|
|
||||||
if (XRE_IsContentProcess()) {
|
if (XRE_IsContentProcess()) {
|
||||||
if (gfxVars::UseOMTP()) {
|
if (gfxVars::UseOMTP() && !recordreplay::IsRecordingOrReplaying()) {
|
||||||
layers::PaintThread::Start();
|
layers::PaintThread::Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying()) {
|
||||||
if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS) && UseWebRender()) {
|
if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS) && UseWebRender()) {
|
||||||
wr::RenderThread::Start();
|
wr::RenderThread::Start();
|
||||||
image::ImageMemoryReporter::InitForWebRender();
|
image::ImageMemoryReporter::InitForWebRender();
|
||||||
|
|
@ -1422,7 +1422,7 @@ void gfxPlatform::ShutdownLayersIPC() {
|
||||||
layers::ImageBridgeChild::ShutDown();
|
layers::ImageBridgeChild::ShutDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfxVars::UseOMTP()) {
|
if (gfxVars::UseOMTP() && !recordreplay::IsRecordingOrReplaying()) {
|
||||||
layers::PaintThread::Shutdown();
|
layers::PaintThread::Shutdown();
|
||||||
}
|
}
|
||||||
} else if (XRE_IsParentProcess()) {
|
} else if (XRE_IsParentProcess()) {
|
||||||
|
|
@ -2707,6 +2707,11 @@ void gfxPlatform::InitCompositorAccelerationPrefs() {
|
||||||
FeatureStatus::Blocked, "Acceleration blocked by headless mode",
|
FeatureStatus::Blocked, "Acceleration blocked by headless mode",
|
||||||
NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_HEADLESSMODE"));
|
NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_HEADLESSMODE"));
|
||||||
}
|
}
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
feature.ForceDisable(
|
||||||
|
FeatureStatus::Blocked, "Acceleration blocked by recording/replaying",
|
||||||
|
NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_RECORDREPLAY"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/
|
/*static*/
|
||||||
|
|
@ -2854,6 +2859,12 @@ void gfxPlatform::InitWebRenderConfig() {
|
||||||
// crash reports.
|
// crash reports.
|
||||||
ScopedGfxFeatureReporter reporter("WR", prefEnabled || envvarEnabled);
|
ScopedGfxFeatureReporter reporter("WR", prefEnabled || envvarEnabled);
|
||||||
if (!XRE_IsParentProcess()) {
|
if (!XRE_IsParentProcess()) {
|
||||||
|
// Force-disable WebRender in recording/replaying child processes, which
|
||||||
|
// have their own compositor.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
gfxVars::SetUseWebRender(false);
|
||||||
|
}
|
||||||
|
|
||||||
// The parent process runs through all the real decision-making code
|
// The parent process runs through all the real decision-making code
|
||||||
// later in this function. For other processes we still want to report
|
// later in this function. For other processes we still want to report
|
||||||
// the state of the feature for crash reports.
|
// the state of the feature for crash reports.
|
||||||
|
|
@ -3250,7 +3261,8 @@ bool gfxPlatform::IsInLayoutAsapMode() {
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool gfxPlatform::ForceSoftwareVsync() {
|
bool gfxPlatform::ForceSoftwareVsync() {
|
||||||
return StaticPrefs::layout_frame_rate() > 0;
|
return StaticPrefs::layout_frame_rate() > 0 ||
|
||||||
|
recordreplay::IsRecordingOrReplaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
|
@ -3267,7 +3279,7 @@ int gfxPlatform::GetDefaultFrameRate() { return 60; }
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void gfxPlatform::ReInitFrameRate() {
|
void gfxPlatform::ReInitFrameRate() {
|
||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying()) {
|
||||||
RefPtr<VsyncSource> oldSource = gPlatform->mVsyncSource;
|
RefPtr<VsyncSource> oldSource = gPlatform->mVsyncSource;
|
||||||
|
|
||||||
// Start a new one:
|
// Start a new one:
|
||||||
|
|
|
||||||
|
|
@ -632,7 +632,8 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
|
||||||
*/
|
*/
|
||||||
virtual mozilla::gfx::VsyncSource* GetHardwareVsync() {
|
virtual mozilla::gfx::VsyncSource* GetHardwareVsync() {
|
||||||
MOZ_ASSERT(mVsyncSource != nullptr);
|
MOZ_ASSERT(mVsyncSource != nullptr);
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess() ||
|
||||||
|
mozilla::recordreplay::IsRecordingOrReplaying());
|
||||||
return mVsyncSource;
|
return mVsyncSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,12 @@ bool LockScreenOrientation(const hal::ScreenOrientation& aOrientation) {
|
||||||
return allowed;
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnlockScreenOrientation() { Hal()->SendUnlockScreenOrientation(); }
|
void UnlockScreenOrientation() {
|
||||||
|
// Don't send this message from both the middleman and recording processes.
|
||||||
|
if (!recordreplay::IsMiddleman()) {
|
||||||
|
Hal()->SendUnlockScreenOrientation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EnableSensorNotifications(SensorType aSensor) {
|
void EnableSensorNotifications(SensorType aSensor) {
|
||||||
Hal()->SendEnableSensorNotifications(aSensor);
|
Hal()->SendEnableSensorNotifications(aSensor);
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class IDecoderFrameRecycler {
|
||||||
|
|
||||||
class Decoder {
|
class Decoder {
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(Decoder)
|
||||||
|
|
||||||
explicit Decoder(RasterImage* aImage);
|
explicit Decoder(RasterImage* aImage);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class IDecodingTask : public IResumable {
|
||||||
*/
|
*/
|
||||||
class MetadataDecodingTask final : public IDecodingTask {
|
class MetadataDecodingTask final : public IDecodingTask {
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataDecodingTask, override)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(MetadataDecodingTask, override)
|
||||||
|
|
||||||
explicit MetadataDecodingTask(NotNull<Decoder*> aDecoder);
|
explicit MetadataDecodingTask(NotNull<Decoder*> aDecoder);
|
||||||
|
|
||||||
|
|
@ -97,7 +97,8 @@ class MetadataDecodingTask final : public IDecodingTask {
|
||||||
*/
|
*/
|
||||||
class AnonymousDecodingTask final : public IDecodingTask {
|
class AnonymousDecodingTask final : public IDecodingTask {
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecodingTask, override)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(AnonymousDecodingTask,
|
||||||
|
override)
|
||||||
|
|
||||||
explicit AnonymousDecodingTask(NotNull<Decoder*> aDecoder, bool aResumable);
|
explicit AnonymousDecodingTask(NotNull<Decoder*> aDecoder, bool aResumable);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@ class CostEntry {
|
||||||
|
|
||||||
bool operator<(const CostEntry& aOther) const {
|
bool operator<(const CostEntry& aOther) const {
|
||||||
return mCost < aOther.mCost ||
|
return mCost < aOther.mCost ||
|
||||||
(mCost == aOther.mCost && mSurface < aOther.mSurface);
|
(mCost == aOther.mCost &&
|
||||||
|
recordreplay::RecordReplayValue(mSurface < aOther.mSurface));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,13 @@ FileDescriptorSet::FileDescriptorSet() : consumed_descriptor_highwater_(0) {}
|
||||||
FileDescriptorSet::~FileDescriptorSet() {
|
FileDescriptorSet::~FileDescriptorSet() {
|
||||||
if (consumed_descriptor_highwater_ == descriptors_.size()) return;
|
if (consumed_descriptor_highwater_ == descriptors_.size()) return;
|
||||||
|
|
||||||
CHROMIUM_LOG(WARNING)
|
// Middleman processes copy FileDescriptorSets before forwarding them to
|
||||||
<< "FileDescriptorSet destroyed with unconsumed descriptors";
|
// recording children, and destroying sets without using their descriptors is
|
||||||
|
// expected.
|
||||||
|
if (!mozilla::recordreplay::IsMiddleman()) {
|
||||||
|
CHROMIUM_LOG(WARNING)
|
||||||
|
<< "FileDescriptorSet destroyed with unconsumed descriptors";
|
||||||
|
}
|
||||||
|
|
||||||
// We close all the descriptors where the close flag is set. If this
|
// We close all the descriptors where the close flag is set. If this
|
||||||
// message should have been transmitted, then closing those with close
|
// message should have been transmitted, then closing those with close
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,11 @@ class CrashReporterClient {
|
||||||
// crash reporter needs metadata), the shmem should be parsed.
|
// crash reporter needs metadata), the shmem should be parsed.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void InitSingleton(T* aToplevelProtocol) {
|
static void InitSingleton(T* aToplevelProtocol) {
|
||||||
|
// The crash reporter is not enabled in recording/replaying processes.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Shmem shmem;
|
Shmem shmem;
|
||||||
if (!AllocShmem(aToplevelProtocol, &shmem)) {
|
if (!AllocShmem(aToplevelProtocol, &shmem)) {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(false, "failed to allocate crash reporter shmem");
|
MOZ_DIAGNOSTIC_ASSERT(false, "failed to allocate crash reporter shmem");
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "CrashReporterHost.h"
|
#include "CrashReporterHost.h"
|
||||||
#include "CrashReporterMetadataShmem.h"
|
#include "CrashReporterMetadataShmem.h"
|
||||||
#include "mozilla/dom/Promise.h"
|
#include "mozilla/dom/Promise.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
#include "mozilla/Sprintf.h"
|
#include "mozilla/Sprintf.h"
|
||||||
#include "mozilla/SyncRunnable.h"
|
#include "mozilla/SyncRunnable.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
@ -100,6 +101,11 @@ bool CrashReporterHost::AdoptMinidump(nsIFile* aFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t CrashReporterHost::GetCrashType() {
|
int32_t CrashReporterHost::GetCrashType() {
|
||||||
|
if (mExtraAnnotations[CrashReporter::Annotation::RecordReplayHang]
|
||||||
|
.EqualsLiteral("1")) {
|
||||||
|
return nsICrashService::CRASH_TYPE_HANG;
|
||||||
|
}
|
||||||
|
|
||||||
if (mExtraAnnotations[CrashReporter::Annotation::PluginHang].EqualsLiteral(
|
if (mExtraAnnotations[CrashReporter::Annotation::PluginHang].EqualsLiteral(
|
||||||
"1")) {
|
"1")) {
|
||||||
return nsICrashService::CRASH_TYPE_HANG;
|
return nsICrashService::CRASH_TYPE_HANG;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/Omnijar.h"
|
#include "mozilla/Omnijar.h"
|
||||||
|
#include "mozilla/RecordReplay.h"
|
||||||
#include "mozilla/RDDProcessHost.h"
|
#include "mozilla/RDDProcessHost.h"
|
||||||
#include "mozilla/Scoped.h"
|
#include "mozilla/Scoped.h"
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
|
|
@ -142,9 +143,14 @@ class BaseProcessLauncher {
|
||||||
SprintfLiteral(mPidString, "%d", base::GetCurrentProcId());
|
SprintfLiteral(mPidString, "%d", base::GetCurrentProcId());
|
||||||
|
|
||||||
// Compute the serial event target we'll use for launching.
|
// Compute the serial event target we'll use for launching.
|
||||||
nsCOMPtr<nsIEventTarget> threadOrPool = GetIPCLauncher();
|
if (mozilla::recordreplay::IsMiddleman()) {
|
||||||
mLaunchThread = new TaskQueue(threadOrPool.forget());
|
// During Web Replay, the middleman process launches the actual content
|
||||||
|
// processes, and doesn't initialize enough of XPCOM to use thread pools.
|
||||||
|
mLaunchThread = IOThread();
|
||||||
|
} else {
|
||||||
|
nsCOMPtr<nsIEventTarget> threadOrPool = GetIPCLauncher();
|
||||||
|
mLaunchThread = new TaskQueue(threadOrPool.forget());
|
||||||
|
}
|
||||||
if (ShouldHaveDirectoryService()) {
|
if (ShouldHaveDirectoryService()) {
|
||||||
// "Current process directory" means the app dir, not the current
|
// "Current process directory" means the app dir, not the current
|
||||||
// working dir or similar.
|
// working dir or similar.
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,18 @@ static void TryRegisterStrongMemoryReporter() {
|
||||||
|
|
||||||
Atomic<size_t> MessageChannel::gUnresolvedResponses;
|
Atomic<size_t> MessageChannel::gUnresolvedResponses;
|
||||||
|
|
||||||
|
// Channels in record/replay middleman processes can forward messages that
|
||||||
|
// originated in a child recording process. Middleman processes are given
|
||||||
|
// a large negative sequence number so that sequence numbers on their messages
|
||||||
|
// can be distinguished from those on recording process messages.
|
||||||
|
static const int32_t MiddlemanStartSeqno = -(1 << 30);
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool MessageChannel::MessageOriginatesFromMiddleman(const Message& aMessage) {
|
||||||
|
MOZ_ASSERT(recordreplay::IsMiddleman());
|
||||||
|
return aMessage.seqno() < MiddlemanStartSeqno;
|
||||||
|
}
|
||||||
|
|
||||||
MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener)
|
MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener)
|
||||||
: mName(aName),
|
: mName(aName),
|
||||||
mListener(aListener),
|
mListener(aListener),
|
||||||
|
|
@ -610,6 +622,10 @@ MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener)
|
||||||
|
|
||||||
TryRegisterStrongMemoryReporter<PendingResponseReporter>();
|
TryRegisterStrongMemoryReporter<PendingResponseReporter>();
|
||||||
TryRegisterStrongMemoryReporter<ChannelCountReporter>();
|
TryRegisterStrongMemoryReporter<ChannelCountReporter>();
|
||||||
|
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
mNextSeqno = MiddlemanStartSeqno;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageChannel::~MessageChannel() {
|
MessageChannel::~MessageChannel() {
|
||||||
|
|
@ -2039,6 +2055,15 @@ void MessageChannel::MessageTask::Clear() {
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
MessageChannel::MessageTask::GetPriority(uint32_t* aPriority) {
|
MessageChannel::MessageTask::GetPriority(uint32_t* aPriority) {
|
||||||
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
// Ignore message priorities in recording/replaying processes. Incoming
|
||||||
|
// messages were sorted in the middleman process according to their
|
||||||
|
// priority before being forwarded here, and reordering them again in this
|
||||||
|
// process can cause problems such as dispatching messages for an actor
|
||||||
|
// before the constructor for that actor.
|
||||||
|
*aPriority = PRIORITY_NORMAL;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
switch (mMessage.priority()) {
|
switch (mMessage.priority()) {
|
||||||
case Message::NORMAL_PRIORITY:
|
case Message::NORMAL_PRIORITY:
|
||||||
*aPriority = PRIORITY_NORMAL;
|
*aPriority = PRIORITY_NORMAL;
|
||||||
|
|
@ -2140,8 +2165,10 @@ void MessageChannel::DispatchSyncMessage(ActorLifecycleProxy* aProxy,
|
||||||
|
|
||||||
int nestedLevel = aMsg.nested_level();
|
int nestedLevel = aMsg.nested_level();
|
||||||
|
|
||||||
MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED ||
|
MOZ_RELEASE_ASSERT(
|
||||||
NS_IsMainThread());
|
nestedLevel == IPC::Message::NOT_NESTED || NS_IsMainThread() ||
|
||||||
|
// Middleman processes forward sync messages on a non-main thread.
|
||||||
|
recordreplay::IsMiddleman());
|
||||||
#ifdef MOZ_TASK_TRACER
|
#ifdef MOZ_TASK_TRACER
|
||||||
AutoScopedLabel autolabel("sync message %s", aMsg.name());
|
AutoScopedLabel autolabel("sync message %s", aMsg.name());
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,10 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver {
|
||||||
*/
|
*/
|
||||||
bool IsCrossProcess() const { return mIsCrossProcess; }
|
bool IsCrossProcess() const { return mIsCrossProcess; }
|
||||||
|
|
||||||
|
// Return whether a message definitely originated from a middleman process,
|
||||||
|
// due to its sequence number.
|
||||||
|
static bool MessageOriginatesFromMiddleman(const Message& aMessage);
|
||||||
|
|
||||||
#ifdef OS_WIN
|
#ifdef OS_WIN
|
||||||
struct MOZ_STACK_CLASS SyncStackFrame {
|
struct MOZ_STACK_CLASS SyncStackFrame {
|
||||||
SyncStackFrame(MessageChannel* channel, bool interrupt);
|
SyncStackFrame(MessageChannel* channel, bool interrupt);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "mozilla/ipc/MessageChannel.h"
|
#include "mozilla/ipc/MessageChannel.h"
|
||||||
#include "mozilla/ipc/Transport.h"
|
#include "mozilla/ipc/Transport.h"
|
||||||
|
#include "mozilla/recordreplay/ChildIPC.h"
|
||||||
|
#include "mozilla/recordreplay/ParentIPC.h"
|
||||||
#include "mozilla/StaticMutex.h"
|
#include "mozilla/StaticMutex.h"
|
||||||
#include "mozilla/SystemGroup.h"
|
#include "mozilla/SystemGroup.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
|
@ -575,6 +577,7 @@ IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
|
||||||
mOtherPid(mozilla::ipc::kInvalidProcessId),
|
mOtherPid(mozilla::ipc::kInvalidProcessId),
|
||||||
mLastLocalId(0),
|
mLastLocalId(0),
|
||||||
mEventTargetMutex("ProtocolEventTargetMutex"),
|
mEventTargetMutex("ProtocolEventTargetMutex"),
|
||||||
|
mMiddlemanChannelOverride(nullptr),
|
||||||
mChannel(aName, this) {
|
mChannel(aName, this) {
|
||||||
mToplevel = this;
|
mToplevel = this;
|
||||||
}
|
}
|
||||||
|
|
@ -586,7 +589,15 @@ base::ProcessId IToplevelProtocol::OtherPid() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid) {
|
void IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid) {
|
||||||
mOtherPid = aOtherPid;
|
// When recording an execution, all communication we do is forwarded from
|
||||||
|
// the middleman to the parent process, so use its pid instead of the
|
||||||
|
// middleman's pid.
|
||||||
|
if (recordreplay::IsRecordingOrReplaying() &&
|
||||||
|
aOtherPid == recordreplay::child::MiddlemanProcessId()) {
|
||||||
|
mOtherPid = recordreplay::child::ParentProcessId();
|
||||||
|
} else {
|
||||||
|
mOtherPid = aOtherPid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IToplevelProtocol::Open(UniquePtr<Transport> aTransport,
|
bool IToplevelProtocol::Open(UniquePtr<Transport> aTransport,
|
||||||
|
|
@ -628,8 +639,12 @@ bool IToplevelProtocol::IsOnCxxStack() const {
|
||||||
|
|
||||||
int32_t IToplevelProtocol::NextId() {
|
int32_t IToplevelProtocol::NextId() {
|
||||||
// Genreate the next ID to use for a shared memory or protocol. Parent and
|
// Genreate the next ID to use for a shared memory or protocol. Parent and
|
||||||
// Child sides of the protocol use different pools.
|
// Child sides of the protocol use different pools, and actors created in the
|
||||||
|
// middleman need to use a distinct pool as well.
|
||||||
int32_t tag = 0;
|
int32_t tag = 0;
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
tag |= 1 << 0;
|
||||||
|
}
|
||||||
if (GetSide() == ParentSide) {
|
if (GetSide() == ParentSide) {
|
||||||
tag |= 1 << 1;
|
tag |= 1 << 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "mozilla/ipc/Shmem.h"
|
#include "mozilla/ipc/Shmem.h"
|
||||||
#include "mozilla/ipc/Transport.h"
|
#include "mozilla/ipc/Transport.h"
|
||||||
#include "mozilla/ipc/MessageLink.h"
|
#include "mozilla/ipc/MessageLink.h"
|
||||||
|
#include "mozilla/recordreplay/ChildIPC.h"
|
||||||
#include "mozilla/LinkedList.h"
|
#include "mozilla/LinkedList.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/MozPromise.h"
|
#include "mozilla/MozPromise.h"
|
||||||
|
|
@ -405,8 +406,18 @@ class IToplevelProtocol : public IProtocol {
|
||||||
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
|
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
|
||||||
bool DestroySharedMemory(Shmem& aShmem);
|
bool DestroySharedMemory(Shmem& aShmem);
|
||||||
|
|
||||||
MessageChannel* GetIPCChannel() { return &mChannel; }
|
MessageChannel* GetIPCChannel() {
|
||||||
const MessageChannel* GetIPCChannel() const { return &mChannel; }
|
if (mMiddlemanChannelOverride) {
|
||||||
|
return mMiddlemanChannelOverride;
|
||||||
|
}
|
||||||
|
return &mChannel;
|
||||||
|
}
|
||||||
|
const MessageChannel* GetIPCChannel() const {
|
||||||
|
if (mMiddlemanChannelOverride) {
|
||||||
|
return mMiddlemanChannelOverride;
|
||||||
|
}
|
||||||
|
return &mChannel;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: The target actor's Manager must already be set.
|
// NOTE: The target actor's Manager must already be set.
|
||||||
void SetEventTargetForActorInternal(IProtocol* aActor,
|
void SetEventTargetForActorInternal(IProtocol* aActor,
|
||||||
|
|
@ -509,6 +520,13 @@ class IToplevelProtocol : public IProtocol {
|
||||||
|
|
||||||
already_AddRefed<nsIEventTarget> GetMessageEventTarget(const Message& aMsg);
|
already_AddRefed<nsIEventTarget> GetMessageEventTarget(const Message& aMsg);
|
||||||
|
|
||||||
|
void SetMiddlemanIPCChannel(MessageChannel* aChannel) {
|
||||||
|
// Middleman processes sometimes need to change the channel used by a
|
||||||
|
// protocol.
|
||||||
|
MOZ_RELEASE_ASSERT(recordreplay::IsMiddleman());
|
||||||
|
mMiddlemanChannelOverride = aChannel;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Override this method in top-level protocols to change the event target
|
// Override this method in top-level protocols to change the event target
|
||||||
// for a new actor (and its sub-actors).
|
// for a new actor (and its sub-actors).
|
||||||
|
|
@ -543,6 +561,12 @@ class IToplevelProtocol : public IProtocol {
|
||||||
Mutex mEventTargetMutex;
|
Mutex mEventTargetMutex;
|
||||||
IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
|
IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
|
||||||
|
|
||||||
|
// In the middleman process for recordreplay, we override the channel which
|
||||||
|
// should be used by an actor. Due to this, we need to hold a separate pointer
|
||||||
|
// here which can be used to specify that we shouldn't send messages to our
|
||||||
|
// mChannel actor member. FIXME: This should probably be removed.
|
||||||
|
MessageChannel* mMiddlemanChannelOverride;
|
||||||
|
|
||||||
MessageChannel mChannel;
|
MessageChannel mChannel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -722,7 +746,16 @@ class Endpoint {
|
||||||
// be used to send and receive messages. The endpoint becomes invalid.
|
// be used to send and receive messages. The endpoint becomes invalid.
|
||||||
bool Bind(PFooSide* aActor) {
|
bool Bind(PFooSide* aActor) {
|
||||||
MOZ_RELEASE_ASSERT(mValid);
|
MOZ_RELEASE_ASSERT(mValid);
|
||||||
MOZ_RELEASE_ASSERT(mMyPid == base::GetCurrentProcId());
|
if (mMyPid != base::GetCurrentProcId()) {
|
||||||
|
// These pids must match, unless we are recording or replaying, in
|
||||||
|
// which case the parent process will have supplied the pid for the
|
||||||
|
// middleman process instead. Fix this here. If we're replaying
|
||||||
|
// we'll see the pid of the middleman used while recording.
|
||||||
|
MOZ_RELEASE_ASSERT(recordreplay::IsRecordingOrReplaying());
|
||||||
|
MOZ_RELEASE_ASSERT(recordreplay::IsReplaying() ||
|
||||||
|
mMyPid == recordreplay::child::MiddlemanProcessId());
|
||||||
|
mMyPid = base::GetCurrentProcId();
|
||||||
|
}
|
||||||
|
|
||||||
UniquePtr<Transport> transport =
|
UniquePtr<Transport> transport =
|
||||||
mozilla::ipc::OpenDescriptor(mTransport, mMode);
|
mozilla::ipc::OpenDescriptor(mTransport, mMode);
|
||||||
|
|
|
||||||
|
|
@ -977,6 +977,8 @@ description = test only
|
||||||
description = test only
|
description = test only
|
||||||
[PContent::SyncMessage]
|
[PContent::SyncMessage]
|
||||||
description = JS MessageManager implementation
|
description = JS MessageManager implementation
|
||||||
|
[PContent::OpenRecordReplayChannel]
|
||||||
|
description = bug 1475898 this could be async
|
||||||
[PContent::LoadPlugin]
|
[PContent::LoadPlugin]
|
||||||
description = Legacy NPAPI IPC
|
description = Legacy NPAPI IPC
|
||||||
[PContent::ConnectPluginBridge]
|
[PContent::ConnectPluginBridge]
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
interface IJSDebugger : nsISupports
|
interface IJSDebugger : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Define the global Debugger constructor on a given global.
|
* Define the global Debugger and RecordReplayControl constructors on a
|
||||||
|
* given global.
|
||||||
*/
|
*/
|
||||||
[implicit_jscontext]
|
[implicit_jscontext]
|
||||||
void addClass(in jsval global);
|
void addClass(in jsval global);
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,21 @@ JSDebugger::AddClass(JS::Handle<JS::Value> global, JSContext* cx) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recordreplay::IsRecordingOrReplaying() || recordreplay::IsMiddleman()) {
|
||||||
|
if (!recordreplay::DefineRecordReplayControlObject(cx, obj)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Define an empty RecordReplayControl object, to avoid reference errors in
|
||||||
|
// scripts that run in normal processes. DefineRecordReplayControlObject
|
||||||
|
// can't be called in normal processes.
|
||||||
|
JS::RootedObject staticObject(cx, JS_NewObject(cx, nullptr));
|
||||||
|
if (!staticObject ||
|
||||||
|
!JS_DefineProperty(cx, obj, "RecordReplayControl", staticObject, 0)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -598,7 +598,13 @@ CrossProcessCpowHolder::~CrossProcessCpowHolder() {
|
||||||
// the corresponding part of the CPOW in the other process
|
// the corresponding part of the CPOW in the other process
|
||||||
// will eventually be collected. The scope for this object
|
// will eventually be collected. The scope for this object
|
||||||
// doesn't really matter, because it immediately becomes
|
// doesn't really matter, because it immediately becomes
|
||||||
// garbage.
|
// garbage. Ignore this for middleman processes used when
|
||||||
|
// recording or replaying, as they do not have a CPOW manager
|
||||||
|
// and the message will also be received in the recording
|
||||||
|
// process.
|
||||||
|
if (recordreplay::IsMiddleman()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
AutoJSAPI jsapi;
|
AutoJSAPI jsapi;
|
||||||
if (!jsapi.Init(xpc::PrivilegedJunkScope())) {
|
if (!jsapi.Init(xpc::PrivilegedJunkScope())) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -622,6 +628,9 @@ bool CrossProcessCpowHolder::ToObject(JSContext* cx,
|
||||||
|
|
||||||
bool JavaScriptShared::Unwrap(JSContext* cx, const nsTArray<CpowEntry>& aCpows,
|
bool JavaScriptShared::Unwrap(JSContext* cx, const nsTArray<CpowEntry>& aCpows,
|
||||||
JS::MutableHandleObject objp) {
|
JS::MutableHandleObject objp) {
|
||||||
|
// Middleman processes never operate on CPOWs.
|
||||||
|
MOZ_ASSERT(!recordreplay::IsMiddleman());
|
||||||
|
|
||||||
objp.set(nullptr);
|
objp.set(nullptr);
|
||||||
|
|
||||||
if (!aCpows.Length()) {
|
if (!aCpows.Length()) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include "mozilla/Range.h"
|
#include "mozilla/Range.h"
|
||||||
#include "mozilla/Span.h"
|
#include "mozilla/Span.h"
|
||||||
#include "mozilla/Utf8.h"
|
|
||||||
|
|
||||||
#include "js/TypeDecls.h"
|
#include "js/TypeDecls.h"
|
||||||
#include "js/Utility.h"
|
#include "js/Utility.h"
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ struct JSStructuredCloneWriter;
|
||||||
|
|
||||||
struct JSPrincipals {
|
struct JSPrincipals {
|
||||||
/* Don't call "destroy"; use reference counting macros below. */
|
/* Don't call "destroy"; use reference counting macros below. */
|
||||||
mozilla::Atomic<int32_t, mozilla::SequentiallyConsistent> refcount;
|
mozilla::Atomic<int32_t, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
refcount;
|
||||||
|
|
||||||
#ifdef JS_DEBUG
|
#ifdef JS_DEBUG
|
||||||
/* A helper to facilitate principals debugging. */
|
/* A helper to facilitate principals debugging. */
|
||||||
|
|
|
||||||
|
|
@ -123,30 +123,42 @@ class ProfilingStackFrame {
|
||||||
|
|
||||||
// Descriptive label for this stack frame. Must be a static string! Can be
|
// Descriptive label for this stack frame. Must be a static string! Can be
|
||||||
// an empty string, but not a null pointer.
|
// an empty string, but not a null pointer.
|
||||||
mozilla::Atomic<const char*, mozilla::ReleaseAcquire> label_;
|
mozilla::Atomic<const char*, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
label_;
|
||||||
|
|
||||||
// An additional descriptive string of this frame which is combined with
|
// An additional descriptive string of this frame which is combined with
|
||||||
// |label_| in profiler output. Need not be (and usually isn't) static. Can
|
// |label_| in profiler output. Need not be (and usually isn't) static. Can
|
||||||
// be null.
|
// be null.
|
||||||
mozilla::Atomic<const char*, mozilla::ReleaseAcquire> dynamicString_;
|
mozilla::Atomic<const char*, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
dynamicString_;
|
||||||
|
|
||||||
// Stack pointer for non-JS stack frames, the script pointer otherwise.
|
// Stack pointer for non-JS stack frames, the script pointer otherwise.
|
||||||
mozilla::Atomic<void*, mozilla::ReleaseAcquire> spOrScript;
|
mozilla::Atomic<void*, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
spOrScript;
|
||||||
|
|
||||||
// The bytecode offset for JS stack frames.
|
// The bytecode offset for JS stack frames.
|
||||||
// Must not be used on non-JS frames; it'll contain either the default 0,
|
// Must not be used on non-JS frames; it'll contain either the default 0,
|
||||||
// or a leftover value from a previous JS stack frame that was using this
|
// or a leftover value from a previous JS stack frame that was using this
|
||||||
// ProfilingStackFrame object.
|
// ProfilingStackFrame object.
|
||||||
mozilla::Atomic<int32_t, mozilla::ReleaseAcquire> pcOffsetIfJS_;
|
mozilla::Atomic<int32_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
pcOffsetIfJS_;
|
||||||
|
|
||||||
// ID of the JS Realm for JS stack frames.
|
// ID of the JS Realm for JS stack frames.
|
||||||
// Must not be used on non-JS frames; it'll contain either the default 0,
|
// Must not be used on non-JS frames; it'll contain either the default 0,
|
||||||
// or a leftover value from a previous JS stack frame that was using this
|
// or a leftover value from a previous JS stack frame that was using this
|
||||||
// ProfilingStackFrame object.
|
// ProfilingStackFrame object.
|
||||||
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> realmID_;
|
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
realmID_;
|
||||||
|
|
||||||
// Bits 0...8 hold the Flags. Bits 9...31 hold the category pair.
|
// Bits 0...8 hold the Flags. Bits 9...31 hold the category pair.
|
||||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> flagsAndCategoryPair_;
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
flagsAndCategoryPair_;
|
||||||
|
|
||||||
static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
|
static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
|
||||||
|
|
||||||
|
|
@ -481,7 +493,8 @@ class JS_FRIEND_API ProfilingStack final {
|
||||||
// written from the current thread.
|
// written from the current thread.
|
||||||
//
|
//
|
||||||
// This is effectively a unique pointer.
|
// This is effectively a unique pointer.
|
||||||
mozilla::Atomic<js::ProfilingStackFrame*, mozilla::SequentiallyConsistent>
|
mozilla::Atomic<js::ProfilingStackFrame*, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
frames{nullptr};
|
frames{nullptr};
|
||||||
|
|
||||||
// This may exceed the capacity, so instead use the stackSize() method to
|
// This may exceed the capacity, so instead use the stackSize() method to
|
||||||
|
|
@ -495,7 +508,9 @@ class JS_FRIEND_API ProfilingStack final {
|
||||||
// This is an atomic variable that uses ReleaseAcquire memory ordering.
|
// This is an atomic variable that uses ReleaseAcquire memory ordering.
|
||||||
// See the "Concurrency considerations" paragraph at the top of this file
|
// See the "Concurrency considerations" paragraph at the top of this file
|
||||||
// for more details.
|
// for more details.
|
||||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> stackPointer;
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
stackPointer;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
|
||||||
|
|
@ -764,7 +764,8 @@ void js::FutexThread::lock() {
|
||||||
lock->lock();
|
lock->lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent>
|
/* static */ mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
FutexThread::lock_;
|
FutexThread::lock_;
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,9 @@ class FutexThread {
|
||||||
// Shared futex lock for all runtimes. We can perhaps do better,
|
// Shared futex lock for all runtimes. We can perhaps do better,
|
||||||
// but any lock will need to be per-domain (consider SharedWorker)
|
// but any lock will need to be per-domain (consider SharedWorker)
|
||||||
// or coarser.
|
// or coarser.
|
||||||
static mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent> lock_;
|
static mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
lock_;
|
||||||
|
|
||||||
// A flag that controls whether waiting is allowed.
|
// A flag that controls whether waiting is allowed.
|
||||||
ThreadData<bool> canWait_;
|
ThreadData<bool> canWait_;
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,12 @@ static MOZ_ALWAYS_INLINE bool ShouldCaptureDebugInfo(JSContext* cx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static mozilla::Maybe<mozilla::TimeStamp> MaybeNow() {
|
static mozilla::Maybe<mozilla::TimeStamp> MaybeNow() {
|
||||||
|
// ShouldCaptureDebugInfo() may return inconsistent values when recording
|
||||||
|
// or replaying, so in places where we might need the current time for
|
||||||
|
// promise debug info we always capture the current time.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return mozilla::Some(mozilla::TimeStamp::Now());
|
||||||
|
}
|
||||||
return mozilla::Nothing();
|
return mozilla::Nothing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -479,6 +485,7 @@ class PromiseDebugInfo : public NativeObject {
|
||||||
if (!ShouldCaptureDebugInfo(cx)) {
|
if (!ShouldCaptureDebugInfo(cx)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||||
|
|
||||||
// If async stacks weren't enabled and the Promise's global wasn't a
|
// If async stacks weren't enabled and the Promise's global wasn't a
|
||||||
// debuggee when the Promise was created, we won't have a debugInfo
|
// debuggee when the Promise was created, we won't have a debugInfo
|
||||||
|
|
@ -2227,6 +2234,7 @@ CreatePromiseObjectInternal(JSContext* cx, HandleObject proto /* = nullptr */,
|
||||||
if (MOZ_LIKELY(!ShouldCaptureDebugInfo(cx))) {
|
if (MOZ_LIKELY(!ShouldCaptureDebugInfo(cx))) {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||||
|
|
||||||
// Store an allocation stack so we can later figure out what the
|
// Store an allocation stack so we can later figure out what the
|
||||||
// control flow was for some unexpected results. Frightfully expensive,
|
// control flow was for some unexpected results. Frightfully expensive,
|
||||||
|
|
@ -5760,6 +5768,15 @@ JS::AutoDebuggerJobQueueInterruption::~AutoDebuggerJobQueueInterruption() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JS::AutoDebuggerJobQueueInterruption::init(JSContext* cx) {
|
bool JS::AutoDebuggerJobQueueInterruption::init(JSContext* cx) {
|
||||||
|
// When recording or replaying this class doesn't do anything. Callbacks on
|
||||||
|
// the job queue can interact with the recording, and this class can be used
|
||||||
|
// at different places between recording and replay. Because nested event
|
||||||
|
// loops aren't pushed in debugger callbacks when recording/replaying, the
|
||||||
|
// job queue does not need to be saved during debugger callbacks.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(cx->jobQueue);
|
MOZ_ASSERT(cx->jobQueue);
|
||||||
this->cx = cx;
|
this->cx = cx;
|
||||||
saved = cx->jobQueue->saveJobQueue(cx);
|
saved = cx->jobQueue->saveJobQueue(cx);
|
||||||
|
|
@ -5767,8 +5784,10 @@ bool JS::AutoDebuggerJobQueueInterruption::init(JSContext* cx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void JS::AutoDebuggerJobQueueInterruption::runJobs() {
|
void JS::AutoDebuggerJobQueueInterruption::runJobs() {
|
||||||
JS::AutoSaveExceptionState ases(cx);
|
if (!mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
cx->jobQueue->runJobs(cx);
|
JS::AutoSaveExceptionState ases(cx);
|
||||||
|
cx->jobQueue->runJobs(cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const JSJitInfo promise_then_info = {
|
const JSJitInfo promise_then_info = {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "mozilla/GuardObjects.h" // for MOZ_GUARD_OBJECT_NOTIFIER_PARAM
|
#include "mozilla/GuardObjects.h" // for MOZ_GUARD_OBJECT_NOTIFIER_PARAM
|
||||||
#include "mozilla/HashTable.h" // for HashSet<>::Range, HashMapEntry
|
#include "mozilla/HashTable.h" // for HashSet<>::Range, HashMapEntry
|
||||||
#include "mozilla/Maybe.h" // for Maybe, Nothing, Some
|
#include "mozilla/Maybe.h" // for Maybe, Nothing, Some
|
||||||
|
#include "mozilla/RecordReplay.h" // for IsMiddleman
|
||||||
#include "mozilla/ScopeExit.h" // for MakeScopeExit, ScopeExit
|
#include "mozilla/ScopeExit.h" // for MakeScopeExit, ScopeExit
|
||||||
#include "mozilla/ThreadLocal.h" // for ThreadLocal
|
#include "mozilla/ThreadLocal.h" // for ThreadLocal
|
||||||
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||||
|
|
@ -5891,6 +5892,29 @@ bool Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool Debugger::recordReplayProcessKind(JSContext* cx, unsigned argc,
|
||||||
|
Value* vp) {
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
|
if (mozilla::recordreplay::IsMiddleman()) {
|
||||||
|
JSString* str = JS_NewStringCopyZ(cx, "Middleman");
|
||||||
|
if (!str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
args.rval().setString(str);
|
||||||
|
} else if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
JSString* str = JS_NewStringCopyZ(cx, "RecordingReplaying");
|
||||||
|
if (!str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
args.rval().setString(str);
|
||||||
|
} else {
|
||||||
|
args.rval().setUndefined();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Debugger::CallData::adoptDebuggeeValue() {
|
bool Debugger::CallData::adoptDebuggeeValue() {
|
||||||
if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1)) {
|
if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -6019,7 +6043,9 @@ const JSFunctionSpec Debugger::methods[] = {
|
||||||
JS_FS_END};
|
JS_FS_END};
|
||||||
|
|
||||||
const JSFunctionSpec Debugger::static_methods[]{
|
const JSFunctionSpec Debugger::static_methods[]{
|
||||||
JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0), JS_FS_END};
|
JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
|
||||||
|
JS_FN("recordReplayProcessKind", Debugger::recordReplayProcessKind, 0, 0),
|
||||||
|
JS_FS_END};
|
||||||
|
|
||||||
DebuggerScript* Debugger::newDebuggerScript(
|
DebuggerScript* Debugger::newDebuggerScript(
|
||||||
JSContext* cx, Handle<DebuggerScriptReferent> referent) {
|
JSContext* cx, Handle<DebuggerScriptReferent> referent) {
|
||||||
|
|
|
||||||
|
|
@ -517,6 +517,12 @@ The functions described below are not called with a `this` value.
|
||||||
compilation - accumulate lines in a buffer until isCompilableUnit is true,
|
compilation - accumulate lines in a buffer until isCompilableUnit is true,
|
||||||
then pass it to the compiler.
|
then pass it to the compiler.
|
||||||
|
|
||||||
|
### `recordReplayProcessKind()`
|
||||||
|
: Return the kind of record/replay firefox process that is currently
|
||||||
|
running: the string "RecordingReplaying" if this is a recording or
|
||||||
|
replaying process, the string "Middleman" if this is a middleman
|
||||||
|
process, or undefined for normal firefox content or UI processes.
|
||||||
|
|
||||||
[add]: #adddebuggee-global
|
[add]: #adddebuggee-global
|
||||||
[source]: Debugger.Source.md
|
[source]: Debugger.Source.md
|
||||||
[script]: Debugger.Script.md
|
[script]: Debugger.Script.md
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ namespace js {
|
||||||
|
|
||||||
// Memory protection occurs at non-deterministic points when
|
// Memory protection occurs at non-deterministic points when
|
||||||
// recording/replaying.
|
// recording/replaying.
|
||||||
static mozilla::Atomic<bool, mozilla::SequentiallyConsistent>
|
static mozilla::Atomic<bool, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
sProtectedRegionsInit(false);
|
sProtectedRegionsInit(false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,12 @@ bool frontend::SourceAwareCompiler<Unit>::createSourceAndParser(
|
||||||
if (!compilationInfo.assignSource(sourceBuffer_)) {
|
if (!compilationInfo.assignSource(sourceBuffer_)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Note the contents of any compiled scripts when recording/replaying.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
mozilla::recordreplay::NoteContentParse(
|
||||||
|
this, compilationInfo.options.filename(), "application/javascript",
|
||||||
|
sourceBuffer_.units(), sourceBuffer_.length());
|
||||||
|
}
|
||||||
|
|
||||||
if (CanLazilyParse(compilationInfo)) {
|
if (CanLazilyParse(compilationInfo)) {
|
||||||
syntaxParser.emplace(compilationInfo.cx, compilationInfo.options,
|
syntaxParser.emplace(compilationInfo.cx, compilationInfo.options,
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,8 @@
|
||||||
#include "mozilla/Span.h" // mozilla::Span
|
#include "mozilla/Span.h" // mozilla::Span
|
||||||
#include "mozilla/Vector.h" // mozilla::Vector
|
#include "mozilla/Vector.h" // mozilla::Vector
|
||||||
|
|
||||||
#include <functional> // std::function
|
#include <stddef.h> // ptrdiff_t
|
||||||
#include <stddef.h> // ptrdiff_t
|
#include <stdint.h> // uint16_t, uint32_t
|
||||||
#include <stdint.h> // uint16_t, uint32_t
|
|
||||||
|
|
||||||
#include "jsapi.h" // CompletionKind
|
#include "jsapi.h" // CompletionKind
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,8 @@ class ArenaLists {
|
||||||
};
|
};
|
||||||
|
|
||||||
using ConcurrentUseState =
|
using ConcurrentUseState =
|
||||||
mozilla::Atomic<ConcurrentUse, mozilla::SequentiallyConsistent>;
|
mozilla::Atomic<ConcurrentUse, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>;
|
||||||
|
|
||||||
// Whether this structure can be accessed by other threads.
|
// Whether this structure can be accessed by other threads.
|
||||||
UnprotectedData<AllAllocKindArray<ConcurrentUseState>> concurrentUseState_;
|
UnprotectedData<AllAllocKindArray<ConcurrentUseState>> concurrentUseState_;
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ class AtomMarkingRuntime {
|
||||||
public:
|
public:
|
||||||
// The extent of all allocated and free words in atom mark bitmaps.
|
// The extent of all allocated and free words in atom mark bitmaps.
|
||||||
// This monotonically increases and may be read from without locking.
|
// This monotonically increases and may be read from without locking.
|
||||||
mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> allocatedWords;
|
mozilla::Atomic<size_t, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
allocatedWords;
|
||||||
|
|
||||||
AtomMarkingRuntime() : allocatedWords(0) {}
|
AtomMarkingRuntime() : allocatedWords(0) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1705,7 +1705,8 @@ bool GCRuntime::shouldCompact() {
|
||||||
|
|
||||||
bool GCRuntime::isCompactingGCEnabled() const {
|
bool GCRuntime::isCompactingGCEnabled() const {
|
||||||
return compactingEnabled &&
|
return compactingEnabled &&
|
||||||
rt->mainContextFromOwnThread()->compactingDisabledCount == 0;
|
rt->mainContextFromOwnThread()->compactingDisabledCount == 0 &&
|
||||||
|
!mozilla::recordreplay::IsRecordingOrReplaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx) : cx(cx) {
|
AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx) : cx(cx) {
|
||||||
|
|
@ -2916,6 +2917,42 @@ void Nursery::requestMinorGC(JS::GCReason reason) const {
|
||||||
runtime()->mainContextFromOwnThread()->requestInterrupt(InterruptReason::GC);
|
runtime()->mainContextFromOwnThread()->requestInterrupt(InterruptReason::GC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return false if a pending GC may not occur because we are recording or
|
||||||
|
// replaying. GCs must occur at the same points when replaying as they did
|
||||||
|
// while recording, so any trigger reasons whose behavior is non-deterministic
|
||||||
|
// between recording and replaying are excluded here.
|
||||||
|
//
|
||||||
|
// Non-deterministic behaviors here are very narrow: the amount of malloc'ed
|
||||||
|
// memory or memory used by GC things may vary between recording or replaying,
|
||||||
|
// but other behaviors that would normally be non-deterministic (timers and so
|
||||||
|
// forth) are captured in the recording and replayed exactly.
|
||||||
|
static bool RecordReplayCheckCanGC(JS::GCReason reason) {
|
||||||
|
if (!mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case JS::GCReason::EAGER_ALLOC_TRIGGER:
|
||||||
|
case JS::GCReason::LAST_DITCH:
|
||||||
|
case JS::GCReason::TOO_MUCH_MALLOC:
|
||||||
|
case JS::GCReason::ALLOC_TRIGGER:
|
||||||
|
case JS::GCReason::DELAYED_ATOMS_GC:
|
||||||
|
case JS::GCReason::TOO_MUCH_WASM_MEMORY:
|
||||||
|
case JS::GCReason::TOO_MUCH_JIT_CODE:
|
||||||
|
case JS::GCReason::INCREMENTAL_ALLOC_TRIGGER:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the above filter misses a non-deterministically triggered GC, this
|
||||||
|
// assertion will fail.
|
||||||
|
mozilla::recordreplay::RecordReplayAssert("RecordReplayCheckCanGC %d",
|
||||||
|
(int)reason);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GCRuntime::triggerGC(JS::GCReason reason) {
|
bool GCRuntime::triggerGC(JS::GCReason reason) {
|
||||||
/*
|
/*
|
||||||
* Don't trigger GCs if this is being called off the main thread from
|
* Don't trigger GCs if this is being called off the main thread from
|
||||||
|
|
@ -2930,6 +2967,11 @@ bool GCRuntime::triggerGC(JS::GCReason reason) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GCs can only be triggered in certain ways when recording/replaying.
|
||||||
|
if (!RecordReplayCheckCanGC(reason)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
JS::PrepareForFullGC(rt->mainContextFromOwnThread());
|
JS::PrepareForFullGC(rt->mainContextFromOwnThread());
|
||||||
requestMajorGC(reason);
|
requestMajorGC(reason);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -3070,6 +3112,11 @@ bool GCRuntime::triggerZoneGC(Zone* zone, JS::GCReason reason, size_t used,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GCs can only be triggered in certain ways when recording/replaying.
|
||||||
|
if (!RecordReplayCheckCanGC(reason)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
if (hasZealMode(ZealMode::Alloc)) {
|
if (hasZealMode(ZealMode::Alloc)) {
|
||||||
MOZ_RELEASE_ASSERT(triggerGC(reason));
|
MOZ_RELEASE_ASSERT(triggerGC(reason));
|
||||||
|
|
@ -5414,6 +5461,10 @@ IncrementalProgress GCRuntime::markUntilBudgetExhausted(
|
||||||
SliceBudget& sliceBudget) {
|
SliceBudget& sliceBudget) {
|
||||||
// Run a marking slice and return whether the stack is now empty.
|
// Run a marking slice and return whether the stack is now empty.
|
||||||
|
|
||||||
|
// Marked GC things may vary between recording and replaying, so marking
|
||||||
|
// and sweeping should not perform any recorded events.
|
||||||
|
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
AutoSetThreadIsMarking threadIsMarking;
|
AutoSetThreadIsMarking threadIsMarking;
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
@ -6036,6 +6087,10 @@ bool GCRuntime::initSweepActions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
IncrementalProgress GCRuntime::performSweepActions(SliceBudget& budget) {
|
IncrementalProgress GCRuntime::performSweepActions(SliceBudget& budget) {
|
||||||
|
// Marked GC things may vary between recording and replaying, so sweep
|
||||||
|
// actions should not perform any recorded events.
|
||||||
|
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||||
|
|
||||||
gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
|
gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
|
||||||
JSFreeOp fop(rt);
|
JSFreeOp fop(rt);
|
||||||
|
|
||||||
|
|
@ -7244,6 +7299,11 @@ SliceBudget GCRuntime::defaultBudget(JS::GCReason reason, int64_t millis) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCRuntime::gc(JSGCInvocationKind gckind, JS::GCReason reason) {
|
void GCRuntime::gc(JSGCInvocationKind gckind, JS::GCReason reason) {
|
||||||
|
// Watch out for calls to gc() that don't go through triggerGC().
|
||||||
|
if (!RecordReplayCheckCanGC(reason)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
collect(true, SliceBudget::unlimited(), mozilla::Some(gckind), reason);
|
collect(true, SliceBudget::unlimited(), mozilla::Some(gckind), reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8216,7 +8276,8 @@ JS_PUBLIC_API void JS::DisableIncrementalGC(JSContext* cx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API bool JS::IsIncrementalGCEnabled(JSContext* cx) {
|
JS_PUBLIC_API bool JS::IsIncrementalGCEnabled(JSContext* cx) {
|
||||||
return cx->runtime()->gc.isIncrementalGCEnabled();
|
return cx->runtime()->gc.isIncrementalGCEnabled() &&
|
||||||
|
!mozilla::recordreplay::IsRecordingOrReplaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSContext* cx) {
|
JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSContext* cx) {
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ class GCParallelTask : public mozilla::LinkedListElement<GCParallelTask>,
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// A flag to signal a request for early completion of the off-thread task.
|
// A flag to signal a request for early completion of the off-thread task.
|
||||||
mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire> cancel_;
|
mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
cancel_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GCParallelTask(gc::GCRuntime* gc)
|
explicit GCParallelTask(gc::GCRuntime* gc)
|
||||||
|
|
|
||||||
|
|
@ -795,7 +795,9 @@ class GCRuntime {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Any activity affecting the heap.
|
// Any activity affecting the heap.
|
||||||
mozilla::Atomic<JS::HeapState, mozilla::SequentiallyConsistent> heapState_;
|
mozilla::Atomic<JS::HeapState, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
heapState_;
|
||||||
friend class AutoHeapSession;
|
friend class AutoHeapSession;
|
||||||
friend class JS::AutoEnterCycleCollection;
|
friend class JS::AutoEnterCycleCollection;
|
||||||
|
|
||||||
|
|
@ -841,12 +843,16 @@ class GCRuntime {
|
||||||
MainThreadData<RootedValueMap> rootsHash;
|
MainThreadData<RootedValueMap> rootsHash;
|
||||||
|
|
||||||
// An incrementing id used to assign unique ids to cells that require one.
|
// An incrementing id used to assign unique ids to cells that require one.
|
||||||
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
|
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
nextCellUniqueId_;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Number of the committed arenas in all GC chunks including empty chunks.
|
* Number of the committed arenas in all GC chunks including empty chunks.
|
||||||
*/
|
*/
|
||||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
numArenasFreeCommitted;
|
||||||
MainThreadData<VerifyPreTracer*> verifyPreData;
|
MainThreadData<VerifyPreTracer*> verifyPreData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -860,7 +866,9 @@ class GCRuntime {
|
||||||
*/
|
*/
|
||||||
MainThreadData<JSGCMode> mode;
|
MainThreadData<JSGCMode> mode;
|
||||||
|
|
||||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
|
mozilla::Atomic<size_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
numActiveZoneIters;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The self hosting zone is collected once after initialization. We don't
|
* The self hosting zone is collected once after initialization. We don't
|
||||||
|
|
@ -898,7 +906,9 @@ class GCRuntime {
|
||||||
*/
|
*/
|
||||||
UnprotectedData<bool> grayBitsValid;
|
UnprotectedData<bool> grayBitsValid;
|
||||||
|
|
||||||
mozilla::Atomic<JS::GCReason, mozilla::Relaxed> majorGCTriggerReason;
|
mozilla::Atomic<JS::GCReason, mozilla::Relaxed,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
majorGCTriggerReason;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* Perform full GC if rt->keepAtoms() becomes false. */
|
/* Perform full GC if rt->keepAtoms() becomes false. */
|
||||||
|
|
|
||||||
|
|
@ -3896,6 +3896,10 @@ bool js::gc::UnmarkGrayGCThingUnchecked(JSRuntime* rt, JS::GCCellPtr thing) {
|
||||||
MOZ_ASSERT(thing);
|
MOZ_ASSERT(thing);
|
||||||
MOZ_ASSERT(thing.asCell()->isMarkedGray());
|
MOZ_ASSERT(thing.asCell()->isMarkedGray());
|
||||||
|
|
||||||
|
// Gray cell unmarking can occur at different points between recording and
|
||||||
|
// replay, so disallow recorded events from occurring in the tracer.
|
||||||
|
mozilla::recordreplay::AutoDisallowThreadEvents d;
|
||||||
|
|
||||||
AutoGeckoProfilerEntry profilingStackFrame(
|
AutoGeckoProfilerEntry profilingStackFrame(
|
||||||
TlsContext.get(), "UnmarkGrayGCThing", JS::ProfilingCategoryPair::GCCC);
|
TlsContext.get(), "UnmarkGrayGCThing", JS::ProfilingCategoryPair::GCCC);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,9 +62,13 @@ static size_t virtualMemoryLimit = size_t(-1);
|
||||||
* VirtualAlloc always hands out regions of memory in increasing order.
|
* VirtualAlloc always hands out regions of memory in increasing order.
|
||||||
*/
|
*/
|
||||||
#if defined(XP_DARWIN)
|
#if defined(XP_DARWIN)
|
||||||
static mozilla::Atomic<int, mozilla::Relaxed> growthDirection(1);
|
static mozilla::Atomic<int, mozilla::Relaxed,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
growthDirection(1);
|
||||||
#elif defined(XP_UNIX)
|
#elif defined(XP_UNIX)
|
||||||
static mozilla::Atomic<int, mozilla::Relaxed> growthDirection(0);
|
static mozilla::Atomic<int, mozilla::Relaxed,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
growthDirection(0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -264,6 +268,7 @@ static inline uint64_t GetNumberInRange(uint64_t minNum, uint64_t maxNum) {
|
||||||
do {
|
do {
|
||||||
mozilla::Maybe<uint64_t> result;
|
mozilla::Maybe<uint64_t> result;
|
||||||
do {
|
do {
|
||||||
|
mozilla::recordreplay::AutoPassThroughThreadEvents pt;
|
||||||
result = mozilla::RandomUint64();
|
result = mozilla::RandomUint64();
|
||||||
} while (!result);
|
} while (!result);
|
||||||
rndNum = result.value() / binSize;
|
rndNum = result.value() / binSize;
|
||||||
|
|
@ -830,6 +835,9 @@ bool MarkPagesInUseHard(void* region, size_t length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetPageFaultCount() {
|
size_t GetPageFaultCount() {
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
PROCESS_MEMORY_COUNTERS pmc;
|
PROCESS_MEMORY_COUNTERS pmc;
|
||||||
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)) == 0) {
|
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)) == 0) {
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,12 @@ js::Nursery::Nursery(GCRuntime* gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool js::Nursery::init(AutoLockGCBgAlloc& lock) {
|
bool js::Nursery::init(AutoLockGCBgAlloc& lock) {
|
||||||
|
// The nursery is permanently disabled when recording or replaying. Nursery
|
||||||
|
// collections may occur at non-deterministic points in execution.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
capacity_ = roundSize(tunables().gcMinNurseryBytes());
|
capacity_ = roundSize(tunables().gcMinNurseryBytes());
|
||||||
if (!allocateNextChunk(0, lock)) {
|
if (!allocateNextChunk(0, lock)) {
|
||||||
capacity_ = 0;
|
capacity_ = 0;
|
||||||
|
|
@ -293,7 +299,7 @@ js::Nursery::~Nursery() { disable(); }
|
||||||
void js::Nursery::enable() {
|
void js::Nursery::enable() {
|
||||||
MOZ_ASSERT(isEmpty());
|
MOZ_ASSERT(isEmpty());
|
||||||
MOZ_ASSERT(!gc->isVerifyPreBarriersEnabled());
|
MOZ_ASSERT(!gc->isVerifyPreBarriersEnabled());
|
||||||
if (isEnabled()) {
|
if (isEnabled() || mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -958,6 +964,8 @@ void js::Nursery::collect(JS::GCReason reason) {
|
||||||
JSRuntime* rt = runtime();
|
JSRuntime* rt = runtime();
|
||||||
MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
|
MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
|
||||||
|
|
||||||
|
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||||
|
|
||||||
if (!isEnabled() || isEmpty()) {
|
if (!isEnabled() || isEmpty()) {
|
||||||
// Our barriers are not always exact, and there may be entries in the
|
// Our barriers are not always exact, and there may be entries in the
|
||||||
// storebuffer even when the nursery is disabled or empty. It's not safe
|
// storebuffer even when the nursery is disabled or empty. It's not safe
|
||||||
|
|
|
||||||
|
|
@ -680,7 +680,9 @@ struct TriggerResult {
|
||||||
size_t thresholdBytes;
|
size_t thresholdBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
using AtomicByteCount = mozilla::Atomic<size_t, mozilla::ReleaseAcquire>;
|
using AtomicByteCount =
|
||||||
|
mozilla::Atomic<size_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tracks the size of allocated data. This is used for both GC and malloc data.
|
* Tracks the size of allocated data. This is used for both GC and malloc data.
|
||||||
|
|
|
||||||
|
|
@ -349,8 +349,10 @@ struct Statistics {
|
||||||
PhaseTimeTable parallelTimes;
|
PhaseTimeTable parallelTimes;
|
||||||
|
|
||||||
/* Number of events of this type for this GC. */
|
/* Number of events of this type for this GC. */
|
||||||
EnumeratedArray<Count, COUNT_LIMIT,
|
EnumeratedArray<
|
||||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire>>
|
Count, COUNT_LIMIT,
|
||||||
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>>
|
||||||
counts;
|
counts;
|
||||||
|
|
||||||
/* Other GC statistics. */
|
/* Other GC statistics. */
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,8 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
|
||||||
js::WriteOnceData<bool> isSystemZone_;
|
js::WriteOnceData<bool> isSystemZone_;
|
||||||
|
|
||||||
enum class HelperThreadUse : uint32_t { None, Pending, Active };
|
enum class HelperThreadUse : uint32_t { None, Pending, Active };
|
||||||
mozilla::Atomic<HelperThreadUse, mozilla::SequentiallyConsistent>
|
mozilla::Atomic<HelperThreadUse, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
helperThreadUse_;
|
helperThreadUse_;
|
||||||
|
|
||||||
// The helper thread context with exclusive access to this zone, if
|
// The helper thread context with exclusive access to this zone, if
|
||||||
|
|
@ -230,7 +231,9 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
|
||||||
js::ZoneOrGCTaskData<js::gc::UniqueIdMap> uniqueIds_;
|
js::ZoneOrGCTaskData<js::gc::UniqueIdMap> uniqueIds_;
|
||||||
|
|
||||||
// Number of allocations since the most recent minor GC for this thread.
|
// Number of allocations since the most recent minor GC for this thread.
|
||||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> tenuredAllocsSinceMinorGC_;
|
mozilla::Atomic<uint32_t, mozilla::Relaxed,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
tenuredAllocsSinceMinorGC_;
|
||||||
|
|
||||||
// Live weakmaps in this zone.
|
// Live weakmaps in this zone.
|
||||||
js::ZoneOrGCTaskData<mozilla::LinkedList<js::WeakMapBase>> gcWeakMapList_;
|
js::ZoneOrGCTaskData<mozilla::LinkedList<js::WeakMapBase>> gcWeakMapList_;
|
||||||
|
|
|
||||||
|
|
@ -481,6 +481,9 @@ static jsbytecode* GetResumePC(JSScript* script, jsbytecode* pc,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (skippedLoopHead && script->trackRecordReplayProgress()) {
|
||||||
|
mozilla::recordreplay::AdvanceExecutionProgressCounter();
|
||||||
|
}
|
||||||
|
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2248,7 +2248,23 @@ bool BaselineCodeGen<Handler>::emit_LoopHead() {
|
||||||
if (!emitWarmUpCounterIncrement()) {
|
if (!emitWarmUpCounterIncrement()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return emitIncExecutionProgressCounter(R0.scratchReg());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Handler>
|
||||||
|
bool BaselineCodeGen<Handler>::emitIncExecutionProgressCounter(
|
||||||
|
Register scratch) {
|
||||||
|
if (!mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto incCounter = [this]() {
|
||||||
|
masm.inc64(
|
||||||
|
AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return emitTestScriptFlag(JSScript::MutableFlags::TrackRecordReplayProgress,
|
||||||
|
true, incCounter, scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Handler>
|
template <typename Handler>
|
||||||
|
|
@ -6718,6 +6734,10 @@ bool BaselineCodeGen<Handler>::emitPrologue() {
|
||||||
// chain is in R1. For function scripts, the env chain is in the callee.
|
// chain is in R1. For function scripts, the env chain is in the callee.
|
||||||
emitInitFrameFields(R1.scratchReg());
|
emitInitFrameFields(R1.scratchReg());
|
||||||
|
|
||||||
|
if (!emitIncExecutionProgressCounter(R2.scratchReg())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// When compiling with Debugger instrumentation, set the debuggeeness of
|
// When compiling with Debugger instrumentation, set the debuggeeness of
|
||||||
// the frame before any operation that can call into the VM.
|
// the frame before any operation that can call into the VM.
|
||||||
if (!emitIsDebuggeeCheck()) {
|
if (!emitIsDebuggeeCheck()) {
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,8 @@ class BaselineCodeGen {
|
||||||
Register scratch);
|
Register scratch);
|
||||||
void emitJumpToInterpretOpLabel();
|
void emitJumpToInterpretOpLabel();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitIncExecutionProgressCounter(Register scratch);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit = false);
|
MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit = false);
|
||||||
void emitLoadReturnValue(ValueOperand val);
|
void emitLoadReturnValue(ValueOperand val);
|
||||||
void emitPushNonArrowFunctionNewTarget();
|
void emitPushNonArrowFunctionNewTarget();
|
||||||
|
|
|
||||||
|
|
@ -4801,6 +4801,11 @@ void CacheIRCompiler::emitPostBarrierShared(Register obj,
|
||||||
Register maybeIndex) {
|
Register maybeIndex) {
|
||||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||||
|
|
||||||
|
// Generational GC is disabled for WebReplay.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (val.constant()) {
|
if (val.constant()) {
|
||||||
MOZ_ASSERT_IF(val.value().isGCThing(),
|
MOZ_ASSERT_IF(val.value().isGCThing(),
|
||||||
!IsInsideNursery(val.value().toGCThing()));
|
!IsInsideNursery(val.value().toGCThing()));
|
||||||
|
|
|
||||||
|
|
@ -13020,6 +13020,11 @@ void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) {
|
||||||
OutOfLineCode* ool =
|
OutOfLineCode* ool =
|
||||||
oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
|
oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
|
||||||
|
|
||||||
|
if (lir->mir()->trackRecordReplayProgress()) {
|
||||||
|
masm.inc64(
|
||||||
|
AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
|
||||||
|
}
|
||||||
|
|
||||||
const void* interruptAddr = gen->runtime->addressOfInterruptBits();
|
const void* interruptAddr = gen->runtime->addressOfInterruptBits();
|
||||||
masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
|
masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
|
||||||
ool->entry());
|
ool->entry());
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,7 @@ class CompileInfo {
|
||||||
hadOverflowBailout_(script->hadOverflowBailout()),
|
hadOverflowBailout_(script->hadOverflowBailout()),
|
||||||
hadFrequentBailouts_(script->hadFrequentBailouts()),
|
hadFrequentBailouts_(script->hadFrequentBailouts()),
|
||||||
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
||||||
|
trackRecordReplayProgress_(script->trackRecordReplayProgress()),
|
||||||
inlineScriptTree_(inlineScriptTree) {
|
inlineScriptTree_(inlineScriptTree) {
|
||||||
MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead);
|
MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead);
|
||||||
|
|
||||||
|
|
@ -230,6 +231,7 @@ class CompileInfo {
|
||||||
hadOverflowBailout_(false),
|
hadOverflowBailout_(false),
|
||||||
hadFrequentBailouts_(false),
|
hadFrequentBailouts_(false),
|
||||||
mayReadFrameArgsDirectly_(false),
|
mayReadFrameArgsDirectly_(false),
|
||||||
|
trackRecordReplayProgress_(false),
|
||||||
inlineScriptTree_(nullptr),
|
inlineScriptTree_(nullptr),
|
||||||
needsBodyEnvironmentObject_(false),
|
needsBodyEnvironmentObject_(false),
|
||||||
funNeedsSomeEnvironmentObject_(false) {
|
funNeedsSomeEnvironmentObject_(false) {
|
||||||
|
|
@ -480,6 +482,7 @@ class CompileInfo {
|
||||||
bool hadOverflowBailout() const { return hadOverflowBailout_; }
|
bool hadOverflowBailout() const { return hadOverflowBailout_; }
|
||||||
bool hadFrequentBailouts() const { return hadFrequentBailouts_; }
|
bool hadFrequentBailouts() const { return hadFrequentBailouts_; }
|
||||||
bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; }
|
bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; }
|
||||||
|
bool trackRecordReplayProgress() const { return trackRecordReplayProgress_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned nimplicit_;
|
unsigned nimplicit_;
|
||||||
|
|
@ -504,6 +507,7 @@ class CompileInfo {
|
||||||
bool hadFrequentBailouts_;
|
bool hadFrequentBailouts_;
|
||||||
|
|
||||||
bool mayReadFrameArgsDirectly_;
|
bool mayReadFrameArgsDirectly_;
|
||||||
|
bool trackRecordReplayProgress_;
|
||||||
|
|
||||||
InlineScriptTree* inlineScriptTree_;
|
InlineScriptTree* inlineScriptTree_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1115,6 +1115,14 @@ AbortReasonOr<Ok> IonBuilder::buildInline(IonBuilder* callerBuilder,
|
||||||
|
|
||||||
insertRecompileCheck(pc);
|
insertRecompileCheck(pc);
|
||||||
|
|
||||||
|
// Insert an interrupt check when recording or replaying, which will bump
|
||||||
|
// the record/replay system's progress counter.
|
||||||
|
if (script()->trackRecordReplayProgress()) {
|
||||||
|
MInterruptCheck* check = MInterruptCheck::New(alloc());
|
||||||
|
check->setTrackRecordReplayProgress();
|
||||||
|
current->add(check);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the env chain now that all resume points operands are
|
// Initialize the env chain now that all resume points operands are
|
||||||
// initialized.
|
// initialized.
|
||||||
MOZ_TRY(initEnvironmentChain(callInfo.fun()));
|
MOZ_TRY(initEnvironmentChain(callInfo.fun()));
|
||||||
|
|
@ -1813,6 +1821,14 @@ AbortReasonOr<Ok> IonBuilder::emitLoopHeadInstructions(jsbytecode* pc) {
|
||||||
current->add(check);
|
current->add(check);
|
||||||
insertRecompileCheck(pc);
|
insertRecompileCheck(pc);
|
||||||
|
|
||||||
|
if (script()->trackRecordReplayProgress()) {
|
||||||
|
check->setTrackRecordReplayProgress();
|
||||||
|
|
||||||
|
// When recording/replaying, MInterruptCheck is effectful and should
|
||||||
|
// not reexecute after bailing out.
|
||||||
|
MOZ_TRY(resumeAfter(check));
|
||||||
|
}
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6148,6 +6164,14 @@ AbortReasonOr<bool> IonBuilder::testShouldDOMCall(TypeSet* inTypes,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some DOM optimizations cause execution to skip over recorded events such
|
||||||
|
// as wrapper cache accesses, e.g. through GVN or loop hoisting of the
|
||||||
|
// expression which performs the event. Disable DOM optimizations when
|
||||||
|
// recording or replaying to avoid this problem.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If all the DOM objects flowing through are legal with this
|
// If all the DOM objects flowing through are legal with this
|
||||||
// property, we can bake in a call to the bottom half of the DOM
|
// property, we can bake in a call to the bottom half of the DOM
|
||||||
// accessor
|
// accessor
|
||||||
|
|
@ -6483,10 +6507,13 @@ AbortReasonOr<Ok> IonBuilder::jsop_eval(uint32_t argc) {
|
||||||
|
|
||||||
// Try to pattern match 'eval(v + "()")'. In this case v is likely a
|
// Try to pattern match 'eval(v + "()")'. In this case v is likely a
|
||||||
// name on the env chain and the eval is performing a call on that
|
// name on the env chain and the eval is performing a call on that
|
||||||
// value. Use an env chain lookup rather than a full eval.
|
// value. Use an env chain lookup rather than a full eval. Avoid this
|
||||||
|
// optimization if we're tracking script progress, as this will not
|
||||||
|
// execute the script and give an inconsistent progress count.
|
||||||
if (string->isConcat() &&
|
if (string->isConcat() &&
|
||||||
string->getOperand(1)->type() == MIRType::String &&
|
string->getOperand(1)->type() == MIRType::String &&
|
||||||
string->getOperand(1)->maybeConstantValue()) {
|
string->getOperand(1)->maybeConstantValue() &&
|
||||||
|
!script()->trackRecordReplayProgress()) {
|
||||||
JSAtom* atom =
|
JSAtom* atom =
|
||||||
&string->getOperand(1)->maybeConstantValue()->toString()->asAtom();
|
&string->getOperand(1)->maybeConstantValue()->toString()->asAtom();
|
||||||
|
|
||||||
|
|
@ -8232,6 +8259,10 @@ AbortReasonOr<Ok> IonBuilder::loadStaticSlot(JSObject* staticObject,
|
||||||
// Whether a write of the given value may need a post-write barrier for GC
|
// Whether a write of the given value may need a post-write barrier for GC
|
||||||
// purposes.
|
// purposes.
|
||||||
bool IonBuilder::needsPostBarrier(MDefinition* value) {
|
bool IonBuilder::needsPostBarrier(MDefinition* value) {
|
||||||
|
// Generational GC is disabled for WebReplay.
|
||||||
|
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
CompileZone* zone = realm->zone();
|
CompileZone* zone = realm->zone();
|
||||||
if (value->mightBeType(MIRType::Object)) {
|
if (value->mightBeType(MIRType::Object)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,8 @@ class JitRuntime {
|
||||||
// Number of Ion compilations which were finished off thread and are
|
// Number of Ion compilations which were finished off thread and are
|
||||||
// waiting to be lazily linked. This is only set while holding the helper
|
// waiting to be lazily linked. This is only set while holding the helper
|
||||||
// thread state lock, but may be read from at other times.
|
// thread state lock, but may be read from at other times.
|
||||||
typedef mozilla::Atomic<size_t, mozilla::SequentiallyConsistent>
|
typedef mozilla::Atomic<size_t, mozilla::SequentiallyConsistent,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
NumFinishedOffThreadTasksType;
|
NumFinishedOffThreadTasksType;
|
||||||
NumFinishedOffThreadTasksType numFinishedOffThreadTasks_;
|
NumFinishedOffThreadTasksType numFinishedOffThreadTasks_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,9 @@ class alignas(uintptr_t) JitScript final {
|
||||||
// Number of times the script has been called or has had backedges taken.
|
// Number of times the script has been called or has had backedges taken.
|
||||||
// Reset if the script's JIT code is forcibly discarded. See also the
|
// Reset if the script's JIT code is forcibly discarded. See also the
|
||||||
// ScriptWarmUpData class.
|
// ScriptWarmUpData class.
|
||||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> warmUpCount_ = {};
|
mozilla::Atomic<uint32_t, mozilla::Relaxed,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
warmUpCount_ = {};
|
||||||
|
|
||||||
// Offset of the StackTypeSet array.
|
// Offset of the StackTypeSet array.
|
||||||
uint32_t typeSetOffset_ = 0;
|
uint32_t typeSetOffset_ = 0;
|
||||||
|
|
|
||||||
|
|
@ -6294,13 +6294,21 @@ class MCheckOverRecursed : public MNullaryInstruction {
|
||||||
|
|
||||||
// Check whether we need to fire the interrupt handler.
|
// Check whether we need to fire the interrupt handler.
|
||||||
class MInterruptCheck : public MNullaryInstruction {
|
class MInterruptCheck : public MNullaryInstruction {
|
||||||
MInterruptCheck() : MNullaryInstruction(classOpcode) { setGuard(); }
|
bool trackRecordReplayProgress_;
|
||||||
|
|
||||||
|
MInterruptCheck()
|
||||||
|
: MNullaryInstruction(classOpcode), trackRecordReplayProgress_(false) {
|
||||||
|
setGuard();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
INSTRUCTION_HEADER(InterruptCheck)
|
INSTRUCTION_HEADER(InterruptCheck)
|
||||||
TRIVIAL_NEW_WRAPPERS
|
TRIVIAL_NEW_WRAPPERS
|
||||||
|
|
||||||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||||
|
|
||||||
|
bool trackRecordReplayProgress() const { return trackRecordReplayProgress_; }
|
||||||
|
void setTrackRecordReplayProgress() { trackRecordReplayProgress_ = true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check whether we need to fire the interrupt handler (in wasm code).
|
// Check whether we need to fire the interrupt handler (in wasm code).
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,9 @@ class MIRGenerator final {
|
||||||
TempAllocator* alloc_;
|
TempAllocator* alloc_;
|
||||||
MIRGraph* graph_;
|
MIRGraph* graph_;
|
||||||
AbortReasonOr<Ok> offThreadStatus_;
|
AbortReasonOr<Ok> offThreadStatus_;
|
||||||
mozilla::Atomic<bool, mozilla::Relaxed> cancelBuild_;
|
mozilla::Atomic<bool, mozilla::Relaxed,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
cancelBuild_;
|
||||||
|
|
||||||
uint32_t wasmMaxStackArgBytes_;
|
uint32_t wasmMaxStackArgBytes_;
|
||||||
bool needsOverrecursedCheck_;
|
bool needsOverrecursedCheck_;
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,9 @@ class ProcessExecutableMemory {
|
||||||
|
|
||||||
// pagesAllocated_ is an Atomic so that bytesAllocated does not have to
|
// pagesAllocated_ is an Atomic so that bytesAllocated does not have to
|
||||||
// take the lock.
|
// take the lock.
|
||||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire> pagesAllocated_;
|
mozilla::Atomic<size_t, mozilla::ReleaseAcquire,
|
||||||
|
mozilla::recordreplay::Behavior::DontPreserve>
|
||||||
|
pagesAllocated_;
|
||||||
|
|
||||||
// Page where we should try to allocate next.
|
// Page where we should try to allocate next.
|
||||||
size_t cursor_;
|
size_t cursor_;
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,11 @@ bool CodeGeneratorShared::generatePrologue() {
|
||||||
masm.profilerEnterFrame(masm.getStackPointer(), CallTempReg0);
|
masm.profilerEnterFrame(masm.getStackPointer(), CallTempReg0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gen->outerInfo().trackRecordReplayProgress()) {
|
||||||
|
masm.inc64(
|
||||||
|
AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that the Ion frame is properly aligned.
|
// Ensure that the Ion frame is properly aligned.
|
||||||
masm.assertStackAlignment(JitStackAlignment, 0);
|
masm.assertStackAlignment(JitStackAlignment, 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2701,6 +2701,14 @@ namespace JS {
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API JSObject* ExceptionStackOrNull(JS::HandleObject obj);
|
extern JS_PUBLIC_API JSObject* ExceptionStackOrNull(JS::HandleObject obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this process is recording or replaying and the given value is an
|
||||||
|
* exception object (or an unwrappable cross-compartment wrapper for one),
|
||||||
|
* return the point where this exception was thrown, for time warping later.
|
||||||
|
* Returns zero otherwise.
|
||||||
|
*/
|
||||||
|
extern JS_PUBLIC_API uint64_t ExceptionTimeWarpTarget(JS::HandleValue exn);
|
||||||
|
|
||||||
} /* namespace JS */
|
} /* namespace JS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue