Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Gurzau Raul 2018-07-24 12:52:06 +03:00
commit 19e302fb18
191 changed files with 5902 additions and 3085 deletions

37
Cargo.lock generated
View File

@ -140,6 +140,14 @@ dependencies = [
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bench-collections-gtest"
version = "0.1.0"
dependencies = [
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "binary-space-partition"
version = "0.1.2"
@ -800,7 +808,7 @@ dependencies = [
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"nsstring 0.1.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.19.0",
"servo_arc 0.1.1",
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -820,6 +828,7 @@ dependencies = [
name = "gkrust-gtest"
version = "0.1.0"
dependencies = [
"bench-collections-gtest 0.1.0",
"gkrust-shared 0.1.0",
"mp4parse-gtest 0.1.0",
"nsstring-gtest 0.1.0",
@ -954,11 +963,6 @@ dependencies = [
"either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.1"
@ -1110,6 +1114,15 @@ name = "linked-hash-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lock_api"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.9"
@ -1479,10 +1492,10 @@ dependencies = [
[[package]]
name = "parking_lot"
version = "0.5.4"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1950,7 +1963,7 @@ dependencies = [
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"hashglobe 0.1.0",
"itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1964,7 +1977,7 @@ dependencies = [
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2616,7 +2629,6 @@ dependencies = [
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
"checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21"
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum khronos_api 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "037ab472c33f67b5fbd3e9163a2645319e5356fcd355efa6d4eb7fff4bbcb554"
@ -2632,6 +2644,7 @@ dependencies = [
"checksum libudev 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
@ -2662,7 +2675,7 @@ dependencies = [
"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
"checksum parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b"
"checksum parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"

View File

@ -120,6 +120,11 @@ TabGroup::GetFromActor(TabChild* aTabChild)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
// Middleman processes do not assign event targets to their tab children.
if (recordreplay::IsMiddleman()) {
return GetChromeTabGroup();
}
nsCOMPtr<nsIEventTarget> target = aTabChild->Manager()->GetEventTargetFor(aTabChild);
if (!target) {
return nullptr;

View File

@ -471,6 +471,12 @@ mozilla::dom::TraceBlackJS(JSTracer* aTrc, bool aIsShutdownGC)
}
if (ProcessGlobal::WasCreated() && nsFrameMessageManager::GetChildProcessManager()) {
// ProcessGlobal::Get() can perform recorded events such as lock accesses,
// which we're not supposed to do here since tracing occurs
// non-deterministically when recording/replaying. Sidestep this by not
// recording these events.
recordreplay::AutoPassThroughThreadEvents pt;
ProcessGlobal* pg = ProcessGlobal::Get();
if (pg) {
mozilla::TraceScriptHolder(ToSupports(pg), aTrc);

View File

@ -6950,8 +6950,7 @@ nsContentUtils::FindInternalContentViewer(const nsACString& aType,
nsCString contractID;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
PromiseFlatCString(aType).get(),
getter_Copies(contractID));
aType, contractID);
if (NS_SUCCEEDED(rv)) {
docFactory = do_GetService(contractID.get());
if (docFactory && aLoaderType) {

View File

@ -1089,8 +1089,8 @@ nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
nsCString contractId;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
getter_Copies(contractId));
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type,
contractId);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
do_GetService(contractId.get());

View File

@ -663,6 +663,17 @@ public:
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:"))
|| aMessage.EqualsLiteral("SessionStore:flush");
}
void
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
@ -676,6 +687,19 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
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 =

View File

@ -286,6 +286,9 @@ FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
static PRTime
GetCollectionTimeDelta()
{
if (recordreplay::IsRecordingOrReplaying()) {
return 0;
}
PRTime now = PR_Now();
if (sFirstCollectionTime) {
return now - sFirstCollectionTime;
@ -625,7 +628,7 @@ nsJSContext::~nsJSContext()
void
nsJSContext::Destroy()
{
if (mGCOnDestruction) {
if (mGCOnDestruction && !recordreplay::IsRecordingOrReplaying()) {
PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY, mWindowProxy);
}
@ -1623,6 +1626,13 @@ ICCRunnerFired(TimeStamp aDeadline)
return true;
}
// Whether to skip the generation of timers for future GC/CC activity.
static bool
SkipCollectionTimers()
{
return sShuttingDown || recordreplay::IsRecordingOrReplaying();
}
//static
void
nsJSContext::BeginCycleCollectionCallback()
@ -1638,7 +1648,7 @@ nsJSContext::BeginCycleCollectionCallback()
MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
if (sShuttingDown) {
if (SkipCollectionTimers()) {
return;
}
@ -1870,7 +1880,7 @@ GCTimerFired(nsITimer *aTimer, void *aClosure)
{
nsJSContext::KillGCTimer();
nsJSContext::KillInterSliceGCRunner();
if (sShuttingDown) {
if (SkipCollectionTimers()) {
return;
}
@ -2173,7 +2183,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
void
nsJSContext::PokeShrinkingGC()
{
if (sShrinkingGCTimer || sShuttingDown) {
if (sShrinkingGCTimer || SkipCollectionTimers()) {
return;
}
@ -2189,7 +2199,7 @@ nsJSContext::PokeShrinkingGC()
void
nsJSContext::MaybePokeCC()
{
if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
if (sCCRunner || sICCRunner || !sHasRunGC || SkipCollectionTimers()) {
return;
}
@ -2360,7 +2370,7 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
nsJSContext::MaybePokeCC();
if (aDesc.isZone_) {
if (!sFullGCTimer && !sShuttingDown) {
if (!sFullGCTimer && !SkipCollectionTimers()) {
NS_NewTimerWithFuncCallback(&sFullGCTimer,
FullGCTimerFired,
nullptr,
@ -2373,7 +2383,8 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
nsJSContext::KillFullGCTimer();
}
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
if (!recordreplay::IsRecordingOrReplaying() &&
ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
nsCycleCollector_dispatchDeferredDeletion();
}
@ -2393,7 +2404,7 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
// Schedule another GC slice if the GC has more work to do.
nsJSContext::KillInterSliceGCRunner();
if (!sShuttingDown && !aDesc.isComplete_) {
if (!SkipCollectionTimers() && !aDesc.isComplete_) {
sInterSliceGCRunner =
IdleTaskRunner::Create([](TimeStamp aDeadline) {
return InterSliceGCRunnerFired(aDeadline, nullptr);
@ -2405,7 +2416,8 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
TaskCategory::GarbageCollection);
}
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
if (!recordreplay::IsRecordingOrReplaying() &&
ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
nsCycleCollector_dispatchDeferredDeletion();
}

View File

@ -411,6 +411,21 @@ nsJSUtils::ExecutionContext::DecodeBinASTAndExec(JS::CompileOptions& aCompileOpt
#endif
}
static bool
IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
if (!aValue.isObject()) {
return false;
}
JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrap(&aValue.toObject()));
if (!obj) {
return false;
}
return JS::IsPromiseObject(obj);
}
nsresult
nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue)
{
@ -428,6 +443,15 @@ nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRe
#ifdef DEBUG
mWantsReturnValue = false;
#endif
if (mCoerceToString && IsPromiseValue(mCx, mRetValue)) {
// We're a javascript: url and we should treat Promise return values as
// undefined.
//
// Once bug 1477821 is fixed this code might be able to go away, or will
// become enshrined in the spec, depending.
mRetValue.setUndefined();
}
if (mCoerceToString && !mRetValue.isUndefined()) {
JSString* str = JS::ToString(mCx, mRetValue);
if (!str) {

View File

@ -43,6 +43,10 @@ nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper)
if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
}
if (mozilla::recordreplay::IsReplaying()) {
mozilla::recordreplay::SetWeakPointerJSRoot(this, aWrapper);
}
}
void
@ -114,6 +118,12 @@ void
nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder,
nsScriptObjectTracer* aTracer)
{
// Skip checking if we are recording or replaying, as calling
// GetWrapperPreserveColor() can cause the cache's wrapper to be cleared.
if (recordreplay::IsRecordingOrReplaying()) {
return;
}
JSObject* wrapper = GetWrapperPreserveColor();
if (!wrapper) {
return;

View File

@ -79,6 +79,10 @@ static_assert(sizeof(void*) == 4, "Only support 32-bit and 64-bit");
* A number of the methods are implemented in nsWrapperCacheInlines.h because we
* have to include some JS headers that don't play nicely with the rest of the
* codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
*
* When recording or replaying an execution, wrapper caches are instrumented so
* that they behave consistently even if the GC executes at different points
* and collects different objects.
*/
class nsWrapperCache
@ -96,6 +100,10 @@ public:
}
~nsWrapperCache()
{
// Clear any JS root associated with this cache while replaying.
if (mozilla::recordreplay::IsReplaying()) {
mozilla::recordreplay::SetWeakPointerJSRoot(this, nullptr);
}
MOZ_ASSERT(!PreservingWrapper(),
"Destroying cache with a preserved wrapper!");
}
@ -133,6 +141,23 @@ public:
*/
JSObject* GetWrapperMaybeDead() const
{
// Keep track of accesses on the cache when recording or replaying an
// execution. Accesses during a GC (when thread events are disallowed)
// fetch the underlying object without making sure the returned value
// is consistent between recording and replay.
if (mozilla::recordreplay::IsRecordingOrReplaying() &&
!mozilla::recordreplay::AreThreadEventsDisallowed() &&
!mozilla::recordreplay::HasDivergedFromRecording()) {
bool success = mozilla::recordreplay::RecordReplayValue(!!mWrapper);
if (mozilla::recordreplay::IsReplaying()) {
if (success) {
MOZ_RELEASE_ASSERT(mWrapper);
} else {
const_cast<nsWrapperCache*>(this)->ClearWrapper();
}
}
}
return mWrapper;
}

View File

@ -13,7 +13,7 @@
inline JSObject*
nsWrapperCache::GetWrapperPreserveColor() const
{
JSObject* obj = mWrapper;
JSObject* obj = GetWrapperMaybeDead();
if (obj && js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
// The object has been found to be dead and is in the process of being
// finalized, so don't let the caller see it. As an optimisation, remove it

View File

@ -2737,6 +2737,12 @@ BindingJSObjectMallocBytes(void *aNativePtr)
return 0;
}
// Register a thing which DeferredFinalize might be called on during GC
// finalization. See DeferredFinalize.h
template<class T>
static void
RecordReplayRegisterDeferredFinalize(T* aObject);
// The BindingJSObjectCreator class is supposed to be used by a caller that
// wants to create and initialise a binding JSObject. After initialisation has
// been successfully completed it should call ForgetObject().
@ -2778,6 +2784,7 @@ public:
js::SetProxyReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
mNative = aNative;
mReflector = aReflector;
RecordReplayRegisterDeferredFinalize<T>(aNative);
}
if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
@ -2795,6 +2802,7 @@ public:
js::SetReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
mNative = aNative;
mReflector = aReflector;
RecordReplayRegisterDeferredFinalize<T>(aNative);
}
if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
@ -2908,6 +2916,15 @@ struct DeferredFinalizer
DeferredFinalize(Impl::AppendDeferredFinalizePointer,
Impl::DeferredFinalize, aObject);
}
static void
RecordReplayRegisterDeferredFinalize(T* aObject)
{
typedef DeferredFinalizerImpl<T> Impl;
RecordReplayRegisterDeferredFinalizeThing(Impl::AppendDeferredFinalizePointer,
Impl::DeferredFinalize,
aObject);
}
};
template<class T>
@ -2918,6 +2935,12 @@ struct DeferredFinalizer<T, true>
{
DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
}
static void
RecordReplayRegisterDeferredFinalize(T* aObject)
{
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, aObject);
}
};
template<class T>
@ -2927,6 +2950,13 @@ AddForDeferredFinalization(T* aObject)
DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
}
template<class T>
static void
RecordReplayRegisterDeferredFinalize(T* aObject)
{
DeferredFinalizer<T>::RecordReplayRegisterDeferredFinalize(aObject);
}
// This returns T's CC participant if it participates in CC or null if it
// doesn't. This also returns null for classes that don't inherit from
// nsISupports (QI should be used to get the participant for those).
@ -3064,6 +3094,7 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
NS_ADDREF(aNative);
aCache->SetWrapper(aGlobal);
RecordReplayRegisterDeferredFinalize<T>(aNative);
dom::AllocateProtoAndIfaceCache(aGlobal,
CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);

View File

@ -2838,7 +2838,12 @@ class IDLWrapperType(IDLType):
elif self.isEnum():
return True
elif self.isDictionary():
return all(m.type.isJSONType() for m in self.inner.members)
dictionary = self.inner
while dictionary:
if not all(m.type.isJSONType() for m in dictionary.members):
return False
dictionary = dictionary.parent
return True
else:
raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
"is serializable" % type(self.inner), [self.location])

View File

@ -514,6 +514,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", nullptr, 0);
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;

View File

@ -594,9 +594,7 @@ mozilla::ipc::IPCResult
ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
const StructuredCloneData& aInitialData,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
nsTArray<SystemFontListEntry>&& aFontList,
const FileDescriptor& aSharedDataMapFile,
const uint32_t& aSharedDataMapSize)
nsTArray<SystemFontListEntry>&& aFontList)
{
if (!sShutdownCanary) {
return IPC_OK();
@ -608,9 +606,6 @@ ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
InitXPCOM(aXPCOMInit, aInitialData);
InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
mSharedData = new SharedMap(ProcessGlobal::Get(), aSharedDataMapFile,
aSharedDataMapSize);
return IPC_OK();
}
@ -1252,8 +1247,12 @@ ContentChild::InitXPCOM(const XPCOMInitData& aXPCOMInit,
RecvSetCaptivePortalState(aXPCOMInit.captivePortalState());
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();
@ -1805,7 +1804,11 @@ 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 *
@ -2052,6 +2055,9 @@ ContentChild::GetCPOWManager()
if (PJavaScriptChild* c = LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
return CPOWManagerFor(c);
}
if (recordreplay::IsMiddleman()) {
return nullptr;
}
return CPOWManagerFor(SendPJavaScriptConstructor());
}
@ -2580,15 +2586,18 @@ ContentChild::RecvUpdateSharedData(const FileDescriptor& aMapFile,
nsTArray<IPCBlob>&& aBlobs,
nsTArray<nsCString>&& aChangedKeys)
{
if (mSharedData) {
nsTArray<RefPtr<BlobImpl>> blobImpls(aBlobs.Length());
for (auto& ipcBlob : aBlobs) {
blobImpls.AppendElement(IPCBlobUtils::Deserialize(ipcBlob));
}
nsTArray<RefPtr<BlobImpl>> blobImpls(aBlobs.Length());
for (auto& ipcBlob : aBlobs) {
blobImpls.AppendElement(IPCBlobUtils::Deserialize(ipcBlob));
}
if (mSharedData) {
mSharedData->Update(aMapFile, aMapSize,
std::move(blobImpls),
std::move(aChangedKeys));
} else {
mSharedData = new SharedMap(ProcessGlobal::Get(), aMapFile,
aMapSize, std::move(blobImpls));
}
return IPC_OK();

View File

@ -626,9 +626,7 @@ public:
RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
const StructuredCloneData& aInitialData,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
nsTArray<SystemFontListEntry>&& aFontList,
const FileDescriptor& aSharedDataMapFile,
const uint32_t& aSharedDataMapSize) override;
nsTArray<SystemFontListEntry>&& aFontList) override;
virtual mozilla::ipc::IPCResult
RecvProvideAnonymousTemporaryFile(const uint64_t& aID, const FileDescOrError& aFD) override;

View File

@ -2480,12 +2480,12 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority)
ScreenManager& screenManager = ScreenManager::GetSingleton();
screenManager.CopyScreensToRemote(this);
Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache,
fontList);
ipc::WritableSharedMap* sharedData = nsFrameMessageManager::sParentProcessManager->SharedData();
sharedData->Flush();
Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache,
fontList, sharedData->CloneMapFile(),
sharedData->MapSize());
sharedData->SendTo(this);
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
nsChromeRegistryChrome* chromeRegistry =

View File

@ -517,9 +517,7 @@ child:
StructuredCloneData initialData,
LookAndFeelInt[] lookAndFeelIntCache,
/* used on MacOSX and Linux only: */
SystemFontListEntry[] systemFontList,
FileDescriptor sharedDataMapFile,
uint32_t sharedDataMapSize);
SystemFontListEntry[] systemFontList);
// Notify child that last-pb-context-exited notification was observed
async LastPrivateDocShellDestroyed();

View File

@ -350,7 +350,9 @@ HangMonitorChild::InterruptCallback()
mPaintWhileInterruptingJS = false;
}
if (paintWhileInterruptingJS) {
// Don't paint from the interrupt callback when recording or replaying, as
// the interrupt callback is triggered non-deterministically.
if (paintWhileInterruptingJS && !recordreplay::IsRecordingOrReplaying()) {
RefPtr<TabChild> tabChild = TabChild::FindTabChild(paintWhileInterruptingJSTab);
if (tabChild) {
js::AutoAssertNoContentJS nojs(mContext);

View File

@ -43,8 +43,9 @@ SharedMap::SharedMap()
{}
SharedMap::SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor& aMapFile,
size_t aMapSize)
size_t aMapSize, nsTArray<RefPtr<BlobImpl>>&& aBlobs)
: DOMEventTargetHelper(aGlobal)
, mBlobImpls(std::move(aBlobs))
{
mMapFile.reset(new FileDescriptor(aMapFile));
mMapSize = aMapSize;
@ -107,7 +108,7 @@ SharedMap::Entry::Read(JSContext* aCx,
}
FileDescriptor
SharedMap::CloneMapFile()
SharedMap::CloneMapFile() const
{
if (mMap.initialized()) {
return mMap.cloneHandle();
@ -283,8 +284,9 @@ SharedMap*
WritableSharedMap::GetReadOnly()
{
if (!mReadOnly) {
nsTArray<RefPtr<BlobImpl>> blobs(mBlobImpls);
mReadOnly = new SharedMap(ProcessGlobal::Get(), CloneMapFile(),
MapSize());
MapSize(), std::move(blobs));
}
return mReadOnly;
}
@ -349,14 +351,15 @@ WritableSharedMap::Serialize()
for (auto& entry : IterHash(mEntries)) {
AlignTo(&offset, kStructuredCloneAlign);
entry->ExtractData(&ptr[offset], offset, blobImpls.Length());
size_t blobOffset = blobImpls.Length();
if (entry->BlobCount()) {
blobImpls.AppendElements(entry->Blobs());
}
entry->ExtractData(&ptr[offset], offset, blobOffset);
entry->Code(header);
offset += entry->Size();
if (entry->BlobCount()) {
mBlobImpls.AppendElements(entry->Blobs());
}
}
mBlobImpls = std::move(blobImpls);
@ -374,6 +377,23 @@ WritableSharedMap::Serialize()
return Ok();
}
void
WritableSharedMap::SendTo(ContentParent* aParent) const
{
nsTArray<IPCBlob> blobs(mBlobImpls.Length());
for (auto& blobImpl : mBlobImpls) {
nsresult rv = IPCBlobUtils::Serialize(blobImpl, aParent,
*blobs.AppendElement());
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
}
Unused << aParent->SendUpdateSharedData(CloneMapFile(), mMap.size(),
blobs, mChangedKeys);
}
void
WritableSharedMap::BroadcastChanges()
{
@ -388,18 +408,7 @@ WritableSharedMap::BroadcastChanges()
nsTArray<ContentParent*> parents;
ContentParent::GetAll(parents);
for (auto& parent : parents) {
nsTArray<IPCBlob> blobs(mBlobImpls.Length());
for (auto& blobImpl : mBlobImpls) {
nsresult rv = IPCBlobUtils::Serialize(blobImpl, parent,
*blobs.AppendElement());
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
}
Unused << parent->SendUpdateSharedData(CloneMapFile(), mMap.size(),
blobs, mChangedKeys);
SendTo(parent);
}
if (mReadOnly) {

View File

@ -22,6 +22,9 @@ class nsIGlobalObject;
namespace mozilla {
namespace dom {
class ContentParent;
namespace ipc {
/**
@ -58,7 +61,8 @@ public:
SharedMap();
SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor&, size_t);
SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor&, size_t,
nsTArray<RefPtr<BlobImpl>>&& aBlobs);
// Returns true if the map contains the given (UTF-8) key.
bool Has(const nsACString& name);
@ -105,7 +109,7 @@ public:
* memory region for this map. The file descriptor may be passed between
* processes, and used to update corresponding instances in child processes.
*/
FileDescriptor CloneMapFile();
FileDescriptor CloneMapFile() const;
/**
* Returns the size of the memory mapped region that backs this map. Must be
@ -346,6 +350,10 @@ public:
void Flush();
// Sends the current set of shared map data to the given content process.
void SendTo(ContentParent* aContentParent) const;
/**
* Returns the read-only SharedMap instance corresponding to this
* WritableSharedMap for use in the parent process.

View File

@ -58,6 +58,7 @@ StructuredCloneData::~StructuredCloneData()
StructuredCloneData&
StructuredCloneData::operator=(StructuredCloneData&& aOther)
{
mBlobImplArray = std::move(aOther.mBlobImplArray);
mExternalData = std::move(aOther.mExternalData);
mSharedData = std::move(aOther.mSharedData);
mIPCStreams = std::move(aOther.mIPCStreams);

View File

@ -3048,8 +3048,11 @@ TabChild::DoSendBlockingMessage(JSContext* aCx,
return false;
}
InfallibleTArray<CpowEntry> cpows;
if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
if (aCpows) {
jsipc::CPOWManager* mgr = Manager()->GetCPOWManager();
if (!mgr || !mgr->Wrap(aCx, aCpows, &cpows)) {
return false;
}
}
if (aIsSync) {
return SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
@ -3072,8 +3075,11 @@ TabChild::DoSendAsyncMessage(JSContext* aCx,
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
InfallibleTArray<CpowEntry> cpows;
if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return NS_ERROR_UNEXPECTED;
if (aCpows) {
jsipc::CPOWManager* mgr = Manager()->GetCPOWManager();
if (!mgr || !mgr->Wrap(aCx, aCpows, &cpows)) {
return NS_ERROR_UNEXPECTED;
}
}
if (!SendAsyncMessage(PromiseFlatString(aMessage), cpows,
Principal(aPrincipal), data)) {

View File

@ -2,14 +2,30 @@
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm");
const PROCESS_COUNT_PREF = "dom.ipc.processCount";
const remote = AppConstants.platform !== "android";
ExtensionTestUtils.init(this);
let contentPage;
Cu.importGlobalProperties(["Blob", "FileReader"]);
async function readBlob(key, sharedData = Services.cpmm.sharedData) {
let reader = new FileReader();
reader.readAsText(sharedData.get(key));
await ExtensionUtils.promiseEvent(reader, "loadend");
return reader.result;
}
function getKey(key, sharedData = Services.cpmm.sharedData) {
return sharedData.get(key);
}
function getContents(sharedMap = Services.cpmm.sharedData) {
return {
keys: Array.from(sharedMap.keys()),
@ -60,9 +76,23 @@ async function checkContentMaps(expected, parentOnly = false) {
}
}
async function loadContentPage() {
let page = await ExtensionTestUtils.loadContentPage("about:blank", {remote});
registerCleanupFunction(() => page.close());
page.addFrameScriptHelper(`
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.importGlobalProperties(["FileReader"]);
`);
return page;
}
add_task(async function setup() {
contentPage = await ExtensionTestUtils.loadContentPage("about:blank", {remote});
registerCleanupFunction(() => contentPage.close());
// Start with one content process so that we can increase the number
// later and test the behavior of a fresh content process.
Services.prefs.setIntPref(PROCESS_COUNT_PREF, 1);
contentPage = await loadContentPage();
});
add_task(async function test_sharedMap() {
@ -160,3 +190,61 @@ add_task(async function test_sharedMap() {
checkParentMap(expected);
await checkContentMaps(expected);
});
add_task(async function test_blobs() {
let {sharedData} = Services.ppmm;
let text = [
"The quick brown fox jumps over the lazy dog",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
];
let blobs = text.map(str => new Blob([str]));
let data = {foo: {bar: "baz"}};
sharedData.set("blob0", blobs[0]);
sharedData.set("blob1", blobs[1]);
sharedData.set("data", data);
equal(await readBlob("blob0", sharedData), text[0], "Expected text for blob0 in parent ppmm");
sharedData.flush();
equal(await readBlob("blob0", sharedData), text[0], "Expected text for blob0 in parent ppmm");
equal(await readBlob("blob1", sharedData), text[1], "Expected text for blob1 in parent ppmm");
equal(await readBlob("blob0"), text[0], "Expected text for blob0 in parent cpmm");
equal(await readBlob("blob1"), text[1], "Expected text for blob1 in parent cpmm");
equal(await contentPage.spawn("blob0", readBlob), text[0], "Expected text for blob0 in child 1 cpmm");
equal(await contentPage.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 1 cpmm");
// Start a second child process
Services.prefs.setIntPref(PROCESS_COUNT_PREF, 2);
let page2 = await loadContentPage();
equal(await page2.spawn("blob0", readBlob), text[0], "Expected text for blob0 in child 2 cpmm");
equal(await page2.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 2 cpmm");
sharedData.set("blob0", blobs[2]);
equal(await readBlob("blob0", sharedData), text[2], "Expected text for blob0 in parent ppmm");
sharedData.flush();
equal(await readBlob("blob0", sharedData), text[2], "Expected text for blob0 in parent ppmm");
equal(await readBlob("blob1", sharedData), text[1], "Expected text for blob1 in parent ppmm");
equal(await readBlob("blob0"), text[2], "Expected text for blob0 in parent cpmm");
equal(await readBlob("blob1"), text[1], "Expected text for blob1 in parent cpmm");
equal(await contentPage.spawn("blob0", readBlob), text[2], "Expected text for blob0 in child 1 cpmm");
equal(await contentPage.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 1 cpmm");
equal(await page2.spawn("blob0", readBlob), text[2], "Expected text for blob0 in child 2 cpmm");
equal(await page2.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 2 cpmm");
deepEqual(await page2.spawn("data", getKey), data, "Expected data for data key in child 2 cpmm");
});

View File

@ -13,3 +13,5 @@ support-files =
[test_bug351633-3.html]
[test_bug351633-4.html]
[test_bug384981.html]
[test_bug1382035-1.html]
[test_bug1382035-2.html]

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1382035
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1382035</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1382035 **/
addLoadEvent(function() {
is(frames[0].document.documentElement.textContent, "",
"Should not navigate to a stringified Promise");
SimpleTest.finish();
});
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1382035">Mozilla Bug 1382035</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe src="javascript: Promise.resolve()">
</iframe>
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1382035
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1382035</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1382035 **/
addLoadEvent(function() {
is(frames[0].document.documentElement.textContent, "test",
"Should not navigate to a stringified Promise");
SimpleTest.finish();
});
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1382035">Mozilla Bug 1382035</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe>
</iframe>
<script>
var blob = new Blob(["test"], { type: "text/html" });
var url = URL.createObjectURL(blob);
frames[0].location.href = url;
frames[0].location.href = "javascript: Promise.resolve()";
</script>
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -560,7 +560,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

View File

@ -220,6 +220,12 @@ 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) {
aRv.Throw(NS_ERROR_FAILURE);
@ -271,6 +277,12 @@ AudioContext::Constructor(const GlobalObject& aGlobal,
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) {
aRv.Throw(NS_ERROR_FAILURE);

View File

@ -2771,16 +2771,15 @@ nsPluginHost::RegisterWithCategoryManager(const nsCString& aMimeType,
return;
}
const char *contractId =
"@mozilla.org/content/plugin/document-loader-factory;1";
NS_NAMED_LITERAL_CSTRING(contractId,
"@mozilla.org/content/plugin/document-loader-factory;1");
if (aType == ePluginRegister) {
catMan->AddCategoryEntry("Gecko-Content-Viewers",
aMimeType.get(),
aMimeType,
contractId,
false, /* persist: broken by bug 193031 */
mOverrideInternalTypes,
nullptr);
mOverrideInternalTypes);
} else {
if (aType == ePluginMaybeUnregister) {
// Bail out if this type is still used by an enabled plugin
@ -2794,12 +2793,10 @@ nsPluginHost::RegisterWithCategoryManager(const nsCString& aMimeType,
// Only delete the entry if a plugin registered for it
nsCString value;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
aMimeType.get(),
getter_Copies(value));
if (NS_SUCCEEDED(rv) && strcmp(value.get(), contractId) == 0) {
aMimeType, value);
if (NS_SUCCEEDED(rv) && value == contractId) {
catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
aMimeType.get(),
true);
aMimeType, true);
}
}
}

View File

@ -320,9 +320,7 @@ PushDispatcher::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catMan) {
nsCString contractId;
nsresult rv = catMan->GetCategoryEntry("push",
mScope.BeginReading(),
getter_Copies(contractId));
nsresult rv = catMan->GetCategoryEntry("push", mScope, contractId);
if (NS_SUCCEEDED(rv)) {
// Ensure the service is created - we don't need to do anything with
// it though - we assume the service constructor attaches a listener.

View File

@ -576,6 +576,13 @@ 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();
@ -965,6 +972,11 @@ public:
NS_WARNING("failed to set workerCx's default locale");
}
}
// Cycle collections must occur at consistent points when recording/replaying.
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::RegisterTrigger(this, [=]() { nsCycleCollector_collect(nullptr); });
}
}
void Shutdown(JSContext* cx) override
@ -979,6 +991,10 @@ public:
~WorkerJSRuntime()
{
MOZ_COUNT_DTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::UnregisterTrigger(this);
}
}
virtual void
@ -1015,7 +1031,11 @@ public:
mWorkerPrivate->AssertIsOnWorkerThread();
if (aStatus == JSGC_END) {
nsCycleCollector_collect(nullptr);
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::ActivateTrigger(this);
} else {
nsCycleCollector_collect(nullptr);
}
}
}

View File

@ -1028,6 +1028,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx,
nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
::JS_SetPrivate(proto, docInfo);
NS_ADDREF(docInfo);
RecordReplayRegisterDeferredFinalize(docInfo);
JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding));
// Next, enter the realm of the property holder, wrap the proto, and

View File

@ -1,4 +1,4 @@
52817
52849
0/nm
0th/pt
1/n1
@ -2436,6 +2436,7 @@ Charlie/M
Charlot/M
Charlotta/M
Charlotte/M
Charlottesville/M
Charlottetown/M
Charlton
Charmaine/M
@ -4556,6 +4557,7 @@ GATT/M
GB/M
GCC/M
GDP/M
GDPR
GE/M
GED
GHQ/M
@ -15835,6 +15837,7 @@ backhoe/MS
backing/M
backlash/MS
backless
backlit
backlog/MS
backlogged
backlogging
@ -16090,6 +16093,7 @@ barbie/S
barbiturate/SM
barbwire/M
barcarole/SM
barcode/SDG
bard/SM
bardic
bare/DRSPYG
@ -16713,6 +16717,7 @@ bigness/M
bigot/MDS
bigotry/SM
bigwig/MS
bijection/S
bijou/M
bijoux
bike/DRSMZG
@ -20260,6 +20265,7 @@ commode's
commode/EIS
commodification
commodious/Y
commoditization
commodity/SM
commodore/SM
common's
@ -23728,7 +23734,9 @@ downy/RT
dowry/SM
dowse/DRSZG
dowser/M
dox/SDG
doxology/SM
doxx/SDG
doyen/SM
doyenne/MS
doz/XGDNS
@ -24516,7 +24524,7 @@ elope/DSGL
elopement/MS
eloquence/M
eloquent/Y
else
else/M
elsewhere
elucidate/DSGNX
elucidation/M
@ -27871,6 +27879,8 @@ genus/M
geocache/DSG
geocentric
geocentrically
geocentricism
geocentrism
geochemistry/M
geode/SM
geodesic/SM
@ -29066,8 +29076,11 @@ handspring/MS
handstand/SM
handwork/M
handwoven
handwrite/GS
handwriting/M
handwritten
handwritten
handwrote
handy/UTR
handyman/M
handymen
@ -29453,6 +29466,9 @@ helical
helices
helicopter/SGMD
heliocentric
heliocentrically
heliocentricism
heliocentrism
heliotrope/SM
helipad/S
heliport/MS
@ -31173,6 +31189,7 @@ infinitesimal/SMY
infinitival
infinitive/MS
infinitude/M
infinitum
infinity/SM
infirm
infirmary/SM
@ -34732,7 +34749,7 @@ mercerize/GDS
merchandise/MZGDRS
merchandiser/M
merchandising/M
merchant/MBS
merchant/MBSG
merchantman/M
merchantmen
merciful/UY
@ -39410,6 +39427,7 @@ photo/SGMD
photocell/MS
photocopier/M
photocopy/DRSMZG
photodetector/S
photoelectric
photoelectrically
photoengrave/DRSJZG
@ -39429,6 +39447,8 @@ photojournalist/SM
photometer/MS
photon/MS
photosensitive
photosensor/S
photosensory
photostat/SM
photostatic
photostatted
@ -40429,6 +40449,7 @@ prawn/MDSG
pray/ZGDRS
prayer/M
prayerful/Y
pre-fill/SDG
preach/DRSZGL
preacher/M
preachment/M
@ -40537,6 +40558,7 @@ preferment/M
preferred
preferring
prefigure/GDS
prefill/SDG
prefix/MDSG
preform/GSD
prefrontal
@ -40564,6 +40586,7 @@ prelim/SM
preliminarily
preliminary/SM
preliterate
preload/SDG
prelude/MS
premarital
premature/Y
@ -40601,6 +40624,7 @@ prepared/UP
preparedness/UM
prepay/GSL
prepayment/MS
prepend/SDG
preponderance/SM
preponderant/Y
preponderate/GDS
@ -42365,6 +42389,7 @@ rejuvenate/DSGN
rejuvenation/M
rel
relate/DRSBXZGNV
relatedly
relatedness/M
relater/M
relation/M
@ -43911,11 +43936,13 @@ scorch/MDRSZG
scorcher/M
score/MZGDRS
scoreboard/SM
scorebook/MS
scorecard/MS
scorekeeper/MS
scoreless
scoreline/S
scorer/M
scoresheet/MS
scorn/MDRSZG
scorner/M
scornful/Y
@ -47686,6 +47713,7 @@ surge/DSMG
surgeon/MS
surgery/SM
surgical/Y
surjection/S
surliness/M
surly/PTR
surmise/MGDS
@ -49192,6 +49220,7 @@ tonsorial
tonsure/DSMG
tony/RT
too
toodles
took/A
tool's
tool/ADGS
@ -50550,6 +50579,8 @@ unpolitical
unpopular
unpractical
unprecedented/Y
unpressured
unpressurized
unprofessional/Y
unpromising
unpropitious
@ -50568,6 +50599,7 @@ unremitting/Y
unrepentant
unreported
unrepresentative
unrequest/D
unrest/M
unrevealing
unripe/TR

View File

@ -917,6 +917,9 @@ IsItemProbablyActive(nsDisplayItem* aItem, nsDisplayListBuilder* aDisplayListBui
return active || HasActiveChildren(*opacityItem->GetChildren(), aDisplayListBuilder);
}
// TODO: handle other items?
if (aItem->GetChildren()) {
return HasActiveChildren(*aItem->GetChildren(), aDisplayListBuilder);;
}
return false;
}

View File

@ -2499,7 +2499,7 @@ gfxPlatform::InitCompositorAccelerationPrefs()
feature.UserForceEnable("Force-enabled by pref");
}
// Safe and headless modes override everything.
// Safe, headless, and record/replay modes override everything.
if (InSafeMode()) {
feature.ForceDisable(FeatureStatus::Blocked, "Acceleration blocked by safe-mode",
NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
@ -2508,6 +2508,10 @@ gfxPlatform::InitCompositorAccelerationPrefs()
feature.ForceDisable(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*/ bool

View File

@ -137,7 +137,7 @@ gfxSVGGlyphsDocument::SetupPresentation()
{
nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
nsCString contractId;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", contractId);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =

View File

@ -4,7 +4,7 @@
use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat, TextureTarget};
use debug_font_data;
use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, VAO};
use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO};
use device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint};
use euclid::{Point2D, Rect, Size2D, Transform3D};
use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
@ -104,13 +104,11 @@ pub struct DebugRenderer {
}
impl DebugRenderer {
pub fn new(device: &mut Device) -> Self {
let font_program = device.create_program("debug_font", "", &DESC_FONT).unwrap();
pub fn new(device: &mut Device) -> Result<Self, ShaderError> {
let font_program = device.create_program("debug_font", "", &DESC_FONT)?;
device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]);
let color_program = device
.create_program("debug_color", "", &DESC_COLOR)
.unwrap();
let color_program = device.create_program("debug_color", "", &DESC_COLOR)?;
let font_vao = device.create_vao(&DESC_FONT);
let line_vao = device.create_vao(&DESC_COLOR);
@ -127,7 +125,7 @@ impl DebugRenderer {
Some(&debug_font_data::FONT_BITMAP),
);
DebugRenderer {
Ok(DebugRenderer {
font_vertices: Vec::new(),
font_indices: Vec::new(),
line_vertices: Vec::new(),
@ -139,7 +137,7 @@ impl DebugRenderer {
font_vao,
line_vao,
font_texture,
}
})
}
pub fn deinit(self, device: &mut Device) {

View File

@ -1306,6 +1306,7 @@ struct TargetSelector {
#[cfg(feature = "debug_renderer")]
struct LazyInitializedDebugRenderer {
debug_renderer: Option<DebugRenderer>,
failed: bool,
}
#[cfg(feature = "debug_renderer")]
@ -1313,11 +1314,25 @@ impl LazyInitializedDebugRenderer {
pub fn new() -> Self {
Self {
debug_renderer: None,
failed: false,
}
}
pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> &'a mut DebugRenderer {
self.debug_renderer.get_or_insert_with(|| DebugRenderer::new(device))
pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> {
if self.failed {
return None;
}
if self.debug_renderer.is_none() {
match DebugRenderer::new(device) {
Ok(renderer) => { self.debug_renderer = Some(renderer); }
Err(_) => {
// The shader compilation code already logs errors.
self.failed = true;
}
}
}
self.debug_renderer.as_mut()
}
pub fn deinit(self, device: &mut Device) {
@ -2384,35 +2399,41 @@ impl Renderer {
if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
if let Some(framebuffer_size) = framebuffer_size {
//TODO: take device/pixel ratio into equation?
let screen_fraction = 1.0 / framebuffer_size.to_f32().area();
self.profiler.draw_profile(
&frame_profiles,
&self.backend_profile_counters,
&self.profile_counters,
&mut profile_timers,
&profile_samplers,
screen_fraction,
self.debug.get_mut(&mut self.device),
self.debug_flags.contains(DebugFlags::COMPACT_PROFILER),
);
if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
let screen_fraction = 1.0 / framebuffer_size.to_f32().area();
self.profiler.draw_profile(
&frame_profiles,
&self.backend_profile_counters,
&self.profile_counters,
&mut profile_timers,
&profile_samplers,
screen_fraction,
debug_renderer,
self.debug_flags.contains(DebugFlags::COMPACT_PROFILER),
);
}
}
}
if self.debug_flags.contains(DebugFlags::NEW_FRAME_INDICATOR) {
self.new_frame_indicator.changed();
self.new_frame_indicator.draw(
0.0, 0.0,
ColorU::new(0, 110, 220, 255),
self.debug.get_mut(&mut self.device)
);
if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
self.new_frame_indicator.changed();
self.new_frame_indicator.draw(
0.0, 0.0,
ColorU::new(0, 110, 220, 255),
debug_renderer,
);
}
}
if self.debug_flags.contains(DebugFlags::NEW_SCENE_INDICATOR) {
self.new_scene_indicator.draw(
160.0, 0.0,
ColorU::new(220, 30, 10, 255),
self.debug.get_mut(&mut self.device)
);
if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
self.new_scene_indicator.draw(
160.0, 0.0,
ColorU::new(220, 30, 10, 255),
debug_renderer,
);
}
}
}
@ -2429,8 +2450,9 @@ impl Renderer {
self.gpu_profile.end_frame();
#[cfg(feature = "debug_renderer")]
{
self.debug.get_mut(&mut self.device)
.render(&mut self.device, framebuffer_size);
if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
debug_renderer.render(&mut self.device, framebuffer_size);
}
}
self.device.end_frame();
});
@ -3755,7 +3777,7 @@ impl Renderer {
}
#[cfg(feature = "debug_renderer")]
pub fn debug_renderer<'b>(&'b mut self) -> &'b mut DebugRenderer {
pub fn debug_renderer<'b>(&'b mut self) -> Option<&'b mut DebugRenderer> {
self.debug.get_mut(&mut self.device)
}
@ -3893,7 +3915,10 @@ impl Renderer {
return;
}
let debug_renderer = self.debug.get_mut(&mut self.device);
let debug_renderer = match self.debug.get_mut(&mut self.device) {
Some(render) => render,
None => { return; }
};
let dy = debug_renderer.line_height();
let x0: f32 = 30.0;

View File

@ -875,9 +875,15 @@ impl ResourceCache {
ImageResult::Err(_) => panic!("Errors should already have been handled"),
};
self.texture_cache.request(&entry.texture_cache_handle, gpu_cache);
let needs_upload = self.texture_cache.request(&entry.texture_cache_handle, gpu_cache);
self.pending_image_requests.insert(request);
if !needs_upload && entry.dirty_rect.is_none() {
return
}
if !self.pending_image_requests.insert(request) {
return
}
if template.data.is_blob() {
let request: BlobImageRequest = request.into();

View File

@ -1 +1 @@
c2c5aaebdd6df22ce13941c1a4b16ef47eaa9f7b
e850fbd2e0e60a8de76c2d2464f0fa27316d5949

View File

@ -561,7 +561,7 @@ impl Wrench {
];
let color_and_offset = [(*BLACK_COLOR, 2.0), (*WHITE_COLOR, 0.0)];
let dr = self.renderer.debug_renderer();
let dr = self.renderer.debug_renderer().unwrap();
for ref co in &color_and_offset {
let x = self.device_pixel_ratio * (15.0 + co.1);

View File

@ -134,7 +134,10 @@ LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
void
UnlockScreenOrientation()
{
Hal()->SendUnlockScreenOrientation();
// Don't send this message from both the middleman and recording processes.
if (!recordreplay::IsMiddleman()) {
Hal()->SendUnlockScreenOrientation();
}
}
void

View File

@ -332,7 +332,7 @@ SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
nsCString contractId;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
getter_Copies(contractId));
contractId);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
do_GetService(contractId.get());

View File

@ -120,7 +120,7 @@ class StringBundleProxy : public nsIStringBundle
NS_DECLARE_STATIC_IID_ACCESSOR(STRINGBUNDLEPROXY_IID)
explicit StringBundleProxy(already_AddRefed<nsIStringBundle> aTarget)
: mReentrantMonitor("StringBundleProxy::mReentrantMonitor")
: mMutex("StringBundleProxy::mMutex")
, mTarget(aTarget)
{}
@ -128,7 +128,7 @@ class StringBundleProxy : public nsIStringBundle
void Retarget(nsIStringBundle* aTarget)
{
ReentrantMonitorAutoEnter automon(mReentrantMonitor);
MutexAutoLock automon(mMutex);
mTarget = aTarget;
}
@ -146,7 +146,7 @@ protected:
virtual ~StringBundleProxy() = default;
private:
ReentrantMonitor mReentrantMonitor;
Mutex mMutex;
nsCOMPtr<nsIStringBundle> mTarget;
// Atomically reads mTarget and returns a strong reference to it. This
@ -154,7 +154,7 @@ private:
// the main thread during access.
nsCOMPtr<nsIStringBundle> Target()
{
ReentrantMonitorAutoEnter automon(mReentrantMonitor);
MutexAutoLock automon(mMutex);
return mTarget;
}
};
@ -304,7 +304,7 @@ NS_IMPL_ISUPPORTS_INHERITED(SharedStringBundle, nsStringBundleBase, SharedString
nsStringBundleBase::nsStringBundleBase(const char* aURLSpec) :
mPropertiesURL(aURLSpec),
mReentrantMonitor("nsStringBundle.mReentrantMonitor"),
mMutex("nsStringBundle.mMutex"),
mAttemptedLoad(false),
mLoaded(false)
{
@ -595,7 +595,7 @@ nsStringBundleBase::GetStringFromName(const char* aName, nsAString& aResult)
{
NS_ENSURE_ARG_POINTER(aName);
ReentrantMonitorAutoEnter automon(mReentrantMonitor);
MutexAutoLock autolock(mMutex);
return GetStringImpl(nsDependentCString(aName), aResult);
}

View File

@ -6,7 +6,7 @@
#ifndef nsStringBundle_h__
#define nsStringBundle_h__
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Mutex.h"
#include "nsIStringBundle.h"
#include "nsIMemoryReporter.h"
#include "nsCOMPtr.h"
@ -55,10 +55,10 @@ protected:
void RegisterMemoryReporter();
nsCString mPropertiesURL;
mozilla::ReentrantMonitor mReentrantMonitor;
bool mAttemptedLoad;
bool mLoaded;
nsCString mPropertiesURL;
mozilla::Mutex mMutex;
bool mAttemptedLoad;
bool mLoaded;
size_t SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const override;

View File

@ -32,6 +32,11 @@ public:
// crash reporter needs metadata), the shmem should be parsed.
template <typename T>
static bool InitSingleton(T* aToplevelProtocol) {
// The crash reporter is not enabled in recording/replaying processes.
if (recordreplay::IsRecordingOrReplaying()) {
return true;
}
Shmem shmem;
if (!AllocShmem(aToplevelProtocol, &shmem)) {
return false;

View File

@ -33,7 +33,9 @@
namespace js {
static mozilla::Atomic<bool> sProtectedRegionsInit(false);
// Memory protection occurs at non-deterministic points when recording/replaying.
static mozilla::Atomic<bool, mozilla::SequentiallyConsistent,
mozilla::recordreplay::Behavior::DontPreserve> sProtectedRegionsInit(false);
/*
* A class to store the addresses of the regions recognized as protected

View File

@ -211,7 +211,11 @@ BytecodeCompiler::canLazilyParse()
!cx->realm()->behaviors().disableLazyParsing() &&
!cx->realm()->behaviors().discardSource() &&
!options.sourceIsLazy &&
!cx->lcovEnabled();
!cx->lcovEnabled() &&
// Disabled during record/replay. The replay debugger requires
// scripts to be constructed in a consistent order, which might not
// happen with lazy parsing.
!mozilla::recordreplay::IsRecordingOrReplaying();
}
bool
@ -795,7 +799,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
// syntax parsing and start of full parsing, so we do this now rather than
// after parsing below.
if (!lazy->scriptSource()->parseEnded().IsNull()) {
const mozilla::TimeDuration delta = mozilla::TimeStamp::Now() -
const mozilla::TimeDuration delta = ReallyNow() -
lazy->scriptSource()->parseEnded();
// Differentiate between web-facing and privileged code, to aid

View File

@ -142,11 +142,23 @@ struct RecyclableAtomMapValueWrapper
}
};
struct NameMapHasher : public DefaultHasher<JSAtom*>
{
static inline HashNumber hash(const Lookup& l) {
// Name maps use the atom's precomputed hash code, which is based on
// the atom's contents rather than its pointer value. This is necessary
// to preserve iteration order while recording/replaying: iteration can
// affect generated script bytecode and the order in which e.g. lookup
// property hooks are performed on the associated global.
return l->hash();
}
};
template <typename MapValue>
using RecyclableNameMap = InlineMap<JSAtom*,
RecyclableAtomMapValueWrapper<MapValue>,
24,
DefaultHasher<JSAtom*>,
NameMapHasher,
SystemAllocPolicy>;
using DeclaredNameMap = RecyclableNameMap<DeclaredNameInfo>;

View File

@ -954,7 +954,7 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
numArenasFreeCommitted(0),
verifyPreData(nullptr),
chunkAllocationSinceLastGC(false),
lastGCTime(mozilla::TimeStamp::Now()),
lastGCTime(ReallyNow()),
mode(TuningDefaults::Mode),
numActiveZoneIters(0),
cleanUpEverything(false),
@ -2144,7 +2144,9 @@ GCRuntime::shouldCompact()
bool
GCRuntime::isCompactingGCEnabled() const
{
return compactingEnabled && rt->mainContextFromOwnThread()->compactingDisabledCount == 0;
return compactingEnabled
&& rt->mainContextFromOwnThread()->compactingDisabledCount == 0
&& !mozilla::recordreplay::IsRecordingOrReplaying();
}
AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx)
@ -3245,7 +3247,7 @@ SliceBudget::SliceBudget(TimeBudget time)
makeUnlimited();
} else {
// Note: TimeBudget(0) is equivalent to WorkBudget(CounterReset).
deadline = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(time.budget);
deadline = ReallyNow() + mozilla::TimeDuration::FromMilliseconds(time.budget);
counter = CounterReset;
}
}
@ -3278,7 +3280,7 @@ SliceBudget::checkOverBudget()
if (deadline.IsNull())
return true;
bool over = mozilla::TimeStamp::Now() >= deadline;
bool over = ReallyNow() >= deadline;
if (!over)
counter = CounterReset;
return over;
@ -4236,7 +4238,7 @@ GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOu
*isFullOut = true;
bool any = false;
auto currentTime = mozilla::TimeStamp::Now();
auto currentTime = ReallyNow();
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
/* Set up which zones will be collected. */
@ -6766,7 +6768,7 @@ GCRuntime::finishCollection()
marker.stop();
clearBufferedGrayRoots();
auto currentTime = mozilla::TimeStamp::Now();
auto currentTime = ReallyNow();
schedulingState.updateHighFrequencyMode(lastGCTime, currentTime, tunables);
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
@ -7751,6 +7753,10 @@ GCRuntime::defaultBudget(JS::gcreason::Reason reason, int64_t millis)
void
GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
{
// Garbage collection can occur at different points between recording and
// replay, so disallow recorded events from occurring during the GC.
mozilla::recordreplay::AutoDisallowThreadEvents d;
invocationKind = gckind;
collect(true, SliceBudget::unlimited(), reason);
}
@ -8754,7 +8760,8 @@ 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)

View File

@ -3499,6 +3499,10 @@ UnmarkGrayGCThing(JSRuntime* rt, JS::GCCellPtr thing)
{
MOZ_ASSERT(thing);
// 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;
UnmarkGrayTracer unmarker(rt);
gcstats::AutoPhase innerPhase(rt->gc.stats(), gcstats::PhaseKind::UNMARK_GRAY);
unmarker.unmark(thing);

View File

@ -286,6 +286,8 @@ MarkPagesInUse(void* p, size_t size)
size_t
GetPageFaultCount()
{
if (mozilla::recordreplay::IsRecordingOrReplaying())
return 0;
PROCESS_MEMORY_COUNTERS pmc;
if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
return 0;
@ -813,6 +815,8 @@ MarkPagesInUse(void* p, size_t size)
size_t
GetPageFaultCount()
{
if (mozilla::recordreplay::IsRecordingOrReplaying())
return 0;
struct rusage usage;
int err = getrusage(RUSAGE_SELF, &usage);
if (err)

View File

@ -375,13 +375,13 @@ js::Nursery::allocate(size_t size)
if (chunkno == maxChunkCount())
return nullptr;
if (MOZ_UNLIKELY(chunkno == allocatedChunkCount())) {
mozilla::TimeStamp start = TimeStamp::Now();
mozilla::TimeStamp start = ReallyNow();
{
AutoLockGCBgAlloc lock(runtime());
if (!allocateNextChunk(chunkno, lock))
return nullptr;
}
timeInChunkAlloc_ += TimeStamp::Now() - start;
timeInChunkAlloc_ += ReallyNow() - start;
MOZ_ASSERT(chunkno < allocatedChunkCount());
}
setCurrentChunk(chunkno);
@ -669,13 +669,13 @@ js::Nursery::maybeClearProfileDurations()
inline void
js::Nursery::startProfile(ProfileKey key)
{
startTimes_[key] = TimeStamp::Now();
startTimes_[key] = ReallyNow();
}
inline void
js::Nursery::endProfile(ProfileKey key)
{
profileDurations_[key] = TimeStamp::Now() - startTimes_[key];
profileDurations_[key] = ReallyNow() - startTimes_[key];
totalDurations_[key] += profileDurations_[key];
}
@ -703,6 +703,8 @@ js::Nursery::collect(JS::gcreason::Reason 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

View File

@ -1051,7 +1051,7 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
if (!slices_.emplaceBack(budget,
reason,
TimeStamp::Now(),
ReallyNow(),
GetPageFaultCount(),
runtime->gc.state()))
{
@ -1083,7 +1083,7 @@ Statistics::endSlice()
if (!aborted) {
auto& slice = slices_.back();
slice.end = TimeStamp::Now();
slice.end = ReallyNow();
slice.endFaults = GetPageFaultCount();
slice.finalState = runtime->gc.state();
@ -1246,7 +1246,7 @@ Statistics::resumePhases()
{
Phase resumePhase = suspendedPhases.popCopy();
if (resumePhase == Phase::MUTATOR)
timedGCTime += TimeStamp::Now() - timedGCStart;
timedGCTime += ReallyNow() - timedGCStart;
recordPhaseBegin(resumePhase);
}
}
@ -1277,7 +1277,7 @@ Statistics::recordPhaseBegin(Phase phase)
Phase current = currentPhase();
MOZ_ASSERT(phases[phase].parent == current);
TimeStamp now = TimeStamp::Now();
TimeStamp now = ReallyNow();
if (current != Phase::NONE) {
MOZ_ASSERT(now >= phaseStartTimes[currentPhase()], "Inconsistent time data; see bug 1400153");
@ -1299,7 +1299,7 @@ Statistics::recordPhaseEnd(Phase phase)
MOZ_ASSERT(phaseStartTimes[phase]);
TimeStamp now = TimeStamp::Now();
TimeStamp now = ReallyNow();
// Make sure this phase ends after it starts.
MOZ_ASSERT(now >= phaseStartTimes[phase], "Inconsistent time data; see bug 1400153");
@ -1383,7 +1383,7 @@ Statistics::recordParallelPhase(PhaseKind phaseKind, TimeDuration duration)
TimeStamp
Statistics::beginSCC()
{
return TimeStamp::Now();
return ReallyNow();
}
void
@ -1392,7 +1392,7 @@ Statistics::endSCC(unsigned scc, TimeStamp start)
if (scc >= sccTimes.length() && !sccTimes.resize(scc + 1))
return;
sccTimes[scc] += TimeStamp::Now() - start;
sccTimes[scc] += ReallyNow() - start;
}
/*

View File

@ -5198,7 +5198,6 @@ CodeGenerator::generateArgumentsChecks(bool assert)
if (!types || types->unknown())
continue;
#ifndef JS_CODEGEN_ARM64
// Calculate the offset on the stack of the argument.
// (i - info.startArgSlot()) - Compute index of arg within arg vector.
// ... * sizeof(Value) - Scale by value size.
@ -5208,16 +5207,9 @@ CodeGenerator::generateArgumentsChecks(bool assert)
// guardObjectType will zero the stack pointer register on speculative
// paths.
Register spectreRegToZero = masm.getStackPointer();
Register spectreRegToZero = AsRegister(masm.getStackPointer());
masm.guardTypeSet(argAddr, types, BarrierKind::TypeSet, temp1, temp2,
spectreRegToZero, &miss);
#else
// On ARM64, the stack pointer situation is more complicated. When we
// enable Ion, we should figure out how to mitigate Spectre there.
mozilla::Unused << temp1;
mozilla::Unused << temp2;
MOZ_CRASH("NYI");
#endif
}
if (miss.used()) {

View File

@ -141,6 +141,12 @@ AsRegister(RegisterOrSP r)
return Register::FromCode(r.code);
}
static inline Register
AsRegister(Register r)
{
return r;
}
inline bool
operator == (Register r, RegisterOrSP e) {
return r.code() == e.code;

View File

@ -1313,7 +1313,7 @@ DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, Handl
return true;
}
if (engine == ICStubEngine::Baseline) {
if (engine == ICStubEngine::Baseline && !JitOptions.disableCacheIR) {
RootedScript script(cx, info.outerScript(cx));
CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
bool attached = false;

View File

@ -94,8 +94,8 @@ static constexpr Register r##N { Registers::x##N };
REGISTER_CODE_LIST(DEFINE_UNSIZED_REGISTERS)
#undef DEFINE_UNSIZED_REGISTERS
static constexpr Register ip0 { Registers::x16 };
static constexpr Register ip1 { Registers::x16 };
static constexpr Register fp { Registers::x30 };
static constexpr Register ip1 { Registers::x17 };
static constexpr Register fp { Registers::x29 };
static constexpr Register lr { Registers::x30 };
static constexpr Register rzr { Registers::xzr };

View File

@ -39,19 +39,38 @@ CodeGeneratorARM64::CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, Macro
bool
CodeGeneratorARM64::generateOutOfLineCode()
{
MOZ_CRASH("generateOutOfLineCode");
if (!CodeGeneratorShared::generateOutOfLineCode())
return false;
if (deoptLabel_.used()) {
// All non-table-based bailouts will go here.
masm.bind(&deoptLabel_);
// Store the frame size, so the handler can recover the IonScript.
masm.Mov(x30, frameSize());
TrampolinePtr handler = gen->jitRuntime()->getGenericBailoutHandler();
masm.jump(handler);
}
return !masm.oom();
}
void
CodeGeneratorARM64::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse)
{
MOZ_CRASH("emitBranch");
if (isNextBlock(mirFalse->lir())) {
jumpToBlock(mirTrue, cond);
} else {
jumpToBlock(mirFalse, Assembler::InvertCondition(cond));
jumpToBlock(mirTrue);
}
}
void
OutOfLineBailout::accept(CodeGeneratorARM64* codegen)
{
MOZ_CRASH("accept");
codegen->visitOutOfLineBailout(this);
}
void
@ -69,19 +88,60 @@ CodeGenerator::visitCompare(LCompare* comp)
void
CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp)
{
MOZ_CRASH("visitCompareAndBranch");
const MCompare* mir = comp->cmpMir();
const MCompare::CompareType type = mir->compareType();
const LAllocation* left = comp->left();
const LAllocation* right = comp->right();
if (type == MCompare::Compare_Object || type == MCompare::Compare_Symbol) {
masm.cmpPtr(ToRegister(left), ToRegister(right));
} else if (right->isConstant()) {
masm.cmp32(ToRegister(left), Imm32(ToInt32(right)));
} else {
masm.cmp32(ToRegister(left), ToRegister(right));
}
Assembler::Condition cond = JSOpToCondition(type, comp->jsop());
emitBranch(cond, comp->ifTrue(), comp->ifFalse());
}
void
CodeGeneratorARM64::bailoutIf(Assembler::Condition condition, LSnapshot* snapshot)
{
MOZ_CRASH("bailoutIf");
encode(snapshot);
// Though the assembler doesn't track all frame pushes, at least make sure
// the known value makes sense.
MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_,
frameClass_.frameSize() == masm.framePushed());
// ARM64 doesn't use a bailout table.
InlineScriptTree* tree = snapshot->mir()->block()->trackedTree();
OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot);
addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code()));
masm.B(ool->entry(), condition);
}
void
CodeGeneratorARM64::bailoutFrom(Label* label, LSnapshot* snapshot)
{
MOZ_CRASH("bailoutFrom");
MOZ_ASSERT(label->used());
MOZ_ASSERT(!label->bound());
encode(snapshot);
// Though the assembler doesn't track all frame pushes, at least make sure
// the known value makes sense.
MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_,
frameClass_.frameSize() == masm.framePushed());
// ARM64 doesn't use a bailout table.
InlineScriptTree* tree = snapshot->mir()->block()->trackedTree();
OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot);
addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code()));
masm.retarget(label, ool->entry());
}
void
@ -93,7 +153,8 @@ CodeGeneratorARM64::bailout(LSnapshot* snapshot)
void
CodeGeneratorARM64::visitOutOfLineBailout(OutOfLineBailout* ool)
{
MOZ_CRASH("visitOutOfLineBailout");
masm.push(Imm32(ool->snapshot()->snapshotOffset()));
masm.B(&deoptLabel_);
}
void
@ -151,13 +212,21 @@ toXRegister(const T* a)
js::jit::Operand
toWOperand(const LAllocation* a)
{
MOZ_CRASH("toWOperand");
if (a->isConstant())
return js::jit::Operand(ToInt32(a));
return js::jit::Operand(toWRegister(a));
}
vixl::CPURegister
ToCPURegister(const LAllocation* a, Scalar::Type type)
{
MOZ_CRASH("ToCPURegister");
if (a->isFloatReg() && type == Scalar::Float64)
return ARMFPRegister(ToFloatRegister(a), 64);
if (a->isFloatReg() && type == Scalar::Float32)
return ARMFPRegister(ToFloatRegister(a), 32);
if (a->isGeneralReg())
return ARMRegister(ToRegister(a), 32);
MOZ_CRASH("Unknown LAllocation");
}
vixl::CPURegister
@ -169,7 +238,19 @@ ToCPURegister(const LDefinition* d, Scalar::Type type)
void
CodeGenerator::visitAddI(LAddI* ins)
{
MOZ_CRASH("visitAddI");
const LAllocation* lhs = ins->getOperand(0);
const LAllocation* rhs = ins->getOperand(1);
const LDefinition* dest = ins->getDef(0);
// Platforms with three-operand arithmetic ops don't need recovery.
MOZ_ASSERT(!ins->recoversInput());
if (ins->snapshot()) {
masm.Adds(toWRegister(dest), toWRegister(lhs), toWOperand(rhs));
bailoutIf(Assembler::Overflow, ins->snapshot());
} else {
masm.Add(toWRegister(dest), toWRegister(lhs), toWOperand(rhs));
}
}
void
@ -428,13 +509,75 @@ CodeGenerator::visitValue(LValue* value)
void
CodeGenerator::visitBox(LBox* box)
{
MOZ_CRASH("visitBox");
const LAllocation* in = box->getOperand(0);
ValueOperand result = ToOutValue(box);
masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result);
}
void
CodeGenerator::visitUnbox(LUnbox* unbox)
{
MOZ_CRASH("visitUnbox");
MUnbox* mir = unbox->mir();
if (mir->fallible()) {
const ValueOperand value = ToValue(unbox, LUnbox::Input);
Assembler::Condition cond;
switch (mir->type()) {
case MIRType::Int32:
cond = masm.testInt32(Assembler::NotEqual, value);
break;
case MIRType::Boolean:
cond = masm.testBoolean(Assembler::NotEqual, value);
break;
case MIRType::Object:
cond = masm.testObject(Assembler::NotEqual, value);
break;
case MIRType::String:
cond = masm.testString(Assembler::NotEqual, value);
break;
case MIRType::Symbol:
cond = masm.testSymbol(Assembler::NotEqual, value);
break;
default:
MOZ_CRASH("Given MIRType cannot be unboxed.");
}
bailoutIf(cond, unbox->snapshot());
} else {
#ifdef DEBUG
JSValueTag tag = MIRTypeToTag(mir->type());
Label ok;
ValueOperand input = ToValue(unbox, LUnbox::Input);
ScratchTagScope scratch(masm, input);
masm.splitTagForTest(input, scratch);
masm.branchTest32(Assembler::Condition::Equal, scratch, Imm32(tag), &ok);
masm.assumeUnreachable("Infallible unbox type mismatch");
masm.bind(&ok);
#endif
}
ValueOperand input = ToValue(unbox, LUnbox::Input);
Register result = ToRegister(unbox->output());
switch (mir->type()) {
case MIRType::Int32:
masm.unboxInt32(input, result);
break;
case MIRType::Boolean:
masm.unboxBoolean(input, result);
break;
case MIRType::Object:
masm.unboxObject(input, result);
break;
case MIRType::String:
masm.unboxString(input, result);
break;
case MIRType::Symbol:
masm.unboxSymbol(input, result);
break;
default:
MOZ_CRASH("Given MIRType cannot be unboxed.");
}
}
void
@ -567,7 +710,22 @@ CodeGeneratorARM64::storeElementTyped(const LAllocation* value, MIRType valueTyp
void
CodeGeneratorARM64::generateInvalidateEpilogue()
{
MOZ_CRASH("generateInvalidateEpilogue");
// Ensure that there is enough space in the buffer for the OsiPoint patching
// to occur. Otherwise, we could overwrite the invalidation epilogue.
for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize())
masm.nop();
masm.bind(&invalidate_);
// Push the Ion script onto the stack (when we determine what that pointer is).
invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
TrampolinePtr thunk = gen->jitRuntime()->getInvalidationThunk();
masm.call(thunk);
// We should never reach this point in JIT code -- the invalidation thunk
// should pop the invalidated JS frame and return directly to its caller.
masm.assumeUnreachable("Should have returned directly to its caller instead of here.");
}
template <class U>

View File

@ -43,26 +43,71 @@ LIRGeneratorARM64::useByteOpRegisterOrNonDoubleConstant(MDefinition* mir)
void
LIRGenerator::visitBox(MBox* box)
{
MOZ_CRASH("visitBox");
MDefinition* opd = box->getOperand(0);
// If the operand is a constant, emit near its uses.
if (opd->isConstant() && box->canEmitAtUses()) {
emitAtUses(box);
return;
}
if (opd->isConstant()) {
define(new(alloc()) LValue(opd->toConstant()->toJSValue()), box, LDefinition(LDefinition::BOX));
} else {
LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
define(ins, box, LDefinition(LDefinition::BOX));
}
}
void
LIRGenerator::visitUnbox(MUnbox* unbox)
{
MOZ_CRASH("visitUnbox");
MDefinition* box = unbox->getOperand(0);
if (box->type() == MIRType::ObjectOrNull) {
LUnboxObjectOrNull* lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
defineReuseInput(lir, unbox, 0);
return;
}
MOZ_ASSERT(box->type() == MIRType::Value);
LUnboxBase* lir;
if (IsFloatingPointType(unbox->type())) {
lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
} else if (unbox->fallible()) {
// If the unbox is fallible, load the Value in a register first to
// avoid multiple loads.
lir = new(alloc()) LUnbox(useRegisterAtStart(box));
} else {
lir = new(alloc()) LUnbox(useAtStart(box));
}
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
define(lir, unbox);
}
void
LIRGenerator::visitReturn(MReturn* ret)
{
MOZ_CRASH("visitReturn");
MDefinition* opd = ret->getOperand(0);
MOZ_ASSERT(opd->type() == MIRType::Value);
LReturn* ins = new(alloc()) LReturn;
ins->setOperand(0, useFixed(opd, JSReturnReg));
add(ins);
}
// x = !y
void
LIRGeneratorARM64::lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input)
{
MOZ_CRASH("lowerForALU");
ins->setOperand(0, ins->snapshot() ? useRegister(input) : useRegisterAtStart(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
// z = x+y
@ -70,7 +115,10 @@ void
LIRGeneratorARM64::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
MOZ_CRASH("lowerForALU");
ins->setOperand(0, ins->snapshot() ? useRegister(lhs) : useRegisterAtStart(lhs));
ins->setOperand(1, ins->snapshot() ? useRegisterOrConstant(rhs) :
useRegisterOrConstantAtStart(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
void

View File

@ -4200,7 +4200,9 @@ CanDoOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t leng
}
}
return cx->runtime()->canUseParallelParsing() && CanUseExtraThreads();
return cx->runtime()->canUseParallelParsing() &&
CanUseExtraThreads() &&
!mozilla::recordreplay::IsRecordingOrReplaying();
}
JS_PUBLIC_API(bool)

View File

@ -345,7 +345,8 @@ AlwaysPoison(void* ptr, uint8_t value, size_t num, MemCheckKind kind)
static inline void
Poison(void* ptr, uint8_t value, size_t num, MemCheckKind kind)
{
static bool disablePoison = bool(getenv("JSGC_DISABLE_POISONING"));
static bool disablePoison = !mozilla::recordreplay::IsRecordingOrReplaying()
&& bool(getenv("JSGC_DISABLE_POISONING"));
if (!disablePoison)
AlwaysPoison(ptr, value, num, kind);
}

View File

@ -197,7 +197,7 @@ js::ThisThread::SetName(const char* name)
#else
rv = pthread_setname_np(pthread_self(), name);
#endif
MOZ_RELEASE_ASSERT(!rv);
MOZ_RELEASE_ASSERT(!rv || mozilla::recordreplay::IsRecordingOrReplaying());
}
void

View File

@ -1547,7 +1547,7 @@ static inline
TimeDuration
TimeSince(TimeStamp prev)
{
TimeStamp now = TimeStamp::Now();
TimeStamp now = ReallyNow();
// Sadly this happens sometimes.
MOZ_ASSERT(now >= prev);
if (now < prev)
@ -1560,7 +1560,7 @@ js::GCParallelTask::runFromMainThread(JSRuntime* rt)
{
assertNotStarted();
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
TimeStamp timeStart = TimeStamp::Now();
TimeStamp timeStart = ReallyNow();
runTask();
duration_ = TimeSince(timeStart);
}
@ -1575,7 +1575,7 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock)
{
AutoUnlockHelperThreadState parallelSection(lock);
TimeStamp timeStart = TimeStamp::Now();
TimeStamp timeStart = ReallyNow();
runTask();
duration_ = TimeSince(timeStart);
}
@ -1859,7 +1859,7 @@ HelperThread::destroy()
void
HelperThread::ensureRegisteredWithProfiler()
{
if (registered)
if (registered || mozilla::recordreplay::IsRecordingOrReplaying())
return;
JS::RegisterThreadCallback callback = HelperThreadState().registerThread;
@ -1888,6 +1888,11 @@ HelperThread::ThreadMain(void* arg)
{
ThisThread::SetName("JS Helper");
// Helper threads are allowed to run differently during recording and
// replay, as compiled scripts and GCs are allowed to vary. Because of
// this, no recorded events at all should occur while on helper threads.
mozilla::recordreplay::AutoDisallowThreadEvents d;
static_cast<HelperThread*>(arg)->threadLoop();
Mutex::ShutDown();
}
@ -2297,6 +2302,13 @@ GlobalHelperThreadState::trace(JSTracer* trc)
parseTask->trace(trc);
}
/* static */ void
HelperThread::WakeupAll()
{
AutoLockHelperThreadState lock;
HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
}
void
JSContext::setHelperThread(HelperThread* thread)
{
@ -2390,6 +2402,19 @@ HelperThread::threadLoop()
const TaskSpec* task = findHighestPriorityTask(lock);
if (!task) {
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
// Unlock the helper thread state lock before potentially
// blocking while the main thread waits for all threads to
// become idle. Otherwise we would need to see if we need to
// block at every point where a helper thread acquires the
// helper thread state lock.
{
AutoUnlockHelperThreadState unlock(lock);
mozilla::recordreplay::MaybeWaitForCheckpointSave();
}
mozilla::recordreplay::NotifyUnrecordedWait(WakeupAll);
}
HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
continue;
}

View File

@ -414,6 +414,8 @@ struct HelperThread
static void ThreadMain(void* arg);
void threadLoop();
static void WakeupAll();
void ensureRegisteredWithProfiler();
void unregisterWithProfilerIfNeeded();

View File

@ -300,6 +300,7 @@ js::ReportOutOfMemory(JSContext* cx)
*/
fprintf(stderr, "ReportOutOfMemory called\n");
#endif
mozilla::recordreplay::InvalidateRecording("OutOfMemory exception thrown");
if (cx->helperThread())
return cx->addPendingOutOfMemory();
@ -336,6 +337,7 @@ js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber)
*/
fprintf(stderr, "ReportOverRecursed called\n");
#endif
mozilla::recordreplay::InvalidateRecording("OverRecursed exception thrown");
if (maybecx) {
if (!maybecx->helperThread()) {
JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, errorNumber);

View File

@ -29,6 +29,7 @@
#include "vm/Scope.h"
#include "vm/Shape.h"
#include "vm/SharedImmutableStringsCache.h"
#include "vm/Time.h"
namespace JS {
struct ScriptSourceInfo;
@ -682,7 +683,7 @@ class ScriptSource
// Inform `this` source that it has been fully parsed.
void recordParseEnded() {
MOZ_ASSERT(parseEnded_.IsNull());
parseEnded_ = mozilla::TimeStamp::Now();
parseEnded_ = ReallyNow();
}
};

View File

@ -413,6 +413,13 @@ HandleInterrupt(JSContext* cx, bool invokeCallback)
MOZ_ASSERT(cx->requestDepth >= 1);
MOZ_ASSERT(!cx->zone()->isAtomsZone());
// Interrupts can occur at different points between recording and replay,
// so no recorded behaviors should occur while handling an interrupt.
// Additionally, returning false here will change subsequent behavior, so
// such an event cannot occur during recording or replay without
// invalidating the recording.
mozilla::recordreplay::AutoDisallowThreadEvents d;
cx->runtime()->gc.gcIfRequested();
// A worker thread may have requested an interrupt after finishing an Ion
@ -447,15 +454,18 @@ HandleInterrupt(JSContext* cx, bool invokeCallback)
RootedValue rval(cx);
switch (Debugger::onSingleStep(cx, &rval)) {
case ResumeMode::Terminate:
mozilla::recordreplay::InvalidateRecording("Debugger single-step produced an error");
return false;
case ResumeMode::Continue:
return true;
case ResumeMode::Return:
// See note in Debugger::propagateForcedReturn.
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
mozilla::recordreplay::InvalidateRecording("Debugger single-step forced return");
return false;
case ResumeMode::Throw:
cx->setPendingException(rval);
mozilla::recordreplay::InvalidateRecording("Debugger single-step threw an exception");
return false;
default:;
}
@ -479,6 +489,7 @@ HandleInterrupt(JSContext* cx, bool invokeCallback)
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
JSMSG_TERMINATED, chars);
mozilla::recordreplay::InvalidateRecording("Interrupt callback forced return");
return false;
}

View File

@ -477,7 +477,8 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
mozilla::recordreplay::Behavior::DontPreserve> numActiveHelperThreadZones;
// Any activity affecting the heap.
mozilla::Atomic<JS::HeapState> heapState_;
mozilla::Atomic<JS::HeapState, mozilla::SequentiallyConsistent,
mozilla::recordreplay::Behavior::DontPreserve> heapState_;
friend class js::AutoLockScriptData;

View File

@ -482,7 +482,7 @@ struct JSStructuredCloneWriter {
counts(out.context()), entries(out.context()),
memory(out.context()),
transferable(out.context(), tVal),
transferableObjects(out.context(), GCHashSet<JSObject*>(cx)),
transferableObjects(out.context(), TransferableObjectsSet(cx)),
cloneDataPolicy(cloneDataPolicy)
{
out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables);
@ -566,9 +566,23 @@ struct JSStructuredCloneWriter {
SystemAllocPolicy>;
Rooted<CloneMemory> memory;
struct TransferableObjectsHasher : public DefaultHasher<JSObject*>
{
static inline HashNumber hash(const Lookup& l) {
// Iteration order of the transferable objects table must be
// preserved during recording/replaying, as the callbacks used
// during transfer may interact with the recording. Just use the
// same hash number for all elements to ensure this.
if (mozilla::recordreplay::IsRecordingOrReplaying())
return 0;
return DefaultHasher<JSObject*>::hash(l);
}
};
// Set of transferable objects
RootedValue transferable;
Rooted<GCHashSet<JSObject*>> transferableObjects;
typedef GCHashSet<JSObject*, TransferableObjectsHasher> TransferableObjectsSet;
Rooted<TransferableObjectsSet> transferableObjects;
const JS::CloneDataPolicy cloneDataPolicy;

View File

@ -7,6 +7,9 @@
#ifndef vm_Time_h
#define vm_Time_h
#include "mozilla/RecordReplay.h"
#include "mozilla/TimeStamp.h"
#include <stddef.h>
#include <stdint.h>
@ -132,6 +135,8 @@ PRMJ_FormatTime(char* buf, int buflen, const char* fmt, const PRMJTime* tm,
static __inline uint64_t
ReadTimestampCounter(void)
{
if (mozilla::recordreplay::IsRecordingOrReplaying())
return 0;
return __rdtsc();
}
@ -140,6 +145,8 @@ ReadTimestampCounter(void)
static __inline__ uint64_t
ReadTimestampCounter(void)
{
if (mozilla::recordreplay::IsRecordingOrReplaying())
return 0;
uint64_t x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
@ -150,6 +157,8 @@ ReadTimestampCounter(void)
static __inline__ uint64_t
ReadTimestampCounter(void)
{
if (mozilla::recordreplay::IsRecordingOrReplaying())
return 0;
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 );
@ -161,4 +170,16 @@ ReadTimestampCounter(void)
#endif
namespace js {
// Get the current time, bypassing any record/replay instrumentation.
static inline mozilla::TimeStamp
ReallyNow()
{
mozilla::recordreplay::AutoPassThroughThreadEvents pt;
return mozilla::TimeStamp::Now();
}
} // namespace js
#endif /* vm_Time_h */

View File

@ -172,6 +172,8 @@ js::InferSpewActive(SpewChannel channel)
if (!checked) {
checked = true;
PodArrayZero(active);
if (mozilla::recordreplay::IsRecordingOrReplaying())
return false;
const char* env = getenv("INFERFLAGS");
if (!env)
return false;
@ -194,6 +196,8 @@ static bool InferSpewColorable()
static bool checked = false;
if (!checked) {
checked = true;
if (mozilla::recordreplay::IsRecordingOrReplaying())
return false;
const char* env = getenv("TERM");
if (!env)
return false;

View File

@ -1398,6 +1398,10 @@ ProcessHasSignalHandlers()
return false;
#endif
// Signal handlers are currently disabled when recording or replaying.
if (mozilla::recordreplay::IsRecordingOrReplaying())
return false;
#if defined(ANDROID) && defined(MOZ_LINKER)
// Signal handling is broken on some android systems.
if (IsSignalHandlingBroken())

View File

@ -51,7 +51,7 @@ class AutoMemMap
void reset();
bool initialized() { return addr; }
bool initialized() const { return addr; }
uint32_t size() const { return size_; }

View File

@ -34,6 +34,7 @@ public:
// Pass on ownership of sbp to |global|.
// The type used to cast to void needs to match the one in GetPrivate.
mozilla::RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, sbp);
JS_SetPrivate(global, static_cast<nsIScriptObjectPrincipal*>(sbp.forget().take()));
}

View File

@ -555,6 +555,12 @@ xpc::SimulateActivityCallback(bool aActive)
void
XPCJSContext::ActivityCallback(void* arg, bool active)
{
// Since the slow script dialog never activates if we are recording or
// replaying, don't record/replay JS activity notifications.
if (recordreplay::IsRecordingOrReplaying()) {
return;
}
if (!active) {
ProcessHangMonitor::ClearHang();
}
@ -567,6 +573,12 @@ XPCJSContext::ActivityCallback(void* arg, bool active)
bool
XPCJSContext::InterruptCallback(JSContext* cx)
{
// The slow script dialog never activates if we are recording or replaying,
// since the precise timing of the dialog cannot be replayed.
if (recordreplay::IsRecordingOrReplaying()) {
return true;
}
XPCJSContext* self = XPCJSContext::Get();
// Now is a good time to turn on profiling if it's pending.

View File

@ -140,8 +140,26 @@ public:
return NS_OK;
}
// Dispatch() can be called during a GC, which occur at non-deterministic
// points when recording or replaying. This callback is used with the
// record/replay trigger mechanism to make sure the snow white freer executes
// at a consistent point.
void RecordReplayRun()
{
// Make sure state in the freer is consistent with the recording.
mActive = recordreplay::RecordReplayValue(mActive);
mPurge = recordreplay::RecordReplayValue(mPurge);
mContinuation = recordreplay::RecordReplayValue(mContinuation);
Run();
}
nsresult Dispatch()
{
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::ActivateTrigger(this);
return NS_OK;
}
nsCOMPtr<nsIRunnable> self(this);
return NS_IdleDispatchToCurrentThread(self.forget(), 2500);
}
@ -157,11 +175,28 @@ public:
}
}
// Workaround static analysis.
struct RawSelfPtr { AsyncFreeSnowWhite* mPtr; };
AsyncFreeSnowWhite()
: Runnable("AsyncFreeSnowWhite")
, mContinuation(false)
, mActive(false)
, mPurge(false) {}
, mPurge(false)
{
if (recordreplay::IsRecordingOrReplaying()) {
RawSelfPtr ptr;
ptr.mPtr = this;
recordreplay::RegisterTrigger(this, [=]() { ptr.mPtr->RecordReplayRun(); });
}
}
~AsyncFreeSnowWhite()
{
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::UnregisterTrigger(this);
}
}
public:
bool mContinuation;

View File

@ -486,6 +486,8 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
MOZ_ASSERT(NS_IsMainThread());
mIdentity = aIdentity;
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity);
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
MOZ_ASSERT(mMaybeProto, "bad ctor param");
@ -503,6 +505,8 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
MOZ_ASSERT(NS_IsMainThread());
mIdentity = aIdentity;
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity);
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
MOZ_ASSERT(aScope, "bad ctor param");
@ -529,8 +533,12 @@ XPCWrappedNative::Destroy()
#endif
if (mIdentity) {
// Either release mIdentity immediately or defer the release. When
// recording or replaying the release must always be deferred, so that
// DeferredFinalize matches the earlier call to
// RecordReplayRegisterDeferredFinalizeThing.
XPCJSRuntime* rt = GetRuntime();
if (rt && rt->GetDoingFinalization()) {
if ((rt && rt->GetDoingFinalization()) || recordreplay::IsRecordingOrReplaying()) {
DeferredFinalize(mIdentity.forget().take());
} else {
mIdentity = nullptr;
@ -753,8 +761,10 @@ XPCWrappedNative::FlatJSObjectFinalized()
}
// We also need to release any native pointers held...
// As for XPCWrappedNative::Destroy, when recording or replaying the
// release must always be deferred.
RefPtr<nsISupports> native = to->TakeNative();
if (native && GetRuntime()) {
if (native && (GetRuntime() || recordreplay::IsRecordingOrReplaying())) {
DeferredFinalize(native.forget().take());
}
@ -1054,6 +1064,8 @@ XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
aTearOff->SetInterface(aInterface);
aTearOff->SetNative(qiResult);
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, qiResult);
if (needJSObject && !InitTearOffJSObject(aTearOff))
return NS_ERROR_OUT_OF_MEMORY;

View File

@ -32,6 +32,8 @@ XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
#ifdef DEBUG
gDEBUG_LiveProtoCount++;
#endif
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mClassInfo);
}
XPCWrappedNativeProto::~XPCWrappedNativeProto()
@ -47,6 +49,8 @@ XPCWrappedNativeProto::~XPCWrappedNativeProto()
// Note that our weak ref to mScope is not to be trusted at this point.
XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);
DeferredFinalize(mClassInfo.forget().take());
}
bool

View File

@ -74,7 +74,7 @@ nsStyleSheetService::RegisterFromEnumerator(nsICategoryManager *aManager,
icStr->GetData(name);
nsCString spec;
aManager->GetCategoryEntry(aCategory, name.get(), getter_Copies(spec));
aManager->GetCategoryEntry(nsDependentCString(aCategory), name, spec);
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), spec);

View File

@ -46,6 +46,25 @@ ImageLoader::DropDocumentReference()
mDocument = nullptr;
}
// Normally, arrays of requests and frames are sorted by their pointer address,
// for faster lookup. When recording or replaying, we don't do this, so that
// the arrays retain their insertion order and are consistent between recording
// and replaying.
template <typename Elem, typename Item, typename Comparator = nsDefaultComparator<Elem, Item>>
static size_t
GetMaybeSortedIndex(const nsTArray<Elem>& aArray, const Item& aItem, bool* aFound,
Comparator aComparator = Comparator())
{
if (recordreplay::IsRecordingOrReplaying()) {
size_t index = aArray.IndexOf(aItem, 0, aComparator);
*aFound = index != nsTArray<Elem>::NoIndex;
return *aFound ? index + 1 : aArray.Length();
}
size_t index = aArray.IndexOfFirstElementGt(aItem, aComparator);
*aFound = index > 0 && aComparator.Equals(aItem, aArray.ElementAt(index - 1));
return index;
}
void
ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
nsIFrame* aFrame,
@ -84,8 +103,9 @@ ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
FrameWithFlags* fwfToModify(&fwf);
// See if the frameSet already has this frame.
uint32_t i = frameSet->IndexOfFirstElementGt(fwf, FrameOnlyComparator());
if (i > 0 && aFrame == frameSet->ElementAt(i-1).mFrame) {
bool found;
uint32_t i = GetMaybeSortedIndex(*frameSet, fwf, &found, FrameOnlyComparator());
if (found) {
// We're already tracking this frame, so prepare to modify the
// existing FrameWithFlags object.
fwfToModify = &frameSet->ElementAt(i-1);
@ -149,14 +169,14 @@ ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
DebugOnly<bool> didAddToRequestSet(false);
// If we weren't already tracking this frame, add it to the frameSet.
if (i == 0 || aFrame != frameSet->ElementAt(i-1).mFrame) {
if (!found) {
frameSet->InsertElementAt(i, fwf);
didAddToFrameSet = true;
}
// Add request to the request set if it wasn't already there.
i = requestSet->IndexOfFirstElementGt(aRequest);
if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
i = GetMaybeSortedIndex(*requestSet, aRequest, &found);
if (!found) {
requestSet->InsertElementAt(i, aRequest);
didAddToRequestSet = true;
}
@ -219,10 +239,10 @@ ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest,
MOZ_ASSERT(frameSet, "This should never be null");
// Before we remove aFrame from the frameSet, unblock onload if needed.
uint32_t i = frameSet->IndexOfFirstElementGt(FrameWithFlags(aFrame),
FrameOnlyComparator());
if (i > 0 && aFrame == frameSet->ElementAt(i-1).mFrame) {
bool found;
uint32_t i = GetMaybeSortedIndex(*frameSet, FrameWithFlags(aFrame), &found,
FrameOnlyComparator());
if (found) {
FrameWithFlags& fwf = frameSet->ElementAt(i-1);
if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) {
mDocument->UnblockOnload(false);
@ -249,7 +269,11 @@ ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest,
if (auto entry = mFrameToRequestMap.Lookup(aFrame)) {
RequestSet* requestSet = entry.Data();
MOZ_ASSERT(requestSet, "This should never be null");
requestSet->RemoveElementSorted(aRequest);
if (recordreplay::IsRecordingOrReplaying()) {
requestSet->RemoveElement(aRequest);
} else {
requestSet->RemoveElementSorted(aRequest);
}
if (requestSet->IsEmpty()) {
aFrame->SetHasImageRequest(false);
entry.Remove();

View File

@ -1340,6 +1340,11 @@ PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
PC_AUTO_ENTER_API_CALL(false);
MOZ_ASSERT(aRetval);
// WebRTC is not enabled when recording/replaying. See bug 1304149.
if (recordreplay::IsRecordingOrReplaying()) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<DataChannel> dataChannel;
DataChannelConnection::Type theType =
static_cast<DataChannelConnection::Type>(aType);

View File

@ -1693,7 +1693,8 @@ static Result<Pref*, nsresult>
pref_LookupForModify(const char* aPrefName,
const std::function<bool(const PrefWrapper&)>& aCheckFn)
{
Maybe<PrefWrapper> wrapper = pref_Lookup(aPrefName, /* includeTypeNone */ true);
Maybe<PrefWrapper> wrapper =
pref_Lookup(aPrefName, /* includeTypeNone */ true);
if (wrapper.isNothing()) {
return Err(NS_ERROR_INVALID_ARG);
}
@ -1756,8 +1757,8 @@ pref_SetPref(const char* aPrefName,
bool valueChanged = false;
nsresult rv;
if (aKind == PrefValueKind::Default) {
rv = pref->SetDefaultValue(
aType, aValue, aIsSticky, aIsLocked, &valueChanged);
rv =
pref->SetDefaultValue(aType, aValue, aIsSticky, aIsLocked, &valueChanged);
} else {
MOZ_ASSERT(!aIsLocked); // `locked` is disallowed in user pref files
rv = pref->SetUserValue(aType, aValue, aFromInit, &valueChanged);
@ -2895,6 +2896,8 @@ nsPrefBranch::DeleteBranch(const char* aStartingAt)
nsDependentCString name(pref->Name());
if (StringBeginsWith(name, branchName) || name.Equals(branchNameNoDot)) {
iter.Remove();
// The saved callback pref may be invalid now.
gCallbackPref = nullptr;
}
}
@ -4776,6 +4779,41 @@ pref_ReadPrefFromJar(nsZipArchive* aJarReader, const char* aName)
return NS_OK;
}
// These preference getter wrappers allow us to look up the value for static
// preferences based on their native types, rather than manually mapping them to
// the appropriate Preferences::Get* functions.
template<typename T>
static T
GetPref(const char* aName, T aDefaultValue);
template<>
bool MOZ_MAYBE_UNUSED
GetPref<bool>(const char* aName, bool aDefaultValue)
{
return Preferences::GetBool(aName, aDefaultValue);
}
template<>
int32_t MOZ_MAYBE_UNUSED
GetPref<int32_t>(const char* aName, int32_t aDefaultValue)
{
return Preferences::GetInt(aName, aDefaultValue);
}
template<>
uint32_t MOZ_MAYBE_UNUSED
GetPref<uint32_t>(const char* aName, uint32_t aDefaultValue)
{
return Preferences::GetInt(aName, aDefaultValue);
}
template<>
float MOZ_MAYBE_UNUSED
GetPref<float>(const char* aName, float aDefaultValue)
{
return Preferences::GetFloat(aName, aDefaultValue);
}
// Initialize default preference JavaScript buffers from appropriate TEXT
// resources.
/* static */ Result<Ok, const char*>
@ -4804,6 +4842,21 @@ Preferences::InitInitialObjects(bool aIsStartup)
}
}
#ifdef DEBUG
// Check that all varcache preferences match their current values. This can
// currently fail if the default value of a static varcache preference is
// changed in a preference file or at runtime, rather than in
// StaticPrefList.h.
#define PREF(name, cpp_type, value)
#define VARCACHE_PREF(name, id, cpp_type, value) \
MOZ_ASSERT(GetPref<StripAtomic<cpp_type>>(name, value) == StaticPrefs::id(), \
"Incorrect cached value for " name);
#include "mozilla/StaticPrefList.h"
#undef PREF
#undef VARCACHE_PREF
#endif
return Ok();
}

View File

@ -5840,3 +5840,9 @@ pref("general.document_open_conversion_depth_limit", 20);
// If true, touchstart and touchmove listeners on window, document,
// documentElement and document.body are passive by default.
pref("dom.event.default_to_passive_touch_listeners", true);
// Enable FastBlock?
pref("browser.fastblock.enabled", false);
// The timeout (ms) since navigation start, all tracker connections been made
// after this timeout will be canceled.
pref("browser.fastblock.timeout", 5000);

View File

@ -242,6 +242,7 @@ struct HttpChannelOpenArgs
TimeStamp handleFetchEventStart;
TimeStamp handleFetchEventEnd;
bool forceMainDocumentChannel;
TimeStamp navigationStartTimeStamp;
};
struct HttpChannelConnectArgs

View File

@ -91,7 +91,7 @@ IsNeckoChild()
if (!didCheck) {
didCheck = true;
amChild = (XRE_GetProcessType() == GeckoProcessType_Content);
amChild = (XRE_GetProcessType() == GeckoProcessType_Content) && !recordreplay::IsMiddleman();
}
return amChild;
}

View File

@ -4593,6 +4593,18 @@ HttpBaseChannel::SetLastRedirectFlags(uint32_t aValue)
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
HttpBaseChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
HttpBaseChannel::CheckRedirectLimit(uint32_t aRedirectFlags) const
{

View File

@ -268,6 +268,8 @@ public:
NS_IMETHOD SetIntegrityMetadata(const nsAString& aIntegrityMetadata) override;
NS_IMETHOD GetLastRedirectFlags(uint32_t *aValue) override;
NS_IMETHOD SetLastRedirectFlags(uint32_t aValue) override;
NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) override;
NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
inline void CleanRedirectCacheChainIfNecessary()
{

View File

@ -2690,11 +2690,14 @@ HttpChannelChild::ContinueAsyncOpen()
// This id identifies the inner window's top-level document,
// which changes on every new load or navigation.
uint64_t contentWindowId = 0;
TimeStamp navigationStartTimeStamp;
if (tabChild) {
MOZ_ASSERT(tabChild->WebNavigation());
nsCOMPtr<nsIDocument> document = tabChild->GetDocument();
if (document) {
contentWindowId = document->InnerWindowID();
navigationStartTimeStamp =
document->GetNavigationTiming()->GetNavigationStartTimeStamp();
mTopLevelOuterContentWindowId = document->OuterWindowID();
}
}
@ -2807,6 +2810,8 @@ HttpChannelChild::ContinueAsyncOpen()
openArgs.forceMainDocumentChannel() = mForceMainDocumentChannel;
openArgs.navigationStartTimeStamp() = navigationStartTimeStamp;
// This must happen before the constructor message is sent. Otherwise messages
// from the parent could arrive quickly and be delivered to the wrong event
// target.

View File

@ -154,7 +154,8 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
a.dispatchFetchEventEnd(),
a.handleFetchEventStart(),
a.handleFetchEventEnd(),
a.forceMainDocumentChannel());
a.forceMainDocumentChannel(),
a.navigationStartTimeStamp());
}
case HttpChannelCreationArgs::THttpChannelConnectArgs:
{
@ -454,7 +455,8 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const TimeStamp& aDispatchFetchEventEnd,
const TimeStamp& aHandleFetchEventStart,
const TimeStamp& aHandleFetchEventEnd,
const bool& aForceMainDocumentChannel)
const bool& aForceMainDocumentChannel,
const TimeStamp& aNavigationStartTimeStamp)
{
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
@ -640,6 +642,8 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
httpChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
httpChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
httpChannel->SetNavigationStartTimeStamp(aNavigationStartTimeStamp);
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
do_QueryObject(httpChannel);
nsCOMPtr<nsIApplicationCacheService> appCacheService =

View File

@ -180,7 +180,8 @@ protected:
const TimeStamp& aDispatchFetchEventEnd,
const TimeStamp& aHandleFetchEventStart,
const TimeStamp& aHandleFetchEventEnd,
const bool& aForceMainDocumentChannel);
const bool& aForceMainDocumentChannel,
const TimeStamp& aNavigationStartTimeStamp);
virtual mozilla::ipc::IPCResult RecvSetPriority(const int16_t& priority) override;
virtual mozilla::ipc::IPCResult RecvSetClassOfService(const uint32_t& cos) override;

View File

@ -571,6 +571,12 @@ nsHttpChannel::Connect()
this, isTrackingResource, mClassOfService));
if (isTrackingResource) {
if (CheckFastBlocked()) {
Unused << AsyncAbort(NS_ERROR_ABORT);
CloseCacheEntry(false);
return NS_OK;
}
AddClassFlags(nsIClassOfService::Tail);
}
@ -583,6 +589,39 @@ nsHttpChannel::Connect()
return ConnectOnTailUnblock();
}
bool
nsHttpChannel::CheckFastBlocked()
{
LOG(("nsHttpChannel::CheckFastBlocked [this=%p]\n", this));
static bool sFastBlockInited = false;
static bool sIsFastBlockEnabled = false;
static uint32_t sFastBlockTimeout = 0;
if (!sFastBlockInited) {
sFastBlockInited = true;
Preferences::AddBoolVarCache(&sIsFastBlockEnabled, "browser.fastblock.enabled");
Preferences::AddUintVarCache(&sFastBlockTimeout, "browser.fastblock.timeout");
}
TimeStamp timestamp;
if (NS_FAILED(GetNavigationStartTimeStamp(&timestamp))) {
return false;
}
if (!sIsFastBlockEnabled || !timestamp) {
return false;
}
TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
if (duration.ToMilliseconds() < sFastBlockTimeout) {
return false;
}
LOG(("FastBlock timeout (%lf) [this=%p]\n", duration.ToMilliseconds(), this));
return true;
}
nsresult
nsHttpChannel::ConnectOnTailUnblock()
{
@ -5514,6 +5553,18 @@ nsHttpChannel::SetupReplacementChannel(nsIURI *newURI,
resumableChannel->ResumeAt(mStartPos, mEntityID);
}
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(newChannel, &rv);
if (NS_SUCCEEDED(rv)) {
TimeStamp timestamp;
rv = GetNavigationStartTimeStamp(&timestamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (timestamp) {
Unused << internalChannel->SetNavigationStartTimeStamp(timestamp);
}
}
return NS_OK;
}
@ -6494,6 +6545,23 @@ nsHttpChannel::AttachStreamFilter(ipc::Endpoint<extensions::PStreamFilterParent>
return true;
}
NS_IMETHODIMP
nsHttpChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp)
{
LOG(("nsHttpChannel::GetNavigationStartTimeStamp %p", this));
MOZ_ASSERT(aTimeStamp);
*aTimeStamp = mNavigationStartTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp)
{
LOG(("nsHttpChannel::SetNavigationStartTimeStamp %p", this));
mNavigationStartTimeStamp = aTimeStamp;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsISupportsPriority
//-----------------------------------------------------------------------------

View File

@ -161,6 +161,8 @@ public:
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;
NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) override;
NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
// nsIClassOfService
@ -655,6 +657,9 @@ private:
// Called on untail when tailed because of being a tracking resource.
nsresult ConnectOnTailUnblock();
// Check if current channel should be canceled by FastBlock rules.
bool CheckFastBlocked();
nsCString mUsername;
// If non-null, warnings should be reported to this object.
@ -704,6 +709,8 @@ private:
// the same time.
mozilla::Mutex mRCWNLock;
TimeStamp mNavigationStartTimeStamp;
protected:
virtual void DoNotifyListenerCleanup() override;

View File

@ -9,11 +9,16 @@
#include "nsStringFwd.h"
#include "nsTArrayForwardDeclare.h"
template<class T> class nsCOMArray;
namespace mozilla {
class TimeStamp;
}
%}
[ptr] native StringArray(nsTArray<nsCString>);
[ref] native StringArrayRef(const nsTArray<nsCString>);
[ref] native securityMessagesArray(nsCOMArray<nsISecurityConsoleMessage>);
native TimeStamp(mozilla::TimeStamp);
interface nsIAsyncInputStream;
interface nsIAsyncOutputStream;
interface nsIPrincipal;
@ -324,4 +329,8 @@ interface nsIHttpChannelInternal : nsISupports
*/
[noscript, infallible]
attribute unsigned long lastRedirectFlags;
// This is use to determine the duration since navigation started.
[noscript] attribute TimeStamp navigationStartTimeStamp;
};

View File

@ -40,7 +40,7 @@ fnv = "1.0"
hashglobe = { path = "../hashglobe" }
html5ever = {version = "0.22", optional = true}
itertools = "0.7.6"
itoa = "0.3"
itoa = "0.4"
lazy_static = "1"
log = "0.4"
malloc_size_of = { path = "../malloc_size_of" }
@ -52,7 +52,7 @@ num-integer = "0.1.32"
num-traits = "0.1.32"
new-ordered-float = "1.0"
owning_ref = "0.3.3"
parking_lot = "0.5"
parking_lot = "0.6"
precomputed-hash = "0.1.1"
rayon = "1"
selectors = { path = "../selectors" }

View File

@ -245,9 +245,8 @@ class Longhand(object):
logical_side = s
assert logical_side
physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES else PHYSICAL_SIZES
return [self.name.replace(logical_side, physical_side).replace("inset-", "") \
for physical_side in physical]
return [self.name.replace(logical_side, physical_side).replace("inset-", "")
for physical_side in physical]
def experimental(self, product):
if product == "gecko":

View File

@ -257,32 +257,41 @@ ${helpers.predefined_type(
)}
% else:
// servo versions (no keyword support)
${helpers.predefined_type(size,
"LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto",
"parse_non_negative",
spec=spec % size,
allow_quirks=not logical,
animation_value_type="ComputedValue", logical = logical,
servo_restyle_damage = "reflow")}
${helpers.predefined_type("min-%s" % size,
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(computed::Length::new(0.))",
"parse_non_negative",
spec=spec % ("min-%s" % size),
animation_value_type="ComputedValue",
logical=logical,
allow_quirks=not logical,
servo_restyle_damage = "reflow")}
${helpers.predefined_type("max-%s" % size,
"LengthOrPercentageOrNone",
"computed::LengthOrPercentageOrNone::None",
"parse_non_negative",
spec=spec % ("min-%s" % size),
animation_value_type="ComputedValue",
logical=logical,
allow_quirks=not logical,
servo_restyle_damage = "reflow")}
${helpers.predefined_type(
size,
"LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto",
"parse_non_negative",
spec=spec % size,
logical_group="size",
allow_quirks=not logical,
animation_value_type="ComputedValue", logical = logical,
servo_restyle_damage = "reflow",
)}
${helpers.predefined_type(
"min-%s" % size,
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(computed::Length::new(0.))",
"parse_non_negative",
spec=spec % ("min-%s" % size),
logical_group="min-size",
animation_value_type="ComputedValue",
logical=logical,
allow_quirks=not logical,
servo_restyle_damage = "reflow",
)}
${helpers.predefined_type(
"max-%s" % size,
"LengthOrPercentageOrNone",
"computed::LengthOrPercentageOrNone::None",
"parse_non_negative",
spec=spec % ("max-%s" % size),
logical_group="max-size",
animation_value_type="ComputedValue",
logical=logical,
allow_quirks=not logical,
servo_restyle_damage = "reflow",
)}
% endif
% endfor

View File

@ -2004,7 +2004,8 @@ impl PropertyDeclaration {
% if prop.logical:
% for physical_property in prop.all_physical_mapped_properties():
% if data.longhands_by_name[physical_property].specified_type() != prop.specified_type():
<% raise "Logical property %s should share specified value with physical property %s" % (prop.name, physical_property) %>
<% raise "Logical property %s should share specified value with physical property %s" % \
(prop.name, physical_property) %>
% endif
% endfor
% endif

View File

@ -14,7 +14,7 @@ use element_state::{DocumentState, ElementState};
use fnv::FnvHashMap;
use invalidation::element::document_state::InvalidationMatchingData;
use invalidation::element::element_wrapper::ElementSnapshot;
use properties::{CascadeFlags, ComputedValues, PropertyFlags};
use properties::{ComputedValues, PropertyFlags};
use properties::longhands::display::computed_value::T as Display;
use selector_parser::{AttrValue as SelectorAttrValue, PseudoElementCascadeType, SelectorParser};
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};

Some files were not shown because too many files have changed in this diff Show More