diff --git a/browser/config/mozconfigs/macosx64/code-coverage b/browser/config/mozconfigs/macosx64/code-coverage new file mode 100644 index 000000000000..9d25aec7310d --- /dev/null +++ b/browser/config/mozconfigs/macosx64/code-coverage @@ -0,0 +1,10 @@ +. "$topsrcdir/browser/config/mozconfigs/macosx64/nightly" + +TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + +ac_add_options --disable-sandbox +ac_add_options --enable-coverage + +export CFLAGS="-coverage" +export CXXFLAGS="-coverage" +export LDFLAGS="-coverage" diff --git a/browser/config/mozconfigs/macosx64/code-coverage-debug b/browser/config/mozconfigs/macosx64/code-coverage-debug new file mode 100644 index 000000000000..7013c4894587 --- /dev/null +++ b/browser/config/mozconfigs/macosx64/code-coverage-debug @@ -0,0 +1,3 @@ +. "$topsrcdir/browser/config/mozconfigs/macosx64/code-coverage" + +ac_add_options --enable-debug diff --git a/dom/base/nsContentList.cpp b/dom/base/nsContentList.cpp index 8d2b1e6ad385..0b101813f893 100644 --- a/dom/base/nsContentList.cpp +++ b/dom/base/nsContentList.cpp @@ -852,7 +852,8 @@ nsContentList::MatchSelf(nsIContent *aContent) } void -nsContentList::PopulateSelf(uint32_t aNeededLength) +nsContentList::PopulateSelf(uint32_t aNeededLength, + uint32_t aExpectedElementsIfDirty) { if (!mRootNode) { return; @@ -861,7 +862,7 @@ nsContentList::PopulateSelf(uint32_t aNeededLength) ASSERT_IN_SYNC; uint32_t count = mElements.Length(); - NS_ASSERTION(mState != LIST_DIRTY || count == 0, + NS_ASSERTION(mState != LIST_DIRTY || count == aExpectedElementsIfDirty, "Reset() not called when setting state to LIST_DIRTY?"); if (count >= aNeededLength) // We're all set @@ -1153,7 +1154,8 @@ nsLabelsNodeList::MaybeResetRoot(nsINode* aRootNode) } void -nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength) +nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength, + uint32_t aExpectedElementsIfDirty) { if (!mRootNode) { return; @@ -1163,7 +1165,8 @@ nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength) nsINode* cur = mRootNode; if (mElements.IsEmpty() && cur->IsElement() && Match(cur->AsElement())) { mElements.AppendElement(cur->AsElement()); + ++aExpectedElementsIfDirty; } - nsContentList::PopulateSelf(aNeededLength); + nsContentList::PopulateSelf(aNeededLength, aExpectedElementsIfDirty); } diff --git a/dom/base/nsContentList.h b/dom/base/nsContentList.h index 542284532c67..c1f5f335b821 100644 --- a/dom/base/nsContentList.h +++ b/dom/base/nsContentList.h @@ -433,8 +433,11 @@ protected: * * @param aNeededLength the length the list should have when we are * done (unless it exhausts the document) + * @param aExpectedElementsIfDirty is for debugging only to + * assert that mElements has expected number of entries. */ - virtual void PopulateSelf(uint32_t aNeededLength); + virtual void PopulateSelf(uint32_t aNeededLength, + uint32_t aExpectedElementsIfDirty = 0); /** * @param aContainer a content node which must be a descendant of @@ -684,7 +687,10 @@ private: * * @param aNeededLength The list of length should have when we are * done (unless it exhausts the document). + * @param aExpectedElementsIfDirty is for debugging only to + * assert that mElements has expected number of entries. */ - void PopulateSelf(uint32_t aNeededLength) override; + void PopulateSelf(uint32_t aNeededLength, + uint32_t aExpectedElementsIfDirty = 0) override; }; #endif // nsContentList_h___ diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 9670d70290f5..c62e3697f87a 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -280,6 +280,15 @@ nsINode::GetParentOrHostNode() const nsINode* nsINode::SubtreeRoot() const { + auto RootOfNode = [](const nsINode* aStart) -> nsINode* { + const nsINode* node = aStart; + const nsINode* iter = node; + while ((iter = iter->GetParentNode())) { + node = iter; + } + return const_cast(node); + }; + // There are four cases of interest here. nsINodes that are really: // 1. nsIDocument nodes - Are always in the document. // 2.a nsIContent nodes not in a shadow tree - Are either in the document, @@ -294,19 +303,18 @@ nsINode::SubtreeRoot() const } else if (IsContent()) { ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); node = containingShadow ? containingShadow : mSubtreeRoot; + if (!node) { + NS_WARNING("Using SubtreeRoot() on unlinked element?"); + node = RootOfNode(this); + } } else { node = mSubtreeRoot; } - NS_ASSERTION(node, "Should always have a node here!"); + MOZ_ASSERT(node, "Should always have a node here!"); #ifdef DEBUG { - const nsINode* slowNode = this; - const nsINode* iter = slowNode; - while ((iter = iter->GetParentNode())) { - slowNode = iter; - } - - NS_ASSERTION(slowNode == node, "These should always be in sync!"); + const nsINode* slowNode = RootOfNode(this); + MOZ_ASSERT(slowNode == node, "These should always be in sync!"); } #endif return node; diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index a28d93325692..3f179477b3bc 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -205,7 +205,21 @@ WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fa UniquePtr& fakeBlackTex = *slot; if (!fakeBlackTex) { + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); + if (IsWebGL2()) { + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, 0); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); + } + fakeBlackTex = FakeBlackTexture::Create(gl, target, fakeBlack); + + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment); + if (IsWebGL2()) { + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, mPixelStore_UnpackSkipPixels); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, mPixelStore_UnpackSkipRows); + gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mPixelStore_UnpackSkipImages); + } if (!fakeBlackTex) { return false; } @@ -995,13 +1009,8 @@ WebGLContext::FakeBlackTexture::Create(gl::GLContext* gl, TexTarget target, gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); - // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to - // minimize the risk of running into a driver bug in texImage2D, as it is a bit - // unusual maybe to create 1x1 textures, and the stack may not have the alignment that - // TexImage2D expects. - const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE}; - UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation. + UniqueBuffer zeros = moz_xcalloc(1, 4); // Infallible allocation. MOZ_ASSERT(gl->IsCurrent()); diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 6e2efe2ef8db..e45483068035 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -473,14 +473,13 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent) } } - // We need to consider a labels element is removed from tree, - // it needs to update labels list and its root as well. + nsStyledElement::UnbindFromTree(aDeep, aNullParent); + + // Invalidate .labels list. It will be repopulated when used the next time. nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); if (slots && slots->mLabelsList) { slots->mLabelsList->MaybeResetRoot(SubtreeRoot()); } - - nsStyledElement::UnbindFromTree(aDeep, aNullParent); } HTMLFormElement* diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index 75f06892206b..01c3999d0334 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -698,12 +698,16 @@ ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& void ImageBridgeChild::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aIdentifier) { + // ImageHost is incompatible between WebRender enabled and WebRender disabled. + // Then drop all ImageContainers' ImageClients during disabling WebRender. bool disablingWebRender = GetCompositorBackendType() == LayersBackend::LAYERS_WR && aIdentifier.mParentBackend != LayersBackend::LAYERS_WR; + // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture, + // drop all ImageContainers' ImageClients. + bool needsDrop = GetCompositorBackendType() == LayersBackend::LAYERS_D3D11 || disablingWebRender; + IdentifyTextureHost(aIdentifier); - if (disablingWebRender) { - // ImageHost is incompatible between WebRender enabled and WebRender disabled. - // Then drop all ImageContainers' ImageClients during disabling WebRender. + if (needsDrop) { nsTArray > listeners; { MutexAutoLock lock(mContainerMapLock); diff --git a/gfx/ots/README.mozilla b/gfx/ots/README.mozilla index 5d610259576d..f958c4d3d9b1 100644 --- a/gfx/ots/README.mozilla +++ b/gfx/ots/README.mozilla @@ -2,7 +2,7 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/. Our reference repository is https://github.com/khaledhosny/ots/. -Current revision: c903692702e888dd9bc3e62a129d01af07320ad8 (6.1.1) +Current revision: 7c8f3fc9cf8b79edb241d94a4847968a5009e78d (7.0.0) Upstream files included: LICENSE, src/, include/, tests/*.cc diff --git a/gfx/ots/src/fvar.h b/gfx/ots/src/fvar.h index 974a3b6940eb..a469c8cddfad 100644 --- a/gfx/ots/src/fvar.h +++ b/gfx/ots/src/fvar.h @@ -23,7 +23,7 @@ class OpenTypeFVAR : public Table { bool Parse(const uint8_t* data, size_t length); bool Serialize(OTSStream* out); - const uint16_t AxisCount() const { return axisCount; } + uint16_t AxisCount() const { return axisCount; } private: uint16_t majorVersion; diff --git a/gfx/ots/src/gvar.cc b/gfx/ots/src/gvar.cc index 45b3972c32f0..324a0fc83874 100644 --- a/gfx/ots/src/gvar.cc +++ b/gfx/ots/src/gvar.cc @@ -154,3 +154,5 @@ bool OpenTypeGVAR::Serialize(OTSStream* out) { } } // namespace ots + +#undef TABLE_NAME diff --git a/gfx/ots/src/hvar.cc b/gfx/ots/src/hvar.cc index d0f286e506f4..845a8835e71c 100644 --- a/gfx/ots/src/hvar.cc +++ b/gfx/ots/src/hvar.cc @@ -85,3 +85,5 @@ bool OpenTypeHVAR::Serialize(OTSStream* out) { } } // namespace ots + +#undef TABLE_NAME diff --git a/gfx/ots/src/mvar.cc b/gfx/ots/src/mvar.cc index f766513f3a81..4ba06c2121d9 100644 --- a/gfx/ots/src/mvar.cc +++ b/gfx/ots/src/mvar.cc @@ -105,3 +105,5 @@ bool OpenTypeMVAR::Serialize(OTSStream* out) { } } // namespace ots + +#undef TABLE_NAME diff --git a/gfx/ots/src/stat.h b/gfx/ots/src/stat.h index 852ddb9590b0..eb16e96adc89 100644 --- a/gfx/ots/src/stat.h +++ b/gfx/ots/src/stat.h @@ -47,7 +47,7 @@ class OpenTypeSTAT : public Table { uint16_t flags; uint16_t valueNameID; Fixed value; - static const size_t Length() { + static size_t Length() { return 3 * sizeof(uint16_t) + sizeof(Fixed); } }; @@ -59,7 +59,7 @@ class OpenTypeSTAT : public Table { Fixed nominalValue; Fixed rangeMinValue; Fixed rangeMaxValue; - static const size_t Length() { + static size_t Length() { return 3 * sizeof(uint16_t) + 3 * sizeof(Fixed); } }; @@ -70,7 +70,7 @@ class OpenTypeSTAT : public Table { uint16_t valueNameID; Fixed value; Fixed linkedValue; - static const size_t Length() { + static size_t Length() { return 3 * sizeof(uint16_t) + 2 * sizeof(Fixed); } }; @@ -84,7 +84,7 @@ class OpenTypeSTAT : public Table { Fixed value; }; std::vector axisValues; - const size_t Length() const { + size_t Length() const { return 3 * sizeof(uint16_t) + axisValues.size() * (sizeof(uint16_t) + sizeof(Fixed)); } }; diff --git a/gfx/ots/src/variations.cc b/gfx/ots/src/variations.cc index ff014665ff93..fa4d39a6f085 100644 --- a/gfx/ots/src/variations.cc +++ b/gfx/ots/src/variations.cc @@ -246,3 +246,5 @@ bool ParseVariationData(const Font* font, const uint8_t* data, size_t length, } } // namespace ots + +#undef TABLE_NAME diff --git a/gfx/ots/src/vvar.cc b/gfx/ots/src/vvar.cc index f1c9ce83d4a6..cb9c9476d56c 100644 --- a/gfx/ots/src/vvar.cc +++ b/gfx/ots/src/vvar.cc @@ -95,3 +95,5 @@ bool OpenTypeVVAR::Serialize(OTSStream* out) { } } // namespace ots + +#undef TABLE_NAME diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 50312d3bb820..2172f2721752 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -6965,10 +6965,13 @@ GCRuntime::resetIncrementalGC(gc::AbortReason reason, AutoTraceSession& session) namespace { -class AutoGCSlice { +/* + * Temporarily disable barriers during GC slices. + */ +class AutoDisableBarriers { public: - explicit AutoGCSlice(JSRuntime* rt); - ~AutoGCSlice(); + explicit AutoDisableBarriers(JSRuntime* rt); + ~AutoDisableBarriers(); private: JSRuntime* runtime; @@ -6977,7 +6980,7 @@ class AutoGCSlice { } /* anonymous namespace */ -AutoGCSlice::AutoGCSlice(JSRuntime* rt) +AutoDisableBarriers::AutoDisableBarriers(JSRuntime* rt) : runtime(rt) { for (GCZonesIter zone(rt); !zone.done(); zone.next()) { @@ -6985,7 +6988,7 @@ AutoGCSlice::AutoGCSlice(JSRuntime* rt) * Clear needsIncrementalBarrier early so we don't do any write * barriers during GC. We don't need to update the Ion barriers (which * is expensive) because Ion code doesn't run during GC. If need be, - * we'll update the Ion barriers in ~AutoGCSlice. + * we'll update the Ion barriers in ~AutoDisableBarriers. */ if (zone->isGCMarking()) { MOZ_ASSERT(zone->needsIncrementalBarrier()); @@ -6995,7 +6998,7 @@ AutoGCSlice::AutoGCSlice(JSRuntime* rt) } } -AutoGCSlice::~AutoGCSlice() +AutoDisableBarriers::~AutoDisableBarriers() { /* We can't use GCZonesIter if this is the end of the last slice. */ for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { @@ -7052,7 +7055,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea if (isIncrementalGCInProgress() && !atomsZone->isCollecting()) session.maybeLock.reset(); - AutoGCSlice slice(rt); + AutoDisableBarriers disableBarriers(rt); bool destroyingRuntime = (reason == JS::gcreason::DESTROY_RUNTIME); @@ -7069,6 +7072,14 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea bool useZeal = false; #endif +#ifdef DEBUG + { + char budgetBuffer[32]; + budget.describe(budgetBuffer, 32); + stats().writeLogMessage("Incremental: %d, useZeal: %d, budget: %s", + bool(isIncremental), bool(useZeal), budgetBuffer); + } +#endif MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental); if (isIncrementalGCInProgress() && budget.isUnlimited()) changeToNonIncrementalGC(); @@ -7080,6 +7091,8 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea * Yields between slices occurs at predetermined points in these modes; * the budget is not used. */ + stats().writeLogMessage( + "Using unlimited budget for two-slice zeal mode"); budget.makeUnlimited(); } @@ -7127,7 +7140,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea MOZ_ASSERT(marker.isDrained()); /* - * In incremental GCs where we have already performed more than once + * In incremental GCs where we have already performed more than one * slice we yield after marking with the aim of starting the sweep in * the next slice, since the first slice of sweeping can be expensive. * @@ -7144,6 +7157,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea (useZeal && hasZealMode(ZealMode::YieldBeforeSweeping)))) { lastMarkSlice = true; + stats().writeLogMessage("Yeilding before starting sweeping"); break; } @@ -7619,6 +7633,9 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R if (!checkIfGCAllowedInCurrentState(reason)) return; + stats().writeLogMessage("GC starting in state %s", + StateName(incrementalState)); + AutoTraceLog logGC(TraceLoggerForCurrentThread(), TraceLogger_GC); AutoStopVerifyingBarriers av(rt, IsShutdownGC(reason)); AutoEnqueuePendingParseTasksAfterGC aept(*this); @@ -7630,6 +7647,7 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R if (reason == JS::gcreason::ABORT_GC) { MOZ_ASSERT(!isIncrementalGCInProgress()); + stats().writeLogMessage("GC aborted by request"); break; } @@ -7669,6 +7687,7 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R MOZ_RELEASE_ASSERT(CheckGrayMarkingState(rt)); } #endif + stats().writeLogMessage("GC ending"); } js::AutoEnqueuePendingParseTasksAfterGC::~AutoEnqueuePendingParseTasksAfterGC() diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 650f5abafdaf..bb8a9501fdd7 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -97,6 +97,30 @@ js::gcstats::ExplainAbortReason(gc::AbortReason reason) } } +static FILE* +MaybeOpenFileFromEnv(const char* env) +{ + FILE *file; + const char* value = getenv(env); + + if (!value) + return nullptr; + + if (strcmp(value, "none") == 0) { + file = nullptr; + } else if (strcmp(value, "stdout") == 0) { + file = stdout; + } else if (strcmp(value, "stderr") == 0) { + file = stderr; + } else { + file = fopen(value, "a"); + if (!file) + MOZ_CRASH("Failed to open log file."); + } + + return file; +} + struct PhaseKindInfo { Phase firstPhase; @@ -551,6 +575,24 @@ Statistics::renderNurseryJson(JSRuntime* rt) const return UniqueChars(printer.release()); } +#ifdef DEBUG +void +Statistics::writeLogMessage(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (gcDebugFile) { + TimeDuration sinceStart = TimeStamp::Now() - + TimeStamp::ProcessCreation(); + fprintf(gcDebugFile, "%12.3f: ", sinceStart.ToMicroseconds()); + vfprintf(gcDebugFile, fmt, args); + fprintf(gcDebugFile, "\n"); + fflush(gcDebugFile); + } + va_end(args); +} +#endif + UniqueChars Statistics::renderJsonMessage(uint64_t timestamp, bool includeSlices) const { @@ -695,7 +737,8 @@ Statistics::formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes, JSONPrinter& Statistics::Statistics(JSRuntime* rt) : runtime(rt), - fp(nullptr), + gcTimerFile(nullptr), + gcDebugFile(nullptr), nonincrementalReason_(gc::AbortReason::None), preBytes(0), thresholdTriggered(false), @@ -734,22 +777,10 @@ Statistics::Statistics(JSRuntime* rt) MOZ_ALWAYS_TRUE(phaseStack.reserve(MAX_PHASE_NESTING)); MOZ_ALWAYS_TRUE(suspendedPhases.reserve(MAX_SUSPENDED_PHASES)); - const char* env = getenv("MOZ_GCTIMER"); - if (env) { - if (strcmp(env, "none") == 0) { - fp = nullptr; - } else if (strcmp(env, "stdout") == 0) { - fp = stdout; - } else if (strcmp(env, "stderr") == 0) { - fp = stderr; - } else { - fp = fopen(env, "a"); - if (!fp) - MOZ_CRASH("Failed to open MOZ_GCTIMER log file."); - } - } + gcTimerFile = MaybeOpenFileFromEnv("MOZ_GCTIMER"); + gcDebugFile = MaybeOpenFileFromEnv("JS_GC_DEBUG"); - env = getenv("JS_GC_PROFILE"); + const char* env = getenv("JS_GC_PROFILE"); if (env) { if (0 == strcmp(env, "help")) { fprintf(stderr, "JS_GC_PROFILE=N\n" @@ -763,8 +794,10 @@ Statistics::Statistics(JSRuntime* rt) Statistics::~Statistics() { - if (fp && fp != stdout && fp != stderr) - fclose(fp); + if (gcTimerFile && gcTimerFile != stdout && gcTimerFile != stderr) + fclose(gcTimerFile); + if (gcDebugFile && gcDebugFile != stdout && gcDebugFile != stderr) + fclose(gcDebugFile); } /* static */ bool @@ -918,16 +951,16 @@ void Statistics::printStats() { if (aborted) { - fprintf(fp, "OOM during GC statistics collection. The report is unavailable for this GC.\n"); + fprintf(gcTimerFile, "OOM during GC statistics collection. The report is unavailable for this GC.\n"); } else { UniqueChars msg = formatDetailedMessage(); if (msg) { double secSinceStart = (slices_[0].start - TimeStamp::ProcessCreation()).ToSeconds(); - fprintf(fp, "GC(T+%.3fs) %s\n", secSinceStart, msg.get()); + fprintf(gcTimerFile, "GC(T+%.3fs) %s\n", secSinceStart, msg.get()); } } - fflush(fp); + fflush(gcTimerFile); } void @@ -1035,6 +1068,8 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, (*sliceCallback)(cx, JS::GC_CYCLE_BEGIN, desc); (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc); } + + writeLogMessage("begin slice"); } void @@ -1049,6 +1084,7 @@ Statistics::endSlice() slice.endFaults = GetPageFaultCount(); slice.finalState = runtime->gc.state(); + writeLogMessage("end slice"); TimeDuration sliceTime = slice.end - slice.start; runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime)); runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slice.wasReset()); @@ -1086,7 +1122,7 @@ Statistics::endSlice() bool last = !runtime->gc.isIncrementalGCInProgress(); if (last) { - if (fp) + if (gcTimerFile) printStats(); if (!aborted) @@ -1250,6 +1286,7 @@ Statistics::recordPhaseBegin(Phase phase) phaseStack.infallibleAppend(phase); phaseStartTimes[phase] = now; + writeLogMessage("begin: %s", phases[phase].path); } void @@ -1299,6 +1336,7 @@ Statistics::recordPhaseEnd(Phase phase) #ifdef DEBUG phaseEndTimes[phase] = now; + writeLogMessage("end: %s", phases[phase].path); #endif } diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 3364e190051f..23ddefc5e9f6 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -166,6 +166,8 @@ struct Statistics void nonincremental(gc::AbortReason reason) { MOZ_ASSERT(reason != gc::AbortReason::None); nonincrementalReason_ = reason; + writeLogMessage("Non-incremental reason: %s", + nonincrementalReason()); } bool nonincremental() const { @@ -266,11 +268,21 @@ struct Statistics // Return JSON for the previous nursery collection. UniqueChars renderNurseryJson(JSRuntime* rt) const; +#ifdef DEBUG + // Print a logging message. + void writeLogMessage(const char* fmt, ...); +#else + void writeLogMessage(const char* fmt, ...) { }; +#endif + private: JSRuntime* runtime; - /* File pointer used for MOZ_GCTIMER output. */ - FILE* fp; + /* File used for MOZ_GCTIMER output. */ + FILE* gcTimerFile; + + /* File used for JS_GC_DEBUG output. */ + FILE* gcDebugFile; ZoneGCStats zoneStats; diff --git a/js/src/jit-test/tests/wasm/memory-bulk.js b/js/src/jit-test/tests/wasm/memory-bulk.js index 7dcddadd38ea..18d0e8b06c61 100644 --- a/js/src/jit-test/tests/wasm/memory-bulk.js +++ b/js/src/jit-test/tests/wasm/memory-bulk.js @@ -113,13 +113,84 @@ function checkMiscPrefixed(opcode, expect_failure) { } //----------------------------------------------------------- -// Verification cases for memory.copy/fill +// Verification cases for memory.copy/fill opcode encodings checkMiscPrefixed(0x3f, true); // unassigned checkMiscPrefixed(0x40, false); // memory.copy checkMiscPrefixed(0x41, false); // memory.fill checkMiscPrefixed(0x42, true); // unassigned +//----------------------------------------------------------- +// Verification cases for memory.copy/fill arguments + +// Invalid argument types +{ + const tys = ['i32', 'f32', 'i64', 'f64']; + const ops = ['copy', 'fill']; + for (let ty1 of tys) { + for (let ty2 of tys) { + for (let ty3 of tys) { + for (let op of ops) { + if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32') + continue; // this is the only valid case + let text = + `(module + (memory (export "memory") 1 1) + (func (export "testfn") + (memory.${op} (${ty1}.const 10) (${ty2}.const 20) (${ty3}.const 30)) + ) + )`; + assertErrorMessage(() => wasmEvalText(text), + WebAssembly.CompileError, /type mismatch/); + }}}} +} + +// Not enough, or too many, args +{ + for (let op of ['copy', 'fill']) { + let text1 = + `(module + (memory (export "memory") 1 1) + (func (export "testfn") + (i32.const 10) + (i32.const 20) + memory.${op} + ) + )`; + assertErrorMessage(() => wasmEvalText(text1), + WebAssembly.CompileError, + /popping value from empty stack/); + let text2 = + `(module + (memory (export "memory") 1 1) + (func (export "testfn") + (i32.const 10) + (i32.const 20) + (i32.const 30) + (i32.const 40) + memory.${op} + ) + )`; + assertErrorMessage(() => wasmEvalText(text2), + WebAssembly.CompileError, + /unused values not explicitly dropped by end of block/); + } +} + +// Module doesn't have a memory +{ + for (let op of ['copy', 'fill']) { + let text = + `(module + (func (export "testfn") + (memory.${op} (i32.const 10) (i32.const 20) (i32.const 30)) + ) + )`; + assertErrorMessage(() => wasmEvalText(text), + WebAssembly.CompileError, + /can't touch memory without memory/); + } +} //---------------------------------------------------------------------// //---------------------------------------------------------------------// @@ -139,14 +210,14 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Range valid { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 256)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0x00000, 0x0FF00, 0x00); @@ -155,42 +226,42 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Range invalid { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 257)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Wraparound the end of 32-bit offset space { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.fill (i32.const 0xFFFFFF00) (i32.const 0x55) (i32.const 257)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Zero len with offset in-bounds is a no-op { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 0)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0x00000, 0x10000, 0x00); @@ -198,28 +269,28 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Zero len with offset out-of-bounds gets an exception { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Very large range { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.fill (i32.const 0x1) (i32.const 0xAA) (i32.const 0xFFFE)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0x00000, 0x00001, 0x00); @@ -229,7 +300,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Sequencing { - let i = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let i = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (result i32) @@ -238,7 +309,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) i32.const 99 ) )` - ))); + ); i.exports.testfn(); let b = new Uint8Array(i.exports.memory.buffer); checkRange(b, 0x0, 0x12+0, 0x00); @@ -255,7 +326,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Both ranges valid. Copy 5 bytes backwards by 1 (overlapping). // result = 0x00--(09) 0x55--(11) 0x00--(pagesize-20) { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") @@ -263,7 +334,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) (memory.copy (i32.const 9) (i32.const 10) (i32.const 5)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0, 0+9, 0x00); @@ -274,7 +345,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Both ranges valid. Copy 5 bytes forwards by 1 (overlapping). // result = 0x00--(10) 0x55--(11) 0x00--(pagesize-19) { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") @@ -282,7 +353,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) (memory.copy (i32.const 16) (i32.const 15) (i32.const 5)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0, 0+10, 0x00); @@ -292,63 +363,63 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Destination range invalid { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.copy (i32.const 0xFF00) (i32.const 0x8000) (i32.const 257)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Destination wraparound the end of 32-bit offset space { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.copy (i32.const 0xFFFFFF00) (i32.const 0x4000) (i32.const 257)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Source range invalid { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.copy (i32.const 0x8000) (i32.const 0xFF00) (i32.const 257)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Source wraparound the end of 32-bit offset space { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.copy (i32.const 0x4000) (i32.const 0xFFFFFF00) (i32.const 257)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Zero len with both offsets in-bounds is a no-op { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") @@ -357,7 +428,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) (memory.copy (i32.const 0x9000) (i32.const 0x7000) (i32.const 0)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0x00000, 0x08000, 0x55); @@ -366,28 +437,28 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // Zero len with dest offset out-of-bounds is an exception { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } // Zero len with src offset out-of-bounds is an exception { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") (memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0)) ) )` - ))); + ); assertErrorMessage(() => inst.exports.testfn(), WebAssembly.RuntimeError, /index out of bounds/); } @@ -395,7 +466,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) // 100 random fills followed by 100 random copies, in a single-page buffer, // followed by verification of the (now heavily mashed-around) buffer. { - let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + let inst = wasmEvalText( `(module (memory (export "memory") 1 1) (func (export "testfn") @@ -601,7 +672,7 @@ function checkRange(arr, minIx, maxIxPlusOne, expectedValue) (memory.copy (i32.const 50370) (i32.const 41271) (i32.const 1406)) ) )` - ))); + ); inst.exports.testfn(); let b = new Uint8Array(inst.exports.memory.buffer); checkRange(b, 0, 124, 0); diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index a6f8cf60401a..efc6ea8f5770 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -4409,6 +4409,16 @@ nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(nsDisplayListBuilde , mStyleFrame(aCellFrame) , mTableType(GetTableTypeFromFrame(mStyleFrame)) { + if (aBuilder->IsRetainingDisplayList()) { + mStyleFrame->AddDisplayItem(this); + } +} + +nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() +{ + if (mStyleFrame) { + mStyleFrame->RemoveDisplayItem(this); + } } bool @@ -7461,6 +7471,9 @@ nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(nsDisplayListBuilder* a , mAncestorFrame(aAncestorFrame) , mTableType(GetTableTypeFromFrame(aAncestorFrame)) { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } } /* static */ nsDisplayTableFixedPosition* diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index b9753974e3ad..57a8524aa460 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -4226,6 +4226,7 @@ TableType GetTableTypeFromFrame(nsIFrame* aFrame); class nsDisplayTableBackgroundImage : public nsDisplayBackgroundImage { public: nsDisplayTableBackgroundImage(nsDisplayListBuilder* aBuilder, const InitData& aInitData, nsIFrame* aCellFrame); + ~nsDisplayTableBackgroundImage(); virtual uint32_t GetPerFrameKey() const override { return (mLayer << (TYPE_BITS + static_cast(TableTypeBits::COUNT))) | @@ -4237,6 +4238,17 @@ public: virtual nsIFrame* FrameForInvalidation() const override { return mStyleFrame; } + virtual bool HasDeletedFrame() const override { + return !mStyleFrame || nsDisplayBackgroundImage::HasDeletedFrame(); + } + + virtual void RemoveFrame(nsIFrame* aFrame) override { + if (aFrame == mStyleFrame) { + mStyleFrame = nullptr; + } + nsDisplayBackgroundImage::RemoveFrame(aFrame); + } + NS_DISPLAY_DECL_NAME("TableBackgroundImage", TYPE_TABLE_BACKGROUND_IMAGE) protected: virtual nsIFrame* StyleFrame() const override { return mStyleFrame; } @@ -4324,7 +4336,16 @@ public: : nsDisplayThemedBackground(aBuilder, aFrame, aBackgroundRect) , mAncestorFrame(aAncestorFrame) , mTableType(GetTableTypeFromFrame(aAncestorFrame)) - { } + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } + ~nsDisplayTableThemedBackground() { + if (mAncestorFrame) { + mAncestorFrame->RemoveDisplayItem(this); + } + } virtual uint32_t GetPerFrameKey() const override { return (static_cast(mTableType) << TYPE_BITS) | @@ -4333,6 +4354,17 @@ public: virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } + virtual bool HasDeletedFrame() const override { + return !mAncestorFrame || nsDisplayThemedBackground::HasDeletedFrame(); + } + + virtual void RemoveFrame(nsIFrame* aFrame) override { + if (aFrame == mAncestorFrame) { + mAncestorFrame = nullptr; + } + nsDisplayThemedBackground::RemoveFrame(aFrame); + } + NS_DISPLAY_DECL_NAME("TableThemedBackground", TYPE_TABLE_THEMED_BACKGROUND_IMAGE) protected: virtual nsIFrame* StyleFrame() const override { return mAncestorFrame; } @@ -4467,10 +4499,30 @@ public: : nsDisplayBackgroundColor(aBuilder, aFrame, aBackgroundRect, aBackgroundStyle, aColor) , mAncestorFrame(aAncestorFrame) , mTableType(GetTableTypeFromFrame(aAncestorFrame)) - { } + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } + ~nsDisplayTableBackgroundColor() { + if (mAncestorFrame) { + mAncestorFrame->RemoveDisplayItem(this); + } + } virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } + virtual bool HasDeletedFrame() const override { + return !mAncestorFrame || nsDisplayBackgroundColor::HasDeletedFrame(); + } + + virtual void RemoveFrame(nsIFrame* aFrame) override { + if (aFrame == mAncestorFrame) { + mAncestorFrame = nullptr; + } + nsDisplayBackgroundColor::RemoveFrame(aFrame); + } + virtual uint32_t GetPerFrameKey() const override { return (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); @@ -5459,14 +5511,27 @@ public: : nsDisplayBlendMode(aBuilder, aFrame, aList, aBlendMode, aActiveScrolledRoot, aIndex) , mAncestorFrame(aAncestorFrame) , mTableType(GetTableTypeFromFrame(aAncestorFrame)) - {} + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder, const nsDisplayTableBlendMode& aOther) : nsDisplayBlendMode(aBuilder, aOther) , mAncestorFrame(aOther.mAncestorFrame) , mTableType(aOther.mTableType) - {} + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } + ~nsDisplayTableBlendMode() { + if (mAncestorFrame) { + mAncestorFrame->RemoveDisplayItem(this); + } + } virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override { @@ -5475,6 +5540,17 @@ public: virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } + virtual bool HasDeletedFrame() const override { + return !mAncestorFrame || nsDisplayBlendMode::HasDeletedFrame(); + } + + virtual void RemoveFrame(nsIFrame* aFrame) override { + if (aFrame == mAncestorFrame) { + mAncestorFrame = nullptr; + } + nsDisplayBlendMode::RemoveFrame(aFrame); + } + virtual uint32_t GetPerFrameKey() const override { return (mIndex << (TYPE_BITS + static_cast(TableTypeBits::COUNT))) | (static_cast(mTableType) << TYPE_BITS) | @@ -5571,6 +5647,17 @@ public: virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } + virtual bool HasDeletedFrame() const override { + return !mAncestorFrame || nsDisplayBlendContainer::HasDeletedFrame(); + } + + virtual void RemoveFrame(nsIFrame* aFrame) override { + if (aFrame == mAncestorFrame) { + mAncestorFrame = nullptr; + } + nsDisplayBlendContainer::RemoveFrame(aFrame); + } + virtual uint32_t GetPerFrameKey() const override { return (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); @@ -5586,14 +5673,27 @@ protected: : nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot, aIsForBackground) , mAncestorFrame(aAncestorFrame) , mTableType(GetTableTypeFromFrame(aAncestorFrame)) - {} + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder, const nsDisplayTableBlendContainer& aOther) : nsDisplayBlendContainer(aBuilder, aOther) , mAncestorFrame(aOther.mAncestorFrame) , mTableType(aOther.mTableType) - {} + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } + ~nsDisplayTableBlendContainer() { + if (mAncestorFrame) { + mAncestorFrame->RemoveDisplayItem(this); + } + } nsIFrame* mAncestorFrame; TableType mTableType; @@ -5939,6 +6039,17 @@ public: virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } + virtual bool HasDeletedFrame() const override { + return !mAncestorFrame || nsDisplayFixedPosition::HasDeletedFrame(); + } + + virtual void RemoveFrame(nsIFrame* aFrame) override { + if (aFrame == mAncestorFrame) { + mAncestorFrame = nullptr; + } + nsDisplayFixedPosition::RemoveFrame(aFrame); + } + virtual uint32_t GetPerFrameKey() const override { return (mIndex << (TYPE_BITS + static_cast(TableTypeBits::COUNT))) | (static_cast(mTableType) << TYPE_BITS) | @@ -5956,7 +6067,16 @@ protected: : nsDisplayFixedPosition(aBuilder, aOther) , mAncestorFrame(aOther.mAncestorFrame) , mTableType(aOther.mTableType) - {} + { + if (aBuilder->IsRetainingDisplayList()) { + mAncestorFrame->AddDisplayItem(this); + } + } + ~nsDisplayTableFixedPosition() { + if (mAncestorFrame) { + mAncestorFrame->RemoveDisplayItem(this); + } + } nsIFrame* mAncestorFrame; TableType mTableType; diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index a86e1171ea08..f81d7613fef4 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1196,9 +1196,9 @@ fuzzy-if(webrender,4,361) == 449519-1.html 449519-1-ref.html == 455280-1.xhtml 455280-1-ref.xhtml == 455826-1.html 455826-1-ref.html fails-if(Android||cocoaWidget||winWidget) == 456147.xul 456147-ref.html # bug 458047 -fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,450-497) == 456219-1a.html 456219-1-ref.html # bug 1128229 -fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,450-497) == 456219-1b.html 456219-1-ref.html # bug 1128229 -fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,450-497) == 456219-1c.html 456219-1-ref.html # bug 1128229 +fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,449-497) == 456219-1a.html 456219-1-ref.html # bug 1128229 +fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,449-497) == 456219-1b.html 456219-1-ref.html # bug 1128229 +fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,449-497) == 456219-1c.html 456219-1-ref.html # bug 1128229 fuzzy-if(skiaContent,1,45) fuzzy-if(webrender,9-9,8-8) == 456219-2.html 456219-2-ref.html == 456330-1.gif 456330-1-ref.png == 456484-1.html 456484-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001-ref.html index 919800cb8925..c7553716ab6c 100644 --- a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001-ref.html +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001-ref.html @@ -1,62 +1,62 @@ - - - - - CSS Reftest Reference - - - - -
-

-
- -
-
-

-
- -
-
-
- -
-

-
- - \ No newline at end of file + + + + + CSS Reftest Reference + + + + +
+

+
+ +
+
+

+
+ +
+
+
+ +
+

+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001a.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001a.html index f17b7adb37db..71102b6c73a3 100644 --- a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001a.html +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001a.html @@ -1,66 +1,66 @@ - - - - - CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children. - - - - - - - - -
-

-
- -
-
-

-
- -
-
-
- -
-

-
- - \ No newline at end of file + + + + + CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children. + + + + + + + + +
+

+
+ +
+
+

+
+ +
+
+
+ +
+

+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001b.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001b.html index 6720fc440552..0c4d3323bf7c 100644 --- a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001b.html +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001b.html @@ -1,66 +1,66 @@ - - - - - CSS Test: 'will-change: contain' with stacking contents. Z-index is defined only for siblings and children. - - - - - - - - -
-

-
- -
-
-

-
- -
-
-
- -
-

-
- - \ No newline at end of file + + + + + CSS Test: 'will-change: contain' with stacking contents. Z-index is defined only for siblings and children. + + + + + + + + +
+

+
+ +
+
+

+
+ +
+
+
+ +
+

+
+ + diff --git a/mozglue/misc/interceptor/VMSharingPolicies.h b/mozglue/misc/interceptor/VMSharingPolicies.h index 05bd391c799b..bf3b1f72fa92 100644 --- a/mozglue/misc/interceptor/VMSharingPolicies.h +++ b/mozglue/misc/interceptor/VMSharingPolicies.h @@ -84,67 +84,20 @@ private: }; template -class VMSharingPolicyShared : public MMPolicyBase +class VMSharingPolicyShared; + +// We only support this policy for in-proc MMPolicy +template +class VMSharingPolicyShared : public MMPolicyBase { - typedef VMSharingPolicyUnique ValueT; - - // We use pid instead of HANDLE for mapping, since more than one handle may - // map to the same pid. We don't worry about pid reuse becuase each mVMPolicy - // holds an open handle to pid, thus keeping the pid reserved at least for the - // lifetime of mVMPolicy. - struct ProcMapEntry - { - ProcMapEntry() - : mPid(::GetCurrentProcessId()) - { - } - - explicit ProcMapEntry(HANDLE aProc) - : mPid(::GetProcessId(aProc)) - , mVMPolicy(aProc) - { - } - - ProcMapEntry(ProcMapEntry&& aOther) - : mPid(aOther.mPid) - , mVMPolicy(Move(aOther.mVMPolicy)) - { - aOther.mPid = 0; - } - - ProcMapEntry(const ProcMapEntry&) = delete; - ProcMapEntry& operator=(const ProcMapEntry&) = delete; - - ProcMapEntry& operator=(ProcMapEntry&& aOther) - { - mPid = aOther.mPid; - mVMPolicy = Move(aOther.mVMPolicy); - aOther.mPid = 0; - return *this; - } - - bool operator==(DWORD aPid) const - { - return mPid == aPid; - } - - DWORD mPid; - ValueT mVMPolicy; - }; - - // We normally expect to reference only one other process at a time, but this - // is not a requirement. - typedef Vector MapT; + typedef VMSharingPolicyUnique UniquePolicyT; public: - typedef MMPolicy MMPolicyT; + typedef MMPolicyInProcess MMPolicyT; - template - explicit VMSharingPolicyShared(Args... aArgs) - : mPid(GetPid(aArgs...)) + VMSharingPolicyShared() { static const bool isAlloc = []() -> bool { - sPerProcVM = new MapT(); DWORD flags = 0; #if defined(RELEASE_OR_BETA) flags |= CRITICAL_SECTION_NO_DEBUG_INFO; @@ -152,101 +105,48 @@ public: ::InitializeCriticalSectionEx(&sCS, 4000, flags); return true; }(); - - MOZ_ASSERT(mPid); - if (!mPid) { - return; - } - - AutoCriticalSection lock(&sCS); - - if (find(mPid)) { - return; - } - - bool appended = sPerProcVM->append(ProcMapEntry(aArgs...)); - MOZ_RELEASE_ASSERT(appended); } explicit operator bool() const { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - bool found = find(mPid, &entry); - MOZ_RELEASE_ASSERT(found); - - return !!entry->mVMPolicy; + return !!sUniqueVM; } - operator const MMPolicy&() const + operator const MMPolicyInProcess&() const { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - bool found = find(mPid, &entry); - MOZ_RELEASE_ASSERT(found); - - return entry->mVMPolicy; + return sUniqueVM; } bool ShouldUnhookUponDestruction() const { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - if (!find(mPid, &entry)) { - return 0; - } - - return entry->mVMPolicy.ShouldUnhookUponDestruction(); + return sUniqueVM.ShouldUnhookUponDestruction(); } bool Reserve(uint32_t aCount) { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - if (!find(mPid, &entry)) { - return false; - } - - return entry->mVMPolicy.Reserve(aCount); + return sUniqueVM.Reserve(aCount); } bool IsPageAccessible(void* aVAddress) const { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - if (!find(mPid, &entry)) { - return false; - } - - return entry->mVMPolicy.IsPageAccessible(aVAddress); + return sUniqueVM.IsPageAccessible(aVAddress); } - Trampoline GetNextTrampoline() + Trampoline GetNextTrampoline() { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - if (!find(mPid, &entry)) { - return nullptr; - } - - return entry->mVMPolicy.GetNextTrampoline(); + return sUniqueVM.GetNextTrampoline(); } - TrampolineCollection Items() const + TrampolineCollection Items() const { AutoCriticalSection lock(&sCS); - - ProcMapEntry* entry; - bool found = find(mPid, &entry); - MOZ_RELEASE_ASSERT(found); - - TrampolineCollection items(Move(entry->mVMPolicy.Items())); + TrampolineCollection items(Move(sUniqueVM.Items())); // We need to continue holding the lock until items is destroyed. items.Lock(sCS); @@ -268,43 +168,16 @@ public: VMSharingPolicyShared& operator=(VMSharingPolicyShared&&) = delete; private: - static bool find(DWORD aPid, ProcMapEntry** aOutEntry = nullptr) - { - MOZ_ASSERT(sPerProcVM); - if (!sPerProcVM) { - return false; - } - - if (aOutEntry) { - *aOutEntry = nullptr; - } - - for (auto&& mapping : *sPerProcVM) { - if (mapping == aPid) { - if (aOutEntry) { - *aOutEntry = &mapping; - } - return true; - } - } - - return false; - } - - static DWORD GetPid() { return ::GetCurrentProcessId(); } - static DWORD GetPid(HANDLE aHandle) { return ::GetProcessId(aHandle); } - - DWORD mPid; - static MapT* sPerProcVM; + static UniquePolicyT sUniqueVM; static CRITICAL_SECTION sCS; }; -template -typename VMSharingPolicyShared::MapT * - VMSharingPolicyShared::sPerProcVM; +template +typename VMSharingPolicyShared::UniquePolicyT + VMSharingPolicyShared::sUniqueVM; -template -CRITICAL_SECTION VMSharingPolicyShared::sCS; +template +CRITICAL_SECTION VMSharingPolicyShared::sCS; } // namespace interceptor } // namespace mozilla diff --git a/netwerk/base/nsBufferedStreams.cpp b/netwerk/base/nsBufferedStreams.cpp index 2d1417563a45..863e8b9ae569 100644 --- a/netwerk/base/nsBufferedStreams.cpp +++ b/netwerk/base/nsBufferedStreams.cpp @@ -140,7 +140,7 @@ nsBufferedStream::Seek(int32_t whence, int64_t offset) nsCOMPtr ras = do_QueryInterface(mStream, &rv); if (NS_FAILED(rv)) { #ifdef DEBUG - NS_ERROR("mStream doesn't QI to nsISeekableStream"); + NS_WARNING("mStream doesn't QI to nsISeekableStream"); #endif return rv; } diff --git a/taskcluster/ci/build/macosx.yml b/taskcluster/ci/build/macosx.yml index c82117926969..cc8248fc6b83 100644 --- a/taskcluster/ci/build/macosx.yml +++ b/taskcluster/ci/build/macosx.yml @@ -277,3 +277,37 @@ macosx64-nightly/opt: - linux64-llvm-dsymutil - linux64-rust-macos - linux64-sccache + +macosx64-ccov/debug: + description: "MacOS X x64 Cross-compile Code Coverage" + index: + product: firefox + job-name: macosx64-ccov-debug + treeherder: + platform: osx-cross-ccov/debug + symbol: B + tier: 1 + worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64 + worker: + max-run-time: 5400 + env: + TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest" + run: + using: mozharness + actions: [get-secrets build update] + config: + - builds/releng_base_firefox.py + - builds/releng_base_mac_64_cross_builds.py + script: "mozharness/scripts/fx_desktop_build.py" + secrets: true + custom-build-variant-cfg: code-coverage-debug + tooltool-downloads: internal + run-on-projects: ['try'] + toolchains: + - linux64-cctools-port + - linux64-clang-6-pre-macosx-cross + - linux64-hfsplus + - linux64-libdmg + - linux64-llvm-dsymutil + - linux64-rust-macos + - linux64-sccache diff --git a/taskcluster/ci/test/test-platforms.yml b/taskcluster/ci/test/test-platforms.yml index b0c9f4db123b..b537e9d40ce0 100644 --- a/taskcluster/ci/test/test-platforms.yml +++ b/taskcluster/ci/test/test-platforms.yml @@ -261,6 +261,11 @@ macosx64-qr/debug: test-sets: - macosx64-qr-tests +macosx64-ccov/debug: + build-platform: macosx64-ccov/debug + test-sets: + - macosx64-tests + ## # Android platforms (matching /android.*/) diff --git a/taskcluster/taskgraph/transforms/job/mozharness_test.py b/taskcluster/taskgraph/transforms/job/mozharness_test.py index a419d7ad23f5..eaba673dd598 100644 --- a/taskcluster/taskgraph/transforms/job/mozharness_test.py +++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py @@ -33,6 +33,7 @@ BUILDER_NAME_PREFIX = { 'linux64-devedition-nightly': 'Ubuntu VM 12.04 x64', 'macosx64': 'Rev7 MacOSX Yosemite 10.10.5', 'macosx64-devedition': 'Rev7 MacOSX Yosemite 10.10.5 DevEdition', + 'macosx64-ccov': 'Rev7 MacOSX Yosemite 10.10.5 Code Coverage', 'android-4.3-arm7-api-16': 'Android 4.3 armv7 api-16+', 'android-4.2-x86': 'Android 4.2 x86 Emulator', 'android-4.3-arm7-api-16-gradle': 'Android 4.3 armv7 api-16+', diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py index b6e297fefe66..43b08f4fc6fc 100644 --- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -702,8 +702,7 @@ def handle_suite_category(config, tests): @transforms.add def enable_code_coverage(config, tests): - """Enable code coverage for the linux64-ccov/.* & linux64-jsdcov/.* & win64-ccov/.* - build-platforms""" + """Enable code coverage for the ccov and jsdcov build-platforms""" for test in tests: if 'ccov' in test['build-platform'] and not test['test-name'].startswith('test-verify'): test['mozharness'].setdefault('extra-options', []).append('--code-coverage') diff --git a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_code_coverage_debug.py b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_code_coverage_debug.py new file mode 100644 index 000000000000..d72440318e24 --- /dev/null +++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_code_coverage_debug.py @@ -0,0 +1,29 @@ +import os + +config = { + 'default_actions': [ + 'clobber', + 'build', + 'check-test', + 'update', # decided by query_is_nightly() + ], + 'stage_platform': 'macosx64-ccov-debug', + 'debug_build': True, + #### 64 bit build specific ##### + 'env': { + 'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'), + 'HG_SHARE_BASE_DIR': '/builds/hg-shared', + 'MOZ_OBJDIR': '%(abs_obj_dir)s', + 'TINDERBOX_OUTPUT': '1', + 'TOOLTOOL_CACHE': '/builds/tooltool_cache', + 'TOOLTOOL_HOME': '/builds', + 'MOZ_CRASHREPORTER_NO_REPORT': '1', + 'LC_ALL': 'C', + ## 64 bit specific + 'PATH': '/tools/python/bin:/opt/local/bin:/usr/bin:' + '/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin', + ## + }, + 'mozconfig_variant': 'code-coverage-debug', + ####################### +} diff --git a/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini b/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini index e89fe2a3191b..11eee36d213c 100644 --- a/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini +++ b/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini @@ -1,3 +1,2 @@ [label-attributes.html] prefs: [dom.webcomponents.shadowdom.enabled:true] - max-asserts: 8 diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html index 2910f2c01fa4..42c7b06dc524 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html +++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html @@ -227,7 +227,8 @@ }, "A labelable element is moved to iframe."); test(function () { - var labels1 = document.getElementById("test14").labels; + var test14 = document.getElementById("test14"); + var labels1 = test14.labels; var labels2 = document.getElementById("test15").labels; assert_true(labels1 instanceof NodeList, "A form control's 'labels' property should be an instance of a NodeList."); @@ -237,12 +238,17 @@ "The number of labels associated with a form control should be the number of label elements for which it is a labeled control."); assert_array_equals(labels1, [document.getElementById("lbl14")], "The labels for a form control should be returned in tree order."); + assert_array_equals(labels2, [document.getElementById("lbl15")], + "The labels for a form control should be returned in tree order."); document.getElementById('div6').removeChild(document.getElementById('div7')); - assert_equals(labels1.length, 0, - "The number of labels should be 0 after the labelable element is removed."); + assert_equals(labels1.length, 1, + "The number of labels should be 1 after the labelable element is removed but label element is still in the same tree."); assert_equals(labels2.length, 0, "The number of labels should be 0 since there is no label with a 'for' attribute associated with this labelable element."); + test14.remove(); + assert_equals(labels1.length, 0, + "The number of labels should be 0 after the labelable element is removed."); }, "A div element which contains labelable element is removed."); test(function () { diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp index 1e555522f25d..c62660237695 100644 --- a/xpcom/io/nsMultiplexInputStream.cpp +++ b/xpcom/io/nsMultiplexInputStream.cpp @@ -17,6 +17,7 @@ #include "base/basictypes.h" #include "nsMultiplexInputStream.h" +#include "nsIBufferedStreams.h" #include "nsICloneableInputStream.h" #include "nsIMultiplexInputStream.h" #include "nsISeekableStream.h" @@ -27,6 +28,8 @@ #include "mozilla/ipc/InputStreamUtils.h" #include "nsIAsyncInputStream.h" #include "nsIInputStreamLength.h" +#include "nsNetUtil.h" +#include "nsStreamUtils.h" using namespace mozilla; using namespace mozilla::ipc; @@ -69,11 +72,12 @@ public: struct StreamData { - void Initialize(nsIInputStream* aStream) + void Initialize(nsIInputStream* aStream, bool aBuffered) { mStream = aStream; mAsyncStream = do_QueryInterface(aStream); mSeekableStream = do_QueryInterface(aStream); + mBuffered = aBuffered; } nsCOMPtr mStream; @@ -82,6 +86,9 @@ public: nsCOMPtr mAsyncStream; // This can be null. nsCOMPtr mSeekableStream; + + // True if the stream is wrapped with nsIBufferedInputStream. + bool mBuffered; }; Mutex& GetLock() @@ -240,6 +247,18 @@ nsMultiplexInputStream::GetCount(uint32_t* aCount) NS_IMETHODIMP nsMultiplexInputStream::AppendStream(nsIInputStream* aStream) { + nsCOMPtr stream = aStream; + + bool buffered = false; + if (!NS_InputStreamIsBuffered(stream)) { + nsCOMPtr bufferedStream; + nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), + stream.forget(), 4096); + NS_ENSURE_SUCCESS(rv, rv); + stream = bufferedStream.forget(); + buffered = true; + } + MutexAutoLock lock(mLock); StreamData* streamData = mStreams.AppendElement(); @@ -247,7 +266,7 @@ nsMultiplexInputStream::AppendStream(nsIInputStream* aStream) return NS_ERROR_OUT_OF_MEMORY; } - streamData->Initialize(aStream); + streamData->Initialize(stream, buffered); UpdateQIMap(*streamData, 1); @@ -271,6 +290,17 @@ nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream** aResult) StreamData& streamData = mStreams.ElementAt(aIndex); nsCOMPtr stream = streamData.mStream; + + if (streamData.mBuffered) { + nsCOMPtr bufferedStream = do_QueryInterface(stream); + MOZ_ASSERT(bufferedStream); + + nsresult rv = bufferedStream->GetData(getter_AddRefs(stream)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + stream.forget(aResult); return NS_OK; } diff --git a/xpcom/tests/gtest/TestMultiplexInputStream.cpp b/xpcom/tests/gtest/TestMultiplexInputStream.cpp index 8593d9d4ceb9..cf3a8181a3e5 100644 --- a/xpcom/tests/gtest/TestMultiplexInputStream.cpp +++ b/xpcom/tests/gtest/TestMultiplexInputStream.cpp @@ -10,6 +10,7 @@ #include "nsIInputStream.h" #include "nsIMultiplexInputStream.h" #include "nsISeekableStream.h" +#include "nsStreamUtils.h" #include "nsThreadUtils.h" #include "Helpers.h" @@ -230,8 +231,7 @@ public: ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, uint32_t *aResult) override { - MOZ_CRASH("This should not be called!"); - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD @@ -281,8 +281,7 @@ public: ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, uint32_t *aResult) override { - MOZ_CRASH("This should not be called!"); - return NS_OK; + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD @@ -387,6 +386,90 @@ TEST(TestMultiplexInputStream, Available) { ASSERT_EQ(buffer.Length(), length); } +class NonBufferableStringStream final : public nsIInputStream +{ + nsCOMPtr mStream; + +public: + NS_DECL_THREADSAFE_ISUPPORTS + + explicit NonBufferableStringStream(const nsACString& aBuffer) + { + NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer); + } + + NS_IMETHOD + Available(uint64_t* aLength) override + { + return mStream->Available(aLength); + } + + NS_IMETHOD + Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override + { + return mStream->Read(aBuffer, aCount, aReadCount); + } + + NS_IMETHOD + ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, + uint32_t aCount, uint32_t *aResult) override + { + return NS_ERROR_NOT_IMPLEMENTED; + } + + NS_IMETHOD + Close() override + { + return mStream->Close(); + } + + NS_IMETHOD + IsNonBlocking(bool* aNonBlocking) override + { + return mStream->IsNonBlocking(aNonBlocking); + } + +private: + ~NonBufferableStringStream() = default; +}; + +NS_IMPL_ISUPPORTS(NonBufferableStringStream, nsIInputStream) + +TEST(TestMultiplexInputStream, Bufferable) { + nsCOMPtr multiplexStream = + do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); + + nsCOMPtr s = do_QueryInterface(multiplexStream); + ASSERT_TRUE(!!s); + + nsCString buf1; + buf1.AssignLiteral("Hello "); + nsCOMPtr inputStream1; + nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream1), buf1); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + nsCString buf2; + buf2.AssignLiteral("world"); + nsCOMPtr inputStream2 = new NonBufferableStringStream(buf2); + + rv = multiplexStream->AppendStream(inputStream1); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + rv = multiplexStream->AppendStream(inputStream2); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + nsCOMPtr stream(do_QueryInterface(multiplexStream)); + ASSERT_TRUE(!!stream); + + char buf3[1024]; + uint32_t size = 0; + rv = stream->ReadSegments(NS_CopySegmentToBuffer, buf3, sizeof(buf3), &size); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + ASSERT_EQ(size, buf1.Length() + buf2.Length()); + ASSERT_TRUE(!strncmp(buf3, "Hello world", size)); +} + TEST(TestMultiplexInputStream, QILengthInputStream) { nsCString buf; buf.AssignLiteral("Hello world");