mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
commit
19e302fb18
37
Cargo.lock
generated
37
Cargo.lock
generated
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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 =
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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])
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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 =
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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]
|
||||
|
33
dom/jsurl/test/test_bug1382035-1.html
Normal file
33
dom/jsurl/test/test_bug1382035-1.html
Normal 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>
|
38
dom/jsurl/test/test_bug1382035-2.html
Normal file
38
dom/jsurl/test/test_bug1382035-2.html
Normal 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>
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -1 +1 @@
|
||||
c2c5aaebdd6df22ce13941c1a4b16ef47eaa9f7b
|
||||
e850fbd2e0e60a8de76c2d2464f0fa27316d5949
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -414,6 +414,8 @@ struct HelperThread
|
||||
static void ThreadMain(void* arg);
|
||||
void threadLoop();
|
||||
|
||||
static void WakeupAll();
|
||||
|
||||
void ensureRegisteredWithProfiler();
|
||||
void unregisterWithProfilerIfNeeded();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -51,7 +51,7 @@ class AutoMemMap
|
||||
|
||||
void reset();
|
||||
|
||||
bool initialized() { return addr; }
|
||||
bool initialized() const { return addr; }
|
||||
|
||||
uint32_t size() const { return size_; }
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -242,6 +242,7 @@ struct HttpChannelOpenArgs
|
||||
TimeStamp handleFetchEventStart;
|
||||
TimeStamp handleFetchEventEnd;
|
||||
bool forceMainDocumentChannel;
|
||||
TimeStamp navigationStartTimeStamp;
|
||||
};
|
||||
|
||||
struct HttpChannelConnectArgs
|
||||
|
@ -91,7 +91,7 @@ IsNeckoChild()
|
||||
|
||||
if (!didCheck) {
|
||||
didCheck = true;
|
||||
amChild = (XRE_GetProcessType() == GeckoProcessType_Content);
|
||||
amChild = (XRE_GetProcessType() == GeckoProcessType_Content) && !recordreplay::IsMiddleman();
|
||||
}
|
||||
return amChild;
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
@ -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(×tamp))) {
|
||||
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(×tamp);
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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" }
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user