зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset d407a28318e6 (bug 1609815) for causing windows ming bustages CLOSED TREE
--HG-- extra : histedit_source : b2c748e31e0f6ba8fcf9960a336e0bbd361b07e6
This commit is contained in:
Родитель
236d721acb
Коммит
00dd87f6f4
|
@ -116,10 +116,15 @@ Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc)
|
|||
mHideEventTarget(false) {
|
||||
mBits.groupInfo = nullptr;
|
||||
mInt.mIndexOfEmbeddedChild = -1;
|
||||
|
||||
// Assign an ID to this Accessible for use in UniqueID().
|
||||
recordreplay::RegisterThing(this);
|
||||
}
|
||||
|
||||
Accessible::~Accessible() {
|
||||
NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
|
||||
|
||||
recordreplay::UnregisterThing(this);
|
||||
}
|
||||
|
||||
ENameValueFlag Accessible::Name(nsString& aName) const {
|
||||
|
|
|
@ -172,7 +172,15 @@ class Accessible : public nsISupports {
|
|||
/**
|
||||
* 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.
|
||||
|
|
|
@ -125,7 +125,7 @@ async function testToggleToolboxButtons() {
|
|||
];
|
||||
|
||||
// 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 toolbarButtons = toolbox.toolbarButtons.filter(tool =>
|
||||
tool.isTargetSupported(target)
|
||||
|
|
|
@ -20,6 +20,7 @@ const {
|
|||
getCSSStyleRules,
|
||||
} = require("devtools/shared/inspector/css-logic");
|
||||
const InspectorUtils = require("InspectorUtils");
|
||||
const Debugger = require("Debugger");
|
||||
|
||||
// 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
|
||||
|
@ -501,6 +502,13 @@ var TestActor = (exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
|
|||
* Get the window which mouse events on node should be delivered to.
|
||||
*/
|
||||
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;
|
||||
},
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace mozilla {
|
|||
|
||||
NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver);
|
||||
|
||||
StaticMutex TimelineConsumers::sMutex;
|
||||
StaticMutexNotRecorded TimelineConsumers::sMutex;
|
||||
|
||||
// Manually manage this singleton's lifetime and destroy it before shutdown.
|
||||
// This avoids the leakchecker detecting false-positive memory leaks when
|
||||
|
|
|
@ -122,7 +122,7 @@ class TimelineConsumers : public nsIObserver {
|
|||
LinkedList<MarkersStorage> mMarkersStores;
|
||||
|
||||
// Protects this class's data structures.
|
||||
static StaticMutex sMutex;
|
||||
static StaticMutexNotRecorded sMutex;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -107,6 +107,11 @@ TabGroup* TabGroup::GetFromWindow(mozIDOMWindowProxy* aWindow) {
|
|||
TabGroup* TabGroup::GetFromActor(BrowserChild* aBrowserChild) {
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Middleman processes do not assign event targets to their tab children.
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
return GetChromeTabGroup();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target =
|
||||
aBrowserChild->Manager()->GetEventTargetFor(aBrowserChild);
|
||||
if (!target) {
|
||||
|
|
|
@ -249,6 +249,7 @@
|
|||
#include "mozilla/HangAnnotations.h"
|
||||
#include "mozilla/Encoding.h"
|
||||
#include "nsXULElement.h"
|
||||
#include "mozilla/RecordReplay.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsIBidiKeyboard.h"
|
||||
#include "ReferrerInfo.h"
|
||||
|
@ -9904,6 +9905,12 @@ uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
|
|||
MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "mozilla/dom/ipc/StructuredCloneData.h"
|
||||
#include "mozilla/dom/DOMStringList.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
@ -593,11 +594,35 @@ class MMListenerRemover {
|
|||
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(
|
||||
nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed,
|
||||
const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData,
|
||||
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
|
||||
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);
|
||||
|
||||
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
||||
|
|
|
@ -530,7 +530,8 @@ class ScriptErrorEvent : public Runnable {
|
|||
JS::Rooted<JSObject*> stackGlobal(rootingCx);
|
||||
xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack, &stack,
|
||||
&stackGlobal);
|
||||
mReport->LogToConsoleWithStack(stack, stackGlobal);
|
||||
mReport->LogToConsoleWithStack(stack, stackGlobal,
|
||||
JS::ExceptionTimeWarpTarget(mError));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -38,6 +38,11 @@ void nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper) {
|
|||
if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
|
||||
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) {
|
||||
|
|
|
@ -4052,6 +4052,13 @@ void DeprecationWarning(JSContext* aCx, JSObject* aObject,
|
|||
void DeprecationWarning(const GlobalObject& aGlobal,
|
||||
Document::DeprecatedOperations aOperation) {
|
||||
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 =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (window && window->GetExtantDoc()) {
|
||||
|
|
|
@ -2653,6 +2653,11 @@ class MOZ_STACK_CLASS BindingJSObjectCreator {
|
|||
void InitializationSucceeded() {
|
||||
T* 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,12 @@ interface nsIScriptError : nsIConsoleMessage
|
|||
|
||||
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
|
||||
* CSS selectors of the CSS ruleset where the error occured.
|
||||
|
|
|
@ -41,6 +41,7 @@ nsScriptErrorBase::nsScriptErrorBase()
|
|||
mOuterWindowID(0),
|
||||
mInnerWindowID(0),
|
||||
mTimeStamp(0),
|
||||
mTimeWarpTarget(0),
|
||||
mInitializedOnMainThread(false),
|
||||
mIsFromPrivateWindow(false),
|
||||
mIsFromChromeContext(false) {}
|
||||
|
@ -385,6 +386,18 @@ nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) {
|
|||
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
|
||||
nsScriptErrorBase::GetIsFromChromeContext(bool* aIsFromChromeContext) {
|
||||
NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
|
||||
|
|
|
@ -83,6 +83,7 @@ class nsScriptErrorBase : public nsIScriptError {
|
|||
uint64_t mOuterWindowID;
|
||||
uint64_t mInnerWindowID;
|
||||
int64_t mTimeStamp;
|
||||
uint64_t mTimeWarpTarget;
|
||||
// mInitializedOnMainThread, mIsFromPrivateWindow and mIsFromChromeContext are
|
||||
// set on the main thread from InitializeOnMainThread().
|
||||
mozilla::Atomic<bool> mInitializedOnMainThread;
|
||||
|
|
|
@ -321,6 +321,17 @@ bool WebGLContext::CreateAndInitGL(
|
|||
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:
|
||||
if (IsWebGL2() && !forceEnabled) {
|
||||
const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
||||
|
|
|
@ -1179,6 +1179,13 @@ HTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
|||
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
|
||||
// InitializeDecoderForChannel. So make sure mElement is cleared here.
|
||||
RefPtr<HTMLMediaElement> element;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "mozilla/Decimal.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsIConstraintValidation.h"
|
||||
#include "nsString.h"
|
||||
|
|
|
@ -95,6 +95,12 @@ interface nsIRemoteTab : nsISupports
|
|||
*/
|
||||
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 {
|
||||
NAVIGATE_BACK = 0,
|
||||
NAVIGATE_FORWARD = 1,
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
#include "mozilla/layers/ShadowLayers.h"
|
||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||
#include "mozilla/plugins/PPluginWidgetChild.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "nsBrowserStatusFilter.h"
|
||||
#include "nsColorPickerProxy.h"
|
||||
#include "nsCommandParams.h"
|
||||
|
@ -290,7 +291,11 @@ class BrowserChild::DelayedDeleteRunnable final : public Runnable,
|
|||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -584,6 +589,11 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
|
|||
|
||||
mIPCOpen = true;
|
||||
|
||||
// Recording/replaying processes use their own compositor.
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
mPuppetWidget->CreateCompositor();
|
||||
}
|
||||
|
||||
#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_THUNDERBIRD) && \
|
||||
!defined(MOZ_SUITE)
|
||||
mSessionStoreListener = new TabListener(docShell, nullptr);
|
||||
|
@ -1175,6 +1185,13 @@ mozilla::ipc::IPCResult BrowserChild::RecvShow(
|
|||
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();
|
||||
|
||||
return IPC_OK();
|
||||
|
@ -1222,7 +1239,9 @@ mozilla::ipc::IPCResult BrowserChild::RecvCompositorOptionsChanged(
|
|||
|
||||
mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions(
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -2217,6 +2236,20 @@ mozilla::ipc::IPCResult BrowserChild::RecvActivateFrameEvent(
|
|||
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(
|
||||
const nsString& aURL, const bool& aRunInGlobalScope) {
|
||||
if (!InitBrowserChildMessageManager())
|
||||
|
@ -2231,6 +2264,11 @@ mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript(
|
|||
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);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -2806,7 +2844,10 @@ void BrowserChild::InitAPZState() {
|
|||
|
||||
void BrowserChild::NotifyPainted() {
|
||||
if (!mNotified) {
|
||||
SendNotifyCompositorTransaction();
|
||||
// Recording/replaying processes have a compositor but not a remote frame.
|
||||
if (!recordreplay::IsRecordingOrReplaying()) {
|
||||
SendNotifyCompositorTransaction();
|
||||
}
|
||||
mNotified = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,6 +292,20 @@ BrowserHost::StopApzAutoscroll(nsViewID aScrollId, uint32_t aPresShellId) {
|
|||
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
|
||||
BrowserHost::MaybeCancelContentJSExecutionFromScript(
|
||||
nsIRemoteTab::NavigationType aNavigationType,
|
||||
|
|
|
@ -223,7 +223,8 @@ BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId,
|
|||
mHasPresented(false),
|
||||
mIsReadyToHandleInputEvents(false),
|
||||
mIsMouseEnterIntoWidgetEventSuppressed(false),
|
||||
mIsDestroyingForProcessSwitch(false) {
|
||||
mIsDestroyingForProcessSwitch(false),
|
||||
mIsActiveRecordReplayTab(false) {
|
||||
MOZ_ASSERT(aManager);
|
||||
// When the input event queue is disabled, we don't need to handle the case
|
||||
// that some input events are dispatched before PBrowserConstructor.
|
||||
|
@ -645,6 +646,8 @@ void BrowserParent::DestroyInternal() {
|
|||
->ParentDestroy();
|
||||
}
|
||||
#endif
|
||||
|
||||
SetIsActiveRecordReplayTab(false);
|
||||
}
|
||||
|
||||
void BrowserParent::Destroy() {
|
||||
|
@ -3340,6 +3343,11 @@ void BrowserParent::SetDocShellIsActive(bool isActive) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Keep track of how many active recording/replaying tabs there are.
|
||||
if (Manager()->IsRecordingOrReplaying()) {
|
||||
SetIsActiveRecordReplayTab(isActive);
|
||||
}
|
||||
}
|
||||
|
||||
bool BrowserParent::GetHasPresented() { return mHasPresented; }
|
||||
|
@ -3921,6 +3929,16 @@ void BrowserParent::SetBrowserHost(BrowserHost* 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(
|
||||
const nsCString& aFontName) {
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
|
|
|
@ -133,6 +133,10 @@ class BrowserParent final : public PBrowserParent,
|
|||
|
||||
static TabId GetTabIdFrom(nsIDocShell* docshell);
|
||||
|
||||
static bool AreRecordReplayTabsActive() {
|
||||
return gNumActiveRecordReplayTabs != 0;
|
||||
}
|
||||
|
||||
const TabId GetTabId() const { return mTabId; }
|
||||
|
||||
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
|
||||
// should ignore nsIWebProgressListener stop requests.
|
||||
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 {
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
#include "mozilla/PerformanceUtils.h"
|
||||
#include "mozilla/plugins/PluginInstanceParent.h"
|
||||
#include "mozilla/plugins/PluginModuleParent.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "mozilla/widget/ScreenManager.h"
|
||||
#include "mozilla/widget/WidgetMessageUtils.h"
|
||||
#include "nsBaseDragService.h"
|
||||
|
@ -687,6 +688,17 @@ bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
|
|||
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)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1367,8 +1379,12 @@ void ContentChild::InitXPCOM(
|
|||
RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(),
|
||||
aXPCOMInit.haveBidiKeyboards());
|
||||
|
||||
// Create the CPOW manager as soon as possible.
|
||||
SendPJavaScriptConstructor();
|
||||
// Create the CPOW manager as soon as possible. Middleman processes don't use
|
||||
// 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()) {
|
||||
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 (Preferences::GetBool("security.sandbox.content.mac.earlyinit")) {
|
||||
if (Preferences::GetBool("security.sandbox.content.mac.earlyinit") &&
|
||||
!recordreplay::IsRecordingOrReplaying()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1857,7 +1874,11 @@ static void FirstIdle(void) {
|
|||
MOZ_ASSERT(gFirstIdleTask);
|
||||
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() {
|
||||
|
@ -2078,6 +2099,9 @@ jsipc::CPOWManager* ContentChild::GetCPOWManager() {
|
|||
LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
|
||||
return CPOWManagerFor(c);
|
||||
}
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
return nullptr;
|
||||
}
|
||||
return CPOWManagerFor(SendPJavaScriptConstructor());
|
||||
}
|
||||
|
||||
|
@ -3618,6 +3642,12 @@ mozilla::ipc::IPCResult ContentChild::RecvAddDynamicScalars(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvSaveRecording(
|
||||
const FileDescriptor& aFile) {
|
||||
recordreplay::parent::SaveRecording(aFile);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect(
|
||||
RedirectToRealChannelArgs&& aArgs,
|
||||
CrossProcessRedirectResolver&& aResolve) {
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
#include "mozilla/net/PCookieServiceParent.h"
|
||||
#include "mozilla/plugins/PluginBridge.h"
|
||||
#include "mozilla/psm/PSMContentListener.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "mozilla/widget/ScreenManager.h"
|
||||
#include "nsAnonymousTemporaryFile.h"
|
||||
#include "nsAppRunner.h"
|
||||
|
@ -643,7 +644,9 @@ bool ContentParent::sEarlySandboxInit = false;
|
|||
/*static*/ RefPtr<ContentParent::LaunchPromise>
|
||||
ContentParent::PreallocateProcess() {
|
||||
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);
|
||||
}
|
||||
|
@ -824,6 +827,44 @@ already_AddRefed<ContentParent> ContentParent::MinTabSelect(
|
|||
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*/
|
||||
already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
||||
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
|
||||
// ready, it calls FinishShutdown() on us. Then we close the channel.
|
||||
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) {
|
||||
// Stop sending input events with input priority when shutting down.
|
||||
SetInputPriorityEventEnabled(false);
|
||||
|
@ -1754,6 +1810,14 @@ void ContentParent::ActorDestroy(ActorDestroyReason why) {
|
|||
[subprocess = mSubprocess] { subprocess->Destroy(); }));
|
||||
mSubprocess = nullptr;
|
||||
|
||||
// Delete any remaining replaying children.
|
||||
for (auto& replayingProcess : mReplayingChildren) {
|
||||
if (replayingProcess) {
|
||||
replayingProcess->Destroy();
|
||||
replayingProcess = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
cpm->RemoveContentProcess(this->ChildID());
|
||||
|
||||
|
@ -1823,6 +1887,12 @@ bool ContentParent::ShouldKeepProcessAlive() {
|
|||
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);
|
||||
if (!contentParents) {
|
||||
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() {
|
||||
if (PJavaScriptParent* p =
|
||||
LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
|
||||
|
@ -2107,8 +2255,10 @@ bool ContentParent::BeginSubprocessLaunch(bool aIsSync,
|
|||
}
|
||||
|
||||
#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();
|
||||
if (sandboxEnabled && sEarlySandboxInit) {
|
||||
if (sandboxEnabled && sEarlySandboxInit && !IsRecordingOrReplaying()) {
|
||||
AppendSandboxParams(extraArgs);
|
||||
}
|
||||
if (sandboxEnabled) {
|
||||
|
@ -2241,7 +2391,10 @@ RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
|
|||
}
|
||||
|
||||
ContentParent::ContentParent(ContentParent* aOpener,
|
||||
const nsAString& aRemoteType, int32_t aJSPluginID)
|
||||
const nsAString& aRemoteType,
|
||||
RecordReplayState aRecordReplayState,
|
||||
const nsAString& aRecordingFile,
|
||||
int32_t aJSPluginID)
|
||||
: mSelfRef(nullptr),
|
||||
mSubprocess(nullptr),
|
||||
mLaunchTS(TimeStamp::Now()),
|
||||
|
@ -2256,6 +2409,8 @@ ContentParent::ContentParent(ContentParent* aOpener,
|
|||
mNumDestroyingTabs(0),
|
||||
mLifecycleState(LifecycleState::LAUNCHING),
|
||||
mIsForBrowser(!mRemoteType.IsEmpty()),
|
||||
mRecordReplayState(aRecordReplayState),
|
||||
mRecordingFile(aRecordingFile),
|
||||
mCalledClose(false),
|
||||
mCalledKillHard(false),
|
||||
mCreatedPairedMinidumps(false),
|
||||
|
@ -5761,6 +5916,30 @@ void ContentParent::DeallocPSHistoryParent(PSHistoryParent* 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() {
|
||||
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
|
||||
pluginHost->ReloadPlugins();
|
||||
|
|
|
@ -314,6 +314,13 @@ class ContentParent final
|
|||
// Let managees query if it is safe to send messages.
|
||||
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 RecvLoadPlugin(
|
||||
|
@ -738,13 +745,20 @@ class ContentParent final
|
|||
nsIReferrerInfo* aReferrerInfo, bool aLoadUri,
|
||||
nsIContentSecurityPolicy* aCsp);
|
||||
|
||||
enum RecordReplayState { eNotRecordingOrReplaying, eRecording, eReplaying };
|
||||
|
||||
explicit ContentParent(int32_t aPluginID)
|
||||
: ContentParent(nullptr, EmptyString(), aPluginID) {}
|
||||
ContentParent(ContentParent* aOpener, const nsAString& aRemoteType)
|
||||
: ContentParent(aOpener, aRemoteType, nsFakePluginTag::NOT_JSPLUGIN) {}
|
||||
: ContentParent(nullptr, EmptyString(), eNotRecordingOrReplaying,
|
||||
EmptyString(), aPluginID) {}
|
||||
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,
|
||||
int32_t aPluginID);
|
||||
RecordReplayState aRecordReplayState,
|
||||
const nsAString& aRecordingFile, int32_t aPluginID);
|
||||
|
||||
// Launch the subprocess and associated initialization.
|
||||
// Returns false if the process fails to start.
|
||||
|
@ -1287,6 +1301,12 @@ class ContentParent final
|
|||
const bool& aMinimizeMemoryUsage,
|
||||
const Maybe<FileDescriptor>& aDMDFile) override;
|
||||
|
||||
nsresult SaveRecording(nsIFile* aFile, bool* aRetval);
|
||||
|
||||
bool IsRecordingOrReplaying() const {
|
||||
return mRecordReplayState != eNotRecordingOrReplaying;
|
||||
}
|
||||
|
||||
void OnBrowsingContextGroupSubscribe(BrowsingContextGroup* aGroup);
|
||||
void OnBrowsingContextGroupUnsubscribe(BrowsingContextGroup* aGroup);
|
||||
|
||||
|
@ -1300,6 +1320,12 @@ class ContentParent final
|
|||
static bool ShouldSyncPreference(const char16_t* aData);
|
||||
|
||||
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`.
|
||||
static already_AddRefed<ContentParent> GetUsedBrowserProcess(
|
||||
ContentParent* aOpener, const nsAString& aRemoteType,
|
||||
|
@ -1376,6 +1402,16 @@ class ContentParent final
|
|||
|
||||
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
|
||||
// channel.
|
||||
bool mCalledClose;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "ContentProcess.h"
|
||||
#include "base/shared_memory.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
||||
# include <stdlib.h>
|
||||
|
@ -175,6 +176,12 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
recordreplay::parent::InitializeMiddleman(aArgc, aArgv, ParentPid(),
|
||||
deserializer.GetPrefsHandle(),
|
||||
deserializer.GetPrefMapHandle());
|
||||
}
|
||||
|
||||
mContent.Init(IOThreadChild::message_loop(), ParentPid(), *parentBuildID,
|
||||
IOThreadChild::TakeChannel(), *childID, *isForBrowser);
|
||||
|
||||
|
@ -182,8 +189,11 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
|
|||
#if (defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
|
||||
mContent.SetProfileDir(profileDir);
|
||||
# if defined(DEBUG)
|
||||
// For WebReplay middleman processes, the sandbox is
|
||||
// started after receiving the SetProcessSandbox message.
|
||||
if (IsContentSandboxEnabled() &&
|
||||
Preferences::GetBool("security.sandbox.content.mac.earlyinit")) {
|
||||
Preferences::GetBool("security.sandbox.content.mac.earlyinit") &&
|
||||
!recordreplay::IsMiddleman()) {
|
||||
AssertMacSandboxEnabled();
|
||||
}
|
||||
# endif /* DEBUG */
|
||||
|
|
|
@ -831,6 +831,9 @@ child:
|
|||
*/
|
||||
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
|
||||
// new HttpChannelChild that will be connected to the parent channel
|
||||
// represented by registrarId.
|
||||
|
@ -877,6 +880,11 @@ child:
|
|||
parent:
|
||||
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 InitStreamFilter(uint64_t channelId, nsString addonId)
|
||||
|
|
|
@ -140,7 +140,11 @@ class HangMonitorChild : public PProcessHangMonitorChild,
|
|||
private:
|
||||
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;
|
||||
Monitor mMonitor;
|
||||
|
@ -173,7 +177,9 @@ class HangMonitorChild : public PProcessHangMonitorChild,
|
|||
Atomic<bool> mPaintWhileInterruptingJSActive;
|
||||
};
|
||||
|
||||
Atomic<HangMonitorChild*, SequentiallyConsistent> HangMonitorChild::sInstance;
|
||||
Atomic<HangMonitorChild*, SequentiallyConsistent,
|
||||
recordreplay::Behavior::DontPreserve>
|
||||
HangMonitorChild::sInstance;
|
||||
|
||||
/* Parent process objects */
|
||||
|
||||
|
@ -304,7 +310,9 @@ class HangMonitorParent : public PProcessHangMonitorParent,
|
|||
|
||||
HangMonitorChild::HangMonitorChild(ProcessHangMonitor* 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),
|
||||
mTerminateScript(false),
|
||||
mTerminateGlobal(false),
|
||||
|
@ -333,6 +341,13 @@ HangMonitorChild::~HangMonitorChild() {
|
|||
bool HangMonitorChild::InterruptCallback() {
|
||||
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
|
||||
// chrome script during layout and such, and it wouldn't be good to interrupt
|
||||
// painting code from there.
|
||||
|
|
|
@ -433,6 +433,10 @@ cubeb* GetCubebContextUnlocked() {
|
|||
PREF_CUBEB_FORCE_NULL_CONTEXT));
|
||||
return nullptr;
|
||||
}
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
// Media is not supported when recording or replaying. See bug 1304146.
|
||||
return nullptr;
|
||||
}
|
||||
if (sCubebState != CubebState::Uninitialized) {
|
||||
// If we have already passed the initialization point (below), just return
|
||||
// the current context, which may be null (e.g., after error or shutdown.)
|
||||
|
@ -605,7 +609,7 @@ void InitLibrary() {
|
|||
NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName));
|
||||
#endif
|
||||
#ifdef MOZ_CUBEB_REMOTING
|
||||
if (sCubebSandbox && XRE_IsContentProcess()) {
|
||||
if (sCubebSandbox && XRE_IsContentProcess() && !recordreplay::IsMiddleman()) {
|
||||
InitAudioIPCConnection();
|
||||
}
|
||||
#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
|
||||
// initialized to a fatal error by default.
|
||||
MediaResult result =
|
||||
|
|
|
@ -295,8 +295,8 @@ class SourceListener : public SupportsWeakPtr<SourceListener> {
|
|||
SourceListenerPromise;
|
||||
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SourceListener)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(
|
||||
SourceListener)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION_AND_RECORDING(
|
||||
SourceListener, recordreplay::Behavior::Preserve)
|
||||
|
||||
SourceListener();
|
||||
|
||||
|
|
|
@ -253,6 +253,13 @@ JSObject* AudioContext::WrapObject(JSContext* aCx,
|
|||
already_AddRefed<AudioContext> AudioContext::Constructor(
|
||||
const GlobalObject& aGlobal, const AudioContextOptions& aOptions,
|
||||
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 =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
|
@ -295,6 +302,13 @@ already_AddRefed<AudioContext> AudioContext::Constructor(
|
|||
already_AddRefed<AudioContext> AudioContext::Constructor(
|
||||
const GlobalObject& aGlobal, uint32_t aNumberOfChannels, uint32_t aLength,
|
||||
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 =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
|
|
|
@ -690,6 +690,12 @@ nsresult nsPluginHost::InstantiatePluginInstance(
|
|||
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();
|
||||
if (!instanceOwner) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
|
|
@ -542,6 +542,13 @@ bool InterruptCallback(JSContext* aCx) {
|
|||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
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.
|
||||
PROFILER_JS_INTERRUPT_CALLBACK();
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "RecordingTypes.h"
|
||||
|
|
|
@ -1341,6 +1341,13 @@ APZEventResult APZCTreeManager::ReceiveInputEvent(InputData& aEvent) {
|
|||
APZThreadUtils::AssertOnControllerThread();
|
||||
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
|
||||
AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
|
||||
|
||||
|
|
|
@ -472,7 +472,7 @@ CompositorBridgeChild* ClientLayerManager::GetRemoteRenderer() {
|
|||
}
|
||||
|
||||
CompositorBridgeChild* ClientLayerManager::GetCompositorBridgeChild() {
|
||||
if (!XRE_IsParentProcess()) {
|
||||
if (!XRE_IsParentProcess() && !recordreplay::IsRecordingOrReplaying()) {
|
||||
return CompositorBridgeChild::Get();
|
||||
}
|
||||
return GetRemoteRenderer();
|
||||
|
@ -495,6 +495,8 @@ void ClientLayerManager::DidComposite(TransactionId aTransactionId,
|
|||
const TimeStamp& aCompositeStart,
|
||||
const TimeStamp& aCompositeEnd) {
|
||||
if (!mWidget) {
|
||||
// When recording/replaying this manager may have already been destroyed.
|
||||
MOZ_ASSERT(recordreplay::IsRecordingOrReplaying());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ CompositorBridgeParent::CompositorBridgeParent(
|
|||
|
||||
void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
|
||||
const LayersId& aLayerTreeId) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mWidget = aWidget;
|
||||
|
@ -1021,7 +1021,11 @@ void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
|
|||
bool requestNextFrame =
|
||||
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();
|
||||
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
||||
// If we have visible windowed plugins then we need to wait for content (and
|
||||
|
|
|
@ -146,7 +146,7 @@ CompositorManagerChild::CreateWidgetCompositorBridge(
|
|||
already_AddRefed<CompositorBridgeChild>
|
||||
CompositorManagerChild::CreateSameProcessWidgetCompositorBridge(
|
||||
LayerManager* aLayerManager, uint32_t aNamespace) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
|
||||
return nullptr;
|
||||
|
|
|
@ -29,7 +29,7 @@ StaticAutoPtr<nsTArray<CompositorManagerParent*>>
|
|||
/* static */
|
||||
already_AddRefed<CompositorManagerParent>
|
||||
CompositorManagerParent::CreateSameProcess() {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
|
||||
|
@ -77,7 +77,7 @@ already_AddRefed<CompositorBridgeParent>
|
|||
CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
|
||||
CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
|
||||
bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// 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.
|
||||
mAsapScheduling =
|
||||
StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
|
||||
gfxPlatform::IsInLayoutAsapMode();
|
||||
gfxPlatform::IsInLayoutAsapMode() ||
|
||||
recordreplay::IsRecordingOrReplaying();
|
||||
}
|
||||
|
||||
CompositorVsyncScheduler::~CompositorVsyncScheduler() {
|
||||
|
|
|
@ -147,6 +147,12 @@ mozilla::ipc::IPCResult LayerTransactionParent::RecvPaintTime(
|
|||
|
||||
mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
|
||||
const TransactionInfo& aInfo) {
|
||||
auto guard = MakeScopeExit([&] {
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
recordreplay::child::NotifyPaintComplete();
|
||||
}
|
||||
});
|
||||
|
||||
AUTO_PROFILER_TRACING_MARKER("Paint", "LayerTransaction", GRAPHICS);
|
||||
AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS);
|
||||
PerfStats::AutoMetricRecording<PerfStats::Metric::LayerTransactions>
|
||||
|
@ -492,6 +498,11 @@ mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
|
|||
(TimeStamp::Now() - updateStart).ToMilliseconds());
|
||||
}
|
||||
|
||||
// Compose after every update when recording/replaying.
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
mCompositorBridge->ForceComposeToTarget(nullptr);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -712,6 +712,10 @@ bool ShadowLayerForwarder::EndTransaction(
|
|||
mShadowManager->SendRecordPaintTimes(mPaintTiming);
|
||||
}
|
||||
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
recordreplay::child::NotifyPaintStart();
|
||||
}
|
||||
|
||||
*aSent = true;
|
||||
mIsFirstPaint = false;
|
||||
mFocusTarget = FocusTarget();
|
||||
|
|
|
@ -48,44 +48,49 @@ struct DeleteOnMainThreadTask : public Runnable {
|
|||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION( \
|
||||
_class) \
|
||||
public: \
|
||||
NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
|
||||
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
|
||||
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
|
||||
nsrefcnt count = ++mRefCnt; \
|
||||
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
|
||||
return (nsrefcnt)count; \
|
||||
} \
|
||||
void DeleteToBeCalledOnMainThread() { \
|
||||
MOZ_ASSERT(NS_IsMainThread()); \
|
||||
NS_LOG_RELEASE(this, 0, #_class); \
|
||||
delete this; \
|
||||
} \
|
||||
NS_METHOD_(MozExternalRefCountType) Release(void) { \
|
||||
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
|
||||
nsrefcnt count = --mRefCnt; \
|
||||
if (count == 0) { \
|
||||
if (NS_IsMainThread()) { \
|
||||
DeleteToBeCalledOnMainThread(); \
|
||||
} else { \
|
||||
NS_DispatchToMainThread( \
|
||||
new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \
|
||||
} \
|
||||
} else { \
|
||||
NS_LOG_RELEASE(this, count, #_class); \
|
||||
} \
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
protected: \
|
||||
::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
|
||||
\
|
||||
private: \
|
||||
::mozilla::layers::HelperForMainThreadDestruction \
|
||||
mHelperForMainThreadDestruction; \
|
||||
\
|
||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION_AND_RECORDING( \
|
||||
_class, _recording) \
|
||||
public: \
|
||||
NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
|
||||
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
|
||||
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
|
||||
nsrefcnt count = ++mRefCnt; \
|
||||
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
|
||||
return (nsrefcnt)count; \
|
||||
} \
|
||||
void DeleteToBeCalledOnMainThread() { \
|
||||
MOZ_ASSERT(NS_IsMainThread()); \
|
||||
NS_LOG_RELEASE(this, 0, #_class); \
|
||||
delete this; \
|
||||
} \
|
||||
NS_METHOD_(MozExternalRefCountType) Release(void) { \
|
||||
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
|
||||
nsrefcnt count = --mRefCnt; \
|
||||
if (count == 0) { \
|
||||
if (NS_IsMainThread()) { \
|
||||
DeleteToBeCalledOnMainThread(); \
|
||||
} else { \
|
||||
NS_DispatchToMainThread( \
|
||||
new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \
|
||||
} \
|
||||
} else { \
|
||||
NS_LOG_RELEASE(this, count, #_class); \
|
||||
} \
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
protected: \
|
||||
::mozilla::ThreadSafeAutoRefCntWithRecording<_recording> mRefCnt; \
|
||||
\
|
||||
private: \
|
||||
::mozilla::layers::HelperForMainThreadDestruction \
|
||||
mHelperForMainThreadDestruction; \
|
||||
\
|
||||
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
|
||||
|
|
|
@ -891,7 +891,7 @@ void gfxPlatform::Init() {
|
|||
|
||||
gfxConfig::Init();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying()) {
|
||||
GPUProcessManager::Initialize();
|
||||
RDDProcessManager::Initialize();
|
||||
|
||||
|
@ -1392,12 +1392,12 @@ void gfxPlatform::InitLayersIPC() {
|
|||
sLayersIPCIsUp = true;
|
||||
|
||||
if (XRE_IsContentProcess()) {
|
||||
if (gfxVars::UseOMTP()) {
|
||||
if (gfxVars::UseOMTP() && !recordreplay::IsRecordingOrReplaying()) {
|
||||
layers::PaintThread::Start();
|
||||
}
|
||||
}
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying()) {
|
||||
if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS) && UseWebRender()) {
|
||||
wr::RenderThread::Start();
|
||||
image::ImageMemoryReporter::InitForWebRender();
|
||||
|
@ -1422,7 +1422,7 @@ void gfxPlatform::ShutdownLayersIPC() {
|
|||
layers::ImageBridgeChild::ShutDown();
|
||||
}
|
||||
|
||||
if (gfxVars::UseOMTP()) {
|
||||
if (gfxVars::UseOMTP() && !recordreplay::IsRecordingOrReplaying()) {
|
||||
layers::PaintThread::Shutdown();
|
||||
}
|
||||
} else if (XRE_IsParentProcess()) {
|
||||
|
@ -2707,6 +2707,11 @@ void gfxPlatform::InitCompositorAccelerationPrefs() {
|
|||
FeatureStatus::Blocked, "Acceleration blocked by headless mode",
|
||||
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*/
|
||||
|
@ -2854,6 +2859,12 @@ void gfxPlatform::InitWebRenderConfig() {
|
|||
// crash reports.
|
||||
ScopedGfxFeatureReporter reporter("WR", prefEnabled || envvarEnabled);
|
||||
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
|
||||
// later in this function. For other processes we still want to report
|
||||
// the state of the feature for crash reports.
|
||||
|
@ -3250,7 +3261,8 @@ bool gfxPlatform::IsInLayoutAsapMode() {
|
|||
|
||||
/* static */
|
||||
bool gfxPlatform::ForceSoftwareVsync() {
|
||||
return StaticPrefs::layout_frame_rate() > 0;
|
||||
return StaticPrefs::layout_frame_rate() > 0 ||
|
||||
recordreplay::IsRecordingOrReplaying();
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -3267,7 +3279,7 @@ int gfxPlatform::GetDefaultFrameRate() { return 60; }
|
|||
|
||||
/* static */
|
||||
void gfxPlatform::ReInitFrameRate() {
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying()) {
|
||||
RefPtr<VsyncSource> oldSource = gPlatform->mVsyncSource;
|
||||
|
||||
// Start a new one:
|
||||
|
|
|
@ -632,7 +632,8 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
|
|||
*/
|
||||
virtual mozilla::gfx::VsyncSource* GetHardwareVsync() {
|
||||
MOZ_ASSERT(mVsyncSource != nullptr);
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(XRE_IsParentProcess() ||
|
||||
mozilla::recordreplay::IsRecordingOrReplaying());
|
||||
return mVsyncSource;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,12 @@ bool LockScreenOrientation(const hal::ScreenOrientation& aOrientation) {
|
|||
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) {
|
||||
Hal()->SendEnableSensorNotifications(aSensor);
|
||||
|
|
|
@ -105,7 +105,7 @@ class IDecoderFrameRecycler {
|
|||
|
||||
class Decoder {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(Decoder)
|
||||
|
||||
explicit Decoder(RasterImage* aImage);
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ class IDecodingTask : public IResumable {
|
|||
*/
|
||||
class MetadataDecodingTask final : public IDecodingTask {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataDecodingTask, override)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(MetadataDecodingTask, override)
|
||||
|
||||
explicit MetadataDecodingTask(NotNull<Decoder*> aDecoder);
|
||||
|
||||
|
@ -97,7 +97,8 @@ class MetadataDecodingTask final : public IDecodingTask {
|
|||
*/
|
||||
class AnonymousDecodingTask final : public IDecodingTask {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecodingTask, override)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(AnonymousDecodingTask,
|
||||
override)
|
||||
|
||||
explicit AnonymousDecodingTask(NotNull<Decoder*> aDecoder, bool aResumable);
|
||||
|
||||
|
|
|
@ -104,7 +104,8 @@ class CostEntry {
|
|||
|
||||
bool operator<(const CostEntry& aOther) const {
|
||||
return mCost < aOther.mCost ||
|
||||
(mCost == aOther.mCost && mSurface < aOther.mSurface);
|
||||
(mCost == aOther.mCost &&
|
||||
recordreplay::RecordReplayValue(mSurface < aOther.mSurface));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -16,8 +16,13 @@ FileDescriptorSet::FileDescriptorSet() : consumed_descriptor_highwater_(0) {}
|
|||
FileDescriptorSet::~FileDescriptorSet() {
|
||||
if (consumed_descriptor_highwater_ == descriptors_.size()) return;
|
||||
|
||||
CHROMIUM_LOG(WARNING)
|
||||
<< "FileDescriptorSet destroyed with unconsumed descriptors";
|
||||
// Middleman processes copy FileDescriptorSets before forwarding them to
|
||||
// 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
|
||||
// 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.
|
||||
template <typename T>
|
||||
static void InitSingleton(T* aToplevelProtocol) {
|
||||
// The crash reporter is not enabled in recording/replaying processes.
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Shmem shmem;
|
||||
if (!AllocShmem(aToplevelProtocol, &shmem)) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(false, "failed to allocate crash reporter shmem");
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "CrashReporterHost.h"
|
||||
#include "CrashReporterMetadataShmem.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -100,6 +101,11 @@ bool CrashReporterHost::AdoptMinidump(nsIFile* aFile,
|
|||
}
|
||||
|
||||
int32_t CrashReporterHost::GetCrashType() {
|
||||
if (mExtraAnnotations[CrashReporter::Annotation::RecordReplayHang]
|
||||
.EqualsLiteral("1")) {
|
||||
return nsICrashService::CRASH_TYPE_HANG;
|
||||
}
|
||||
|
||||
if (mExtraAnnotations[CrashReporter::Annotation::PluginHang].EqualsLiteral(
|
||||
"1")) {
|
||||
return nsICrashService::CRASH_TYPE_HANG;
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
#include "mozilla/RecordReplay.h"
|
||||
#include "mozilla/RDDProcessHost.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
@ -142,9 +143,14 @@ class BaseProcessLauncher {
|
|||
SprintfLiteral(mPidString, "%d", base::GetCurrentProcId());
|
||||
|
||||
// Compute the serial event target we'll use for launching.
|
||||
nsCOMPtr<nsIEventTarget> threadOrPool = GetIPCLauncher();
|
||||
mLaunchThread = new TaskQueue(threadOrPool.forget());
|
||||
|
||||
if (mozilla::recordreplay::IsMiddleman()) {
|
||||
// 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()) {
|
||||
// "Current process directory" means the app dir, not the current
|
||||
// working dir or similar.
|
||||
|
|
|
@ -561,6 +561,18 @@ static void TryRegisterStrongMemoryReporter() {
|
|||
|
||||
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)
|
||||
: mName(aName),
|
||||
mListener(aListener),
|
||||
|
@ -610,6 +622,10 @@ MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener)
|
|||
|
||||
TryRegisterStrongMemoryReporter<PendingResponseReporter>();
|
||||
TryRegisterStrongMemoryReporter<ChannelCountReporter>();
|
||||
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
mNextSeqno = MiddlemanStartSeqno;
|
||||
}
|
||||
}
|
||||
|
||||
MessageChannel::~MessageChannel() {
|
||||
|
@ -2039,6 +2055,15 @@ void MessageChannel::MessageTask::Clear() {
|
|||
|
||||
NS_IMETHODIMP
|
||||
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()) {
|
||||
case Message::NORMAL_PRIORITY:
|
||||
*aPriority = PRIORITY_NORMAL;
|
||||
|
@ -2140,8 +2165,10 @@ void MessageChannel::DispatchSyncMessage(ActorLifecycleProxy* aProxy,
|
|||
|
||||
int nestedLevel = aMsg.nested_level();
|
||||
|
||||
MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED ||
|
||||
NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(
|
||||
nestedLevel == IPC::Message::NOT_NESTED || NS_IsMainThread() ||
|
||||
// Middleman processes forward sync messages on a non-main thread.
|
||||
recordreplay::IsMiddleman());
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
AutoScopedLabel autolabel("sync message %s", aMsg.name());
|
||||
#endif
|
||||
|
|
|
@ -322,6 +322,10 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver {
|
|||
*/
|
||||
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
|
||||
struct MOZ_STACK_CLASS SyncStackFrame {
|
||||
SyncStackFrame(MessageChannel* channel, bool interrupt);
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "mozilla/ipc/MessageChannel.h"
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
#include "mozilla/recordreplay/ChildIPC.h"
|
||||
#include "mozilla/recordreplay/ParentIPC.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
@ -575,6 +577,7 @@ IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
|
|||
mOtherPid(mozilla::ipc::kInvalidProcessId),
|
||||
mLastLocalId(0),
|
||||
mEventTargetMutex("ProtocolEventTargetMutex"),
|
||||
mMiddlemanChannelOverride(nullptr),
|
||||
mChannel(aName, this) {
|
||||
mToplevel = this;
|
||||
}
|
||||
|
@ -586,7 +589,15 @@ base::ProcessId IToplevelProtocol::OtherPid() const {
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -628,8 +639,12 @@ bool IToplevelProtocol::IsOnCxxStack() const {
|
|||
|
||||
int32_t IToplevelProtocol::NextId() {
|
||||
// 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;
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
tag |= 1 << 0;
|
||||
}
|
||||
if (GetSide() == ParentSide) {
|
||||
tag |= 1 << 1;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "mozilla/ipc/Shmem.h"
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
#include "mozilla/ipc/MessageLink.h"
|
||||
#include "mozilla/recordreplay/ChildIPC.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
@ -405,8 +406,18 @@ class IToplevelProtocol : public IProtocol {
|
|||
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
|
||||
bool DestroySharedMemory(Shmem& aShmem);
|
||||
|
||||
MessageChannel* GetIPCChannel() { return &mChannel; }
|
||||
const MessageChannel* GetIPCChannel() const { return &mChannel; }
|
||||
MessageChannel* GetIPCChannel() {
|
||||
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.
|
||||
void SetEventTargetForActorInternal(IProtocol* aActor,
|
||||
|
@ -509,6 +520,13 @@ class IToplevelProtocol : public IProtocol {
|
|||
|
||||
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:
|
||||
// Override this method in top-level protocols to change the event target
|
||||
// for a new actor (and its sub-actors).
|
||||
|
@ -543,6 +561,12 @@ class IToplevelProtocol : public IProtocol {
|
|||
Mutex mEventTargetMutex;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -722,7 +746,16 @@ class Endpoint {
|
|||
// be used to send and receive messages. The endpoint becomes invalid.
|
||||
bool Bind(PFooSide* aActor) {
|
||||
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 =
|
||||
mozilla::ipc::OpenDescriptor(mTransport, mMode);
|
||||
|
|
|
@ -977,6 +977,8 @@ description = test only
|
|||
description = test only
|
||||
[PContent::SyncMessage]
|
||||
description = JS MessageManager implementation
|
||||
[PContent::OpenRecordReplayChannel]
|
||||
description = bug 1475898 this could be async
|
||||
[PContent::LoadPlugin]
|
||||
description = Legacy NPAPI IPC
|
||||
[PContent::ConnectPluginBridge]
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
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]
|
||||
void addClass(in jsval global);
|
||||
|
|
|
@ -50,6 +50,21 @@ JSDebugger::AddClass(JS::Handle<JS::Value> global, JSContext* cx) {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -598,7 +598,13 @@ CrossProcessCpowHolder::~CrossProcessCpowHolder() {
|
|||
// the corresponding part of the CPOW in the other process
|
||||
// will eventually be collected. The scope for this object
|
||||
// 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;
|
||||
if (!jsapi.Init(xpc::PrivilegedJunkScope())) {
|
||||
return;
|
||||
|
@ -622,6 +628,9 @@ bool CrossProcessCpowHolder::ToObject(JSContext* cx,
|
|||
|
||||
bool JavaScriptShared::Unwrap(JSContext* cx, const nsTArray<CpowEntry>& aCpows,
|
||||
JS::MutableHandleObject objp) {
|
||||
// Middleman processes never operate on CPOWs.
|
||||
MOZ_ASSERT(!recordreplay::IsMiddleman());
|
||||
|
||||
objp.set(nullptr);
|
||||
|
||||
if (!aCpows.Length()) {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "mozilla/Utf8.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
|
|
@ -20,7 +20,9 @@ struct JSStructuredCloneWriter;
|
|||
|
||||
struct JSPrincipals {
|
||||
/* 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
|
||||
/* 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
|
||||
// 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
|
||||
// |label_| in profiler output. Need not be (and usually isn't) static. Can
|
||||
// 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.
|
||||
mozilla::Atomic<void*, mozilla::ReleaseAcquire> spOrScript;
|
||||
mozilla::Atomic<void*, mozilla::ReleaseAcquire,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
spOrScript;
|
||||
|
||||
// The bytecode offset for JS stack frames.
|
||||
// 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
|
||||
// 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.
|
||||
// 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
|
||||
// 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.
|
||||
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);
|
||||
|
||||
|
@ -481,7 +493,8 @@ class JS_FRIEND_API ProfilingStack final {
|
|||
// written from the current thread.
|
||||
//
|
||||
// This is effectively a unique pointer.
|
||||
mozilla::Atomic<js::ProfilingStackFrame*, mozilla::SequentiallyConsistent>
|
||||
mozilla::Atomic<js::ProfilingStackFrame*, mozilla::SequentiallyConsistent,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
frames{nullptr};
|
||||
|
||||
// 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.
|
||||
// See the "Concurrency considerations" paragraph at the top of this file
|
||||
// for more details.
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> stackPointer;
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
stackPointer;
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -764,7 +764,8 @@ void js::FutexThread::lock() {
|
|||
lock->lock();
|
||||
}
|
||||
|
||||
/* static */ mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent>
|
||||
/* static */ mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
FutexThread::lock_;
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -129,7 +129,9 @@ class FutexThread {
|
|||
// Shared futex lock for all runtimes. We can perhaps do better,
|
||||
// but any lock will need to be per-domain (consider SharedWorker)
|
||||
// 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.
|
||||
ThreadData<bool> canWait_;
|
||||
|
|
|
@ -390,6 +390,12 @@ static MOZ_ALWAYS_INLINE bool ShouldCaptureDebugInfo(JSContext* cx) {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -479,6 +485,7 @@ class PromiseDebugInfo : public NativeObject {
|
|||
if (!ShouldCaptureDebugInfo(cx)) {
|
||||
return;
|
||||
}
|
||||
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||
|
||||
// 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
|
||||
|
@ -2227,6 +2234,7 @@ CreatePromiseObjectInternal(JSContext* cx, HandleObject proto /* = nullptr */,
|
|||
if (MOZ_LIKELY(!ShouldCaptureDebugInfo(cx))) {
|
||||
return promise;
|
||||
}
|
||||
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||
|
||||
// Store an allocation stack so we can later figure out what the
|
||||
// control flow was for some unexpected results. Frightfully expensive,
|
||||
|
@ -5760,6 +5768,15 @@ JS::AutoDebuggerJobQueueInterruption::~AutoDebuggerJobQueueInterruption() {
|
|||
}
|
||||
|
||||
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);
|
||||
this->cx = cx;
|
||||
saved = cx->jobQueue->saveJobQueue(cx);
|
||||
|
@ -5767,8 +5784,10 @@ bool JS::AutoDebuggerJobQueueInterruption::init(JSContext* cx) {
|
|||
}
|
||||
|
||||
void JS::AutoDebuggerJobQueueInterruption::runJobs() {
|
||||
JS::AutoSaveExceptionState ases(cx);
|
||||
cx->jobQueue->runJobs(cx);
|
||||
if (!mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||
JS::AutoSaveExceptionState ases(cx);
|
||||
cx->jobQueue->runJobs(cx);
|
||||
}
|
||||
}
|
||||
|
||||
const JSJitInfo promise_then_info = {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "mozilla/GuardObjects.h" // for MOZ_GUARD_OBJECT_NOTIFIER_PARAM
|
||||
#include "mozilla/HashTable.h" // for HashSet<>::Range, HashMapEntry
|
||||
#include "mozilla/Maybe.h" // for Maybe, Nothing, Some
|
||||
#include "mozilla/RecordReplay.h" // for IsMiddleman
|
||||
#include "mozilla/ScopeExit.h" // for MakeScopeExit, ScopeExit
|
||||
#include "mozilla/ThreadLocal.h" // for ThreadLocal
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||
|
@ -5891,6 +5892,29 @@ bool Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp) {
|
|||
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() {
|
||||
if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1)) {
|
||||
return false;
|
||||
|
@ -6019,7 +6043,9 @@ const JSFunctionSpec Debugger::methods[] = {
|
|||
JS_FS_END};
|
||||
|
||||
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(
|
||||
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,
|
||||
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
|
||||
[source]: Debugger.Source.md
|
||||
[script]: Debugger.Script.md
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace js {
|
|||
|
||||
// Memory protection occurs at non-deterministic points when
|
||||
// recording/replaying.
|
||||
static mozilla::Atomic<bool, mozilla::SequentiallyConsistent>
|
||||
static mozilla::Atomic<bool, mozilla::SequentiallyConsistent,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
sProtectedRegionsInit(false);
|
||||
|
||||
/*
|
||||
|
|
|
@ -395,6 +395,12 @@ bool frontend::SourceAwareCompiler<Unit>::createSourceAndParser(
|
|||
if (!compilationInfo.assignSource(sourceBuffer_)) {
|
||||
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)) {
|
||||
syntaxParser.emplace(compilationInfo.cx, compilationInfo.options,
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
#include "mozilla/Span.h" // mozilla::Span
|
||||
#include "mozilla/Vector.h" // mozilla::Vector
|
||||
|
||||
#include <functional> // std::function
|
||||
#include <stddef.h> // ptrdiff_t
|
||||
#include <stdint.h> // uint16_t, uint32_t
|
||||
#include <stddef.h> // ptrdiff_t
|
||||
#include <stdint.h> // uint16_t, uint32_t
|
||||
|
||||
#include "jsapi.h" // CompletionKind
|
||||
|
||||
|
|
|
@ -262,7 +262,8 @@ class ArenaLists {
|
|||
};
|
||||
|
||||
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.
|
||||
UnprotectedData<AllAllocKindArray<ConcurrentUseState>> concurrentUseState_;
|
||||
|
|
|
@ -34,7 +34,9 @@ class AtomMarkingRuntime {
|
|||
public:
|
||||
// The extent of all allocated and free words in atom mark bitmaps.
|
||||
// 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) {}
|
||||
|
||||
|
|
|
@ -1705,7 +1705,8 @@ bool GCRuntime::shouldCompact() {
|
|||
|
||||
bool GCRuntime::isCompactingGCEnabled() const {
|
||||
return compactingEnabled &&
|
||||
rt->mainContextFromOwnThread()->compactingDisabledCount == 0;
|
||||
rt->mainContextFromOwnThread()->compactingDisabledCount == 0 &&
|
||||
!mozilla::recordreplay::IsRecordingOrReplaying();
|
||||
}
|
||||
|
||||
AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx) : cx(cx) {
|
||||
|
@ -2916,6 +2917,42 @@ void Nursery::requestMinorGC(JS::GCReason reason) const {
|
|||
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) {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
// GCs can only be triggered in certain ways when recording/replaying.
|
||||
if (!RecordReplayCheckCanGC(reason)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::PrepareForFullGC(rt->mainContextFromOwnThread());
|
||||
requestMajorGC(reason);
|
||||
return true;
|
||||
|
@ -3070,6 +3112,11 @@ bool GCRuntime::triggerZoneGC(Zone* zone, JS::GCReason reason, size_t used,
|
|||
return false;
|
||||
}
|
||||
|
||||
// GCs can only be triggered in certain ways when recording/replaying.
|
||||
if (!RecordReplayCheckCanGC(reason)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (hasZealMode(ZealMode::Alloc)) {
|
||||
MOZ_RELEASE_ASSERT(triggerGC(reason));
|
||||
|
@ -5414,6 +5461,10 @@ IncrementalProgress GCRuntime::markUntilBudgetExhausted(
|
|||
SliceBudget& sliceBudget) {
|
||||
// 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
|
||||
AutoSetThreadIsMarking threadIsMarking;
|
||||
#endif // DEBUG
|
||||
|
@ -6036,6 +6087,10 @@ bool GCRuntime::initSweepActions() {
|
|||
}
|
||||
|
||||
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);
|
||||
JSFreeOp fop(rt);
|
||||
|
||||
|
@ -7244,6 +7299,11 @@ SliceBudget GCRuntime::defaultBudget(JS::GCReason reason, int64_t millis) {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -8216,7 +8276,8 @@ JS_PUBLIC_API void JS::DisableIncrementalGC(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) {
|
||||
|
|
|
@ -70,7 +70,9 @@ class GCParallelTask : public mozilla::LinkedListElement<GCParallelTask>,
|
|||
|
||||
protected:
|
||||
// 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:
|
||||
explicit GCParallelTask(gc::GCRuntime* gc)
|
||||
|
|
|
@ -795,7 +795,9 @@ class GCRuntime {
|
|||
|
||||
private:
|
||||
// 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 JS::AutoEnterCycleCollection;
|
||||
|
||||
|
@ -841,12 +843,16 @@ class GCRuntime {
|
|||
MainThreadData<RootedValueMap> rootsHash;
|
||||
|
||||
// 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.
|
||||
*/
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
numArenasFreeCommitted;
|
||||
MainThreadData<VerifyPreTracer*> verifyPreData;
|
||||
|
||||
private:
|
||||
|
@ -860,7 +866,9 @@ class GCRuntime {
|
|||
*/
|
||||
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
|
||||
|
@ -898,7 +906,9 @@ class GCRuntime {
|
|||
*/
|
||||
UnprotectedData<bool> grayBitsValid;
|
||||
|
||||
mozilla::Atomic<JS::GCReason, mozilla::Relaxed> majorGCTriggerReason;
|
||||
mozilla::Atomic<JS::GCReason, mozilla::Relaxed,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
majorGCTriggerReason;
|
||||
|
||||
private:
|
||||
/* 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.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(
|
||||
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.
|
||||
*/
|
||||
#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)
|
||||
static mozilla::Atomic<int, mozilla::Relaxed> growthDirection(0);
|
||||
static mozilla::Atomic<int, mozilla::Relaxed,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
growthDirection(0);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -264,6 +268,7 @@ static inline uint64_t GetNumberInRange(uint64_t minNum, uint64_t maxNum) {
|
|||
do {
|
||||
mozilla::Maybe<uint64_t> result;
|
||||
do {
|
||||
mozilla::recordreplay::AutoPassThroughThreadEvents pt;
|
||||
result = mozilla::RandomUint64();
|
||||
} while (!result);
|
||||
rndNum = result.value() / binSize;
|
||||
|
@ -830,6 +835,9 @@ bool MarkPagesInUseHard(void* region, size_t length) {
|
|||
}
|
||||
|
||||
size_t GetPageFaultCount() {
|
||||
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)) == 0) {
|
||||
|
|
|
@ -245,6 +245,12 @@ js::Nursery::Nursery(GCRuntime* gc)
|
|||
}
|
||||
|
||||
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());
|
||||
if (!allocateNextChunk(0, lock)) {
|
||||
capacity_ = 0;
|
||||
|
@ -293,7 +299,7 @@ js::Nursery::~Nursery() { disable(); }
|
|||
void js::Nursery::enable() {
|
||||
MOZ_ASSERT(isEmpty());
|
||||
MOZ_ASSERT(!gc->isVerifyPreBarriersEnabled());
|
||||
if (isEnabled()) {
|
||||
if (isEnabled() || mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -958,6 +964,8 @@ void js::Nursery::collect(JS::GCReason reason) {
|
|||
JSRuntime* rt = runtime();
|
||||
MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
|
||||
|
||||
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
|
||||
|
||||
if (!isEnabled() || isEmpty()) {
|
||||
// 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
|
||||
|
|
|
@ -680,7 +680,9 @@ struct TriggerResult {
|
|||
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.
|
||||
|
|
|
@ -349,8 +349,10 @@ struct Statistics {
|
|||
PhaseTimeTable parallelTimes;
|
||||
|
||||
/* Number of events of this type for this GC. */
|
||||
EnumeratedArray<Count, COUNT_LIMIT,
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire>>
|
||||
EnumeratedArray<
|
||||
Count, COUNT_LIMIT,
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>>
|
||||
counts;
|
||||
|
||||
/* Other GC statistics. */
|
||||
|
|
|
@ -180,7 +180,8 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
|
|||
js::WriteOnceData<bool> isSystemZone_;
|
||||
|
||||
enum class HelperThreadUse : uint32_t { None, Pending, Active };
|
||||
mozilla::Atomic<HelperThreadUse, mozilla::SequentiallyConsistent>
|
||||
mozilla::Atomic<HelperThreadUse, mozilla::SequentiallyConsistent,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
helperThreadUse_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// 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.
|
||||
js::ZoneOrGCTaskData<mozilla::LinkedList<js::WeakMapBase>> gcWeakMapList_;
|
||||
|
|
|
@ -481,6 +481,9 @@ static jsbytecode* GetResumePC(JSScript* script, jsbytecode* pc,
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (skippedLoopHead && script->trackRecordReplayProgress()) {
|
||||
mozilla::recordreplay::AdvanceExecutionProgressCounter();
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
|
|
@ -2248,7 +2248,23 @@ bool BaselineCodeGen<Handler>::emit_LoopHead() {
|
|||
if (!emitWarmUpCounterIncrement()) {
|
||||
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>
|
||||
|
@ -6718,6 +6734,10 @@ bool BaselineCodeGen<Handler>::emitPrologue() {
|
|||
// chain is in R1. For function scripts, the env chain is in the callee.
|
||||
emitInitFrameFields(R1.scratchReg());
|
||||
|
||||
if (!emitIncExecutionProgressCounter(R2.scratchReg())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// When compiling with Debugger instrumentation, set the debuggeeness of
|
||||
// the frame before any operation that can call into the VM.
|
||||
if (!emitIsDebuggeeCheck()) {
|
||||
|
|
|
@ -174,6 +174,8 @@ class BaselineCodeGen {
|
|||
Register scratch);
|
||||
void emitJumpToInterpretOpLabel();
|
||||
|
||||
MOZ_MUST_USE bool emitIncExecutionProgressCounter(Register scratch);
|
||||
|
||||
MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit = false);
|
||||
void emitLoadReturnValue(ValueOperand val);
|
||||
void emitPushNonArrowFunctionNewTarget();
|
||||
|
|
|
@ -4801,6 +4801,11 @@ void CacheIRCompiler::emitPostBarrierShared(Register obj,
|
|||
Register maybeIndex) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
// Generational GC is disabled for WebReplay.
|
||||
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (val.constant()) {
|
||||
MOZ_ASSERT_IF(val.value().isGCThing(),
|
||||
!IsInsideNursery(val.value().toGCThing()));
|
||||
|
|
|
@ -13020,6 +13020,11 @@ void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) {
|
|||
OutOfLineCode* ool =
|
||||
oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
|
||||
|
||||
if (lir->mir()->trackRecordReplayProgress()) {
|
||||
masm.inc64(
|
||||
AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
|
||||
}
|
||||
|
||||
const void* interruptAddr = gen->runtime->addressOfInterruptBits();
|
||||
masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
|
||||
ool->entry());
|
||||
|
|
|
@ -170,6 +170,7 @@ class CompileInfo {
|
|||
hadOverflowBailout_(script->hadOverflowBailout()),
|
||||
hadFrequentBailouts_(script->hadFrequentBailouts()),
|
||||
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
||||
trackRecordReplayProgress_(script->trackRecordReplayProgress()),
|
||||
inlineScriptTree_(inlineScriptTree) {
|
||||
MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead);
|
||||
|
||||
|
@ -230,6 +231,7 @@ class CompileInfo {
|
|||
hadOverflowBailout_(false),
|
||||
hadFrequentBailouts_(false),
|
||||
mayReadFrameArgsDirectly_(false),
|
||||
trackRecordReplayProgress_(false),
|
||||
inlineScriptTree_(nullptr),
|
||||
needsBodyEnvironmentObject_(false),
|
||||
funNeedsSomeEnvironmentObject_(false) {
|
||||
|
@ -480,6 +482,7 @@ class CompileInfo {
|
|||
bool hadOverflowBailout() const { return hadOverflowBailout_; }
|
||||
bool hadFrequentBailouts() const { return hadFrequentBailouts_; }
|
||||
bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; }
|
||||
bool trackRecordReplayProgress() const { return trackRecordReplayProgress_; }
|
||||
|
||||
private:
|
||||
unsigned nimplicit_;
|
||||
|
@ -504,6 +507,7 @@ class CompileInfo {
|
|||
bool hadFrequentBailouts_;
|
||||
|
||||
bool mayReadFrameArgsDirectly_;
|
||||
bool trackRecordReplayProgress_;
|
||||
|
||||
InlineScriptTree* inlineScriptTree_;
|
||||
|
||||
|
|
|
@ -1115,6 +1115,14 @@ AbortReasonOr<Ok> IonBuilder::buildInline(IonBuilder* callerBuilder,
|
|||
|
||||
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
|
||||
// initialized.
|
||||
MOZ_TRY(initEnvironmentChain(callInfo.fun()));
|
||||
|
@ -1813,6 +1821,14 @@ AbortReasonOr<Ok> IonBuilder::emitLoopHeadInstructions(jsbytecode* pc) {
|
|||
current->add(check);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -6148,6 +6164,14 @@ AbortReasonOr<bool> IonBuilder::testShouldDOMCall(TypeSet* inTypes,
|
|||
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
|
||||
// property, we can bake in a call to the bottom half of the DOM
|
||||
// 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
|
||||
// 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() &&
|
||||
string->getOperand(1)->type() == MIRType::String &&
|
||||
string->getOperand(1)->maybeConstantValue()) {
|
||||
string->getOperand(1)->maybeConstantValue() &&
|
||||
!script()->trackRecordReplayProgress()) {
|
||||
JSAtom* atom =
|
||||
&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
|
||||
// purposes.
|
||||
bool IonBuilder::needsPostBarrier(MDefinition* value) {
|
||||
// Generational GC is disabled for WebReplay.
|
||||
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
|
||||
return false;
|
||||
}
|
||||
CompileZone* zone = realm->zone();
|
||||
if (value->mightBeType(MIRType::Object)) {
|
||||
return true;
|
||||
|
|
|
@ -227,7 +227,8 @@ class JitRuntime {
|
|||
// Number of Ion compilations which were finished off thread and are
|
||||
// waiting to be lazily linked. This is only set while holding the helper
|
||||
// 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 numFinishedOffThreadTasks_;
|
||||
|
||||
|
|
|
@ -181,7 +181,9 @@ class alignas(uintptr_t) JitScript final {
|
|||
// 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
|
||||
// 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.
|
||||
uint32_t typeSetOffset_ = 0;
|
||||
|
|
|
@ -6294,13 +6294,21 @@ class MCheckOverRecursed : public MNullaryInstruction {
|
|||
|
||||
// Check whether we need to fire the interrupt handler.
|
||||
class MInterruptCheck : public MNullaryInstruction {
|
||||
MInterruptCheck() : MNullaryInstruction(classOpcode) { setGuard(); }
|
||||
bool trackRecordReplayProgress_;
|
||||
|
||||
MInterruptCheck()
|
||||
: MNullaryInstruction(classOpcode), trackRecordReplayProgress_(false) {
|
||||
setGuard();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(InterruptCheck)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
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).
|
||||
|
|
|
@ -133,7 +133,9 @@ class MIRGenerator final {
|
|||
TempAllocator* alloc_;
|
||||
MIRGraph* graph_;
|
||||
AbortReasonOr<Ok> offThreadStatus_;
|
||||
mozilla::Atomic<bool, mozilla::Relaxed> cancelBuild_;
|
||||
mozilla::Atomic<bool, mozilla::Relaxed,
|
||||
mozilla::recordreplay::Behavior::DontPreserve>
|
||||
cancelBuild_;
|
||||
|
||||
uint32_t wasmMaxStackArgBytes_;
|
||||
bool needsOverrecursedCheck_;
|
||||
|
|
|
@ -510,7 +510,9 @@ class ProcessExecutableMemory {
|
|||
|
||||
// pagesAllocated_ is an Atomic so that bytesAllocated does not have to
|
||||
// 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.
|
||||
size_t cursor_;
|
||||
|
|
|
@ -116,6 +116,11 @@ bool CodeGeneratorShared::generatePrologue() {
|
|||
masm.profilerEnterFrame(masm.getStackPointer(), CallTempReg0);
|
||||
}
|
||||
|
||||
if (gen->outerInfo().trackRecordReplayProgress()) {
|
||||
masm.inc64(
|
||||
AbsoluteAddress(mozilla::recordreplay::ExecutionProgressCounter()));
|
||||
}
|
||||
|
||||
// Ensure that the Ion frame is properly aligned.
|
||||
masm.assertStackAlignment(JitStackAlignment, 0);
|
||||
|
||||
|
|
|
@ -2701,6 +2701,14 @@ namespace JS {
|
|||
*/
|
||||
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 */
|
||||
|
||||
/**
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче